Coverage for pyTooling/CallByRef/__init__.py: 90%
241 statements
« prev ^ index » next coverage.py v7.11.1, created at 2025-11-07 22:21 +0000
« prev ^ index » next coverage.py v7.11.1, created at 2025-11-07 22:21 +0000
1# ==================================================================================================================== #
2# _____ _ _ ____ _ _ ____ ____ __ #
3# _ __ _ |_ _|__ ___ | (_)_ __ __ _ / ___|__ _| | | __ ) _ _| _ \ ___ / _| #
4# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` || | / _` | | | _ \| | | | |_) / _ \ |_ #
5# | |_) | |_| || | (_) | (_) | | | | | | (_| || |__| (_| | | | |_) | |_| | _ < __/ _| #
6# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)____\__,_|_|_|____/ \__, |_| \_\___|_| #
7# |_| |___/ |___/ |___/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2017-2025 Patrick Lehmann - Bötzingen, Germany #
15# #
16# Licensed under the Apache License, Version 2.0 (the "License"); #
17# you may not use this file except in compliance with the License. #
18# You may obtain a copy of the License at #
19# #
20# http://www.apache.org/licenses/LICENSE-2.0 #
21# #
22# Unless required by applicable law or agreed to in writing, software #
23# distributed under the License is distributed on an "AS IS" BASIS, #
24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
25# See the License for the specific language governing permissions and #
26# limitations under the License. #
27# #
28# SPDX-License-Identifier: Apache-2.0 #
29# ==================================================================================================================== #
30#
31"""
32Auxiliary classes to implement call-by-reference.
34.. hint:: See :ref:`high-level help <COMMON/CallByRef>` for explanations and usage examples.
35"""
36from decimal import Decimal
37from sys import version_info # needed for versions before Python 3.11
38from typing import Any, Generic, TypeVar, Optional as Nullable
40try:
41 from pyTooling.Decorators import export
42 from pyTooling.MetaClasses import ExtendedType
43except (ImportError, ModuleNotFoundError): # pragma: no cover
44 print("[pyTooling.CallByRef] Could not import from 'pyTooling.*'!")
46 try:
47 from Decorators import export
48 from MetaClasses import ExtendedType
49 except (ImportError, ModuleNotFoundError) as ex: # pragma: no cover
50 print("[pyTooling.CallByRef] Could not import directly!")
51 raise ex
54T = TypeVar("T")
57@export
58class CallByRefParam(Generic[T], metaclass=ExtendedType, slots=True):
59 """
60 Implements a *call-by-reference* parameter.
62 .. seealso::
64 * :class:`CallByRefBoolParam` |br|
65 |rarr| A special *call-by-reference* implementation for boolean reference types.
66 * :class:`CallByRefIntParam` |br|
67 |rarr| A special *call-by-reference* implementation for integer reference types.
68 """
70 Value: T #: internal value
72 def __init__(self, value: Nullable[T] = None) -> None:
73 """Constructs a *call-by-reference* object for any type.
75 :param value: The value to be set as an initial value.
76 """
77 self.Value = value
79 def __ilshift__(self, other: T) -> 'CallByRefParam[T]': # Starting with Python 3.11+, use typing.Self as return type
80 """Assigns a value to the *call-by-reference* object.
82 :param other: The value to be assigned to this *call-by-reference* object.
83 :returns: Itself.
84 """
85 self.Value = other
86 return self
88 # binary operators - comparison
89 def __eq__(self, other: Any) -> bool:
90 """
91 Compare a CallByRefParam wrapped value with another instances (CallbyRefParam) or non-wrapped value for equality.
93 :param other: Parameter to compare against.
94 :returns: ``True``, if both values are equal.
95 """
96 if isinstance(other, CallByRefParam): 96 ↛ 97line 96 didn't jump to line 97 because the condition on line 96 was never true
97 return self.Value == other.Value
98 else:
99 return self.Value == other
101 def __ne__(self, other) -> bool:
102 """
103 Compare a CallByRefParam wrapped value with another instances (CallbyRefParam) or non-wrapped value for inequality.
105 :param other: Parameter to compare against.
106 :returns: ``True``, if both values are unequal.
107 """
108 if isinstance(other, CallByRefParam): 108 ↛ 109line 108 didn't jump to line 109 because the condition on line 108 was never true
109 return self.Value != other.Value
110 else:
111 return self.Value != other
113 # Type conversion operators
114 def __repr__(self) -> str:
115 """
116 Returns the wrapped object's string representation.
118 :returns: The string representation of the wrapped value.
119 """
120 return repr(self.Value)
122 def __str__(self) -> str:
123 """
124 Returns the wrapped object's string equivalent.
126 :returns: The string equivalent of the wrapped value.
127 """
128 return str(self.Value)
131@export
132class CallByRefBoolParam(CallByRefParam):
133 """A special *call-by-reference* implementation for boolean reference types."""
135 # Binary operators - comparison
136 def __eq__(self, other: Any) -> bool:
137 """
138 Compare a CallByRefBoolParam wrapped boolean value with another instances (CallByRefBoolParam) or non-wrapped boolean value for equality.
140 :param other: Parameter to compare against.
141 :returns: ``True``, if both values are equal.
142 :raises TypeError: If parameter ``other`` is not of type :class:`bool` or :class:`CallByRefBoolParam`.
143 """
144 if isinstance(other, bool):
145 return self.Value == other
146 elif isinstance(other, CallByRefBoolParam): 146 ↛ 147line 146 didn't jump to line 147 because the condition on line 146 was never true
147 return self.Value == other.Value
148 else:
149 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.")
150 ex.add_note("Supported types for second operand: bool, CallByRefBoolParam")
151 raise ex
153 def __ne__(self, other) -> bool:
154 """
155 Compare a CallByRefBoolParam wrapped boolean value with another instances (CallByRefBoolParam) or non-wrapped boolean value for inequality.
157 :param other: Parameter to compare against.
158 :returns: ``True``, if both values are unequal.
159 :raises TypeError: If parameter ``other`` is not of type :class:`bool` or :class:`CallByRefBoolParam`.
160 """
161 if isinstance(other, bool):
162 return self.Value != other
163 elif isinstance(other, CallByRefBoolParam): 163 ↛ 164line 163 didn't jump to line 164 because the condition on line 163 was never true
164 return self.Value != other.Value
165 else:
166 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by != operator.")
167 ex.add_note(f"Supported types for second operand: bool, CallByRefBoolParam")
168 raise ex
170 # Type conversion operators
171 def __bool__(self) -> bool:
172 """
173 Type conversion to :class:`bool`.
175 :returns: The wrapped value.
176 """
177 return self.Value
179 def __int__(self) -> int:
180 """
181 Type conversion to :class:`int`.
183 :returns: The integer representation of the wrapped boolean value.
184 """
185 return int(self.Value)
188@export
189class CallByRefIntParam(CallByRefParam):
190 """A special *call-by-reference* implementation for integer reference types."""
192 # Unary operators
193 def __neg__(self) -> int:
194 """Negate: -self."""
195 return -self.Value
197 def __pos__(self) -> int:
198 """Positive: +self."""
199 return +self.Value
201 def __invert__(self) -> int:
202 """Invert: ~self."""
203 return ~self.Value
205 # Binary operators - logical
206 def __and__(self, other: Any) -> int:
207 """And: self & other."""
208 if isinstance(other, int):
209 return self.Value & other
210 else:
211 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by and operator.")
212 ex.add_note(f"Supported types for second operand: int")
213 raise ex
215 def __or__(self, other: Any) -> int:
216 """Or: self | other."""
217 if isinstance(other, int):
218 return self.Value | other
219 else:
220 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by or operator.")
221 ex.add_note(f"Supported types for second operand: int")
222 raise ex
224 def __xor__(self, other: Any) -> int:
225 """Xor: self ^ other."""
226 if isinstance(other, int):
227 return self.Value ^ other
228 else:
229 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
230 ex.add_note(f"Supported types for second operand: int")
231 raise ex
233 # Binary inplace operators
234 def __iand__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type
235 """Inplace and: self &= other."""
236 if isinstance(other, int):
237 self.Value &= other
238 return self
239 else:
240 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by &= operator.")
241 ex.add_note(f"Supported types for second operand: int")
242 raise ex
244 def __ior__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type
245 r"""Inplace or: self \|= other."""
246 if isinstance(other, int):
247 self.Value |= other
248 return self
249 else:
250 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by |= operator.")
251 ex.add_note(f"Supported types for second operand: int")
252 raise ex
254 def __ixor__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type
255 r"""Inplace or: self \|= other."""
256 if isinstance(other, int):
257 self.Value ^= other
258 return self
259 else:
260 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by ^= operator.")
261 ex.add_note(f"Supported types for second operand: int")
262 raise ex
264 # Binary operators - arithmetic
265 def __add__(self, other: Any) -> int:
266 """Addition: self + other."""
267 if isinstance(other, int):
268 return self.Value + other
269 else:
270 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by + operator.")
271 ex.add_note(f"Supported types for second operand: int")
272 raise ex
274 def __sub__(self, other: Any) -> int:
275 """Subtraction: self - other."""
276 if isinstance(other, int):
277 return self.Value - other
278 else:
279 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by - operator.")
280 ex.add_note(f"Supported types for second operand: int")
281 raise ex
283 def __truediv__(self, other: Any) -> int:
284 """Division: self / other."""
285 if isinstance(other, int):
286 return self.Value / other
287 else:
288 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by / operator.")
289 ex.add_note(f"Supported types for second operand: int")
290 raise ex
292 def __floordiv__(self, other: Any) -> int:
293 """Floor division: self // other."""
294 if isinstance(other, int):
295 return self.Value // other
296 else:
297 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by // operator.")
298 ex.add_note(f"Supported types for second operand: int")
299 raise ex
301 def __mul__(self, other: Any) -> int:
302 """Multiplication: self * other."""
303 if isinstance(other, int):
304 return self.Value * other
305 else:
306 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by * operator.")
307 ex.add_note(f"Supported types for second operand: int")
308 raise ex
310 def __mod__(self, other: Any) -> int:
311 """Modulo: self % other."""
312 if isinstance(other, int):
313 return self.Value % other
314 else:
315 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by % operator.")
316 ex.add_note(f"Supported types for second operand: int")
317 raise ex
319 def __pow__(self, other: Any) -> int:
320 """Power: self ** other."""
321 if isinstance(other, int):
322 return self.Value ** other
323 else:
324 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by ** operator.")
325 ex.add_note(f"Supported types for second operand: int")
326 raise ex
328 # Binary inplace operators - arithmetic
329 def __iadd__(self, other: Any) -> 'CallByRefIntParam':
330 """Addition: self += other."""
331 if isinstance(other, int):
332 self.Value += other
333 return self
334 else:
335 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
336 ex.add_note(f"Supported types for second operand: int")
337 raise ex
339 def __isub__(self, other: Any) -> 'CallByRefIntParam':
340 """Subtraction: self -= other."""
341 if isinstance(other, int):
342 self.Value -= other
343 return self
344 else:
345 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
346 ex.add_note(f"Supported types for second operand: int")
347 raise ex
349 def __idiv__(self, other: Any) -> 'CallByRefIntParam':
350 """Division: self /= other."""
351 if isinstance(other, int):
352 self.Value /= other
353 return self
354 else:
355 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
356 ex.add_note(f"Supported types for second operand: int")
357 raise ex
359 def __ifloordiv__(self, other: Any) -> 'CallByRefIntParam':
360 """Floor division: self // other."""
361 if isinstance(other, int):
362 self.Value //= other
363 return self
364 else:
365 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
366 ex.add_note(f"Supported types for second operand: int")
367 raise ex
369 def __imul__(self, other: Any) -> 'CallByRefIntParam':
370 r"""Multiplication: self \*= other."""
371 if isinstance(other, int):
372 self.Value *= other
373 return self
374 else:
375 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
376 ex.add_note(f"Supported types for second operand: int")
377 raise ex
379 def __imod__(self, other: Any) -> 'CallByRefIntParam':
380 """Modulo: self %= other."""
381 if isinstance(other, int):
382 self.Value %= other
383 return self
384 else:
385 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
386 ex.add_note(f"Supported types for second operand: int")
387 raise ex
389 def __ipow__(self, other: Any) -> 'CallByRefIntParam':
390 r"""Power: self \*\*= other."""
391 if isinstance(other, int):
392 self.Value **= other
393 return self
394 else:
395 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
396 ex.add_note(f"Supported types for second operand: int")
397 raise ex
399 # Binary operators - comparison
400 def __eq__(self, other: Any) -> bool:
401 """
402 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for equality.
404 :param other: Parameter to compare against.
405 :returns: ``True``, if both values are equal.
406 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
407 """
408 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
409 return self.Value == other
410 elif isinstance(other, CallByRefIntParam): 410 ↛ 411line 410 didn't jump to line 411 because the condition on line 410 was never true
411 return self.Value == other.Value
412 else:
413 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.")
414 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
415 raise ex
417 def __ne__(self, other) -> bool:
418 """
419 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for inequality.
421 :param other: Parameter to compare against.
422 :returns: ``True``, if both values are unequal.
423 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
424 """
425 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
426 return self.Value != other
427 elif isinstance(other, CallByRefIntParam): 427 ↛ 428line 427 didn't jump to line 428 because the condition on line 427 was never true
428 return self.Value != other.Value
429 else:
430 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by != operator.")
431 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
432 raise ex
434 def __lt__(self, other: Any) -> bool:
435 """
436 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for less-than.
438 :param other: Parameter to compare against.
439 :returns: ``True``, if the wrapped value is less than the other value.
440 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
441 """
442 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
443 return self.Value < other
444 elif isinstance(other, CallByRefIntParam): 444 ↛ 445line 444 didn't jump to line 445 because the condition on line 444 was never true
445 return self.Value < other.Value
446 else:
447 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by < operator.")
448 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
449 raise ex
451 def __le__(self, other: Any) -> bool:
452 """
453 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for less-than-or-equal.
455 :param other: Parameter to compare against.
456 :returns: ``True``, if the wrapped value is less than or equal the other value.
457 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
458 """
459 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
460 return self.Value <= other
461 elif isinstance(other, CallByRefIntParam): 461 ↛ 462line 461 didn't jump to line 462 because the condition on line 461 was never true
462 return self.Value <= other.Value
463 else:
464 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by <= operator.")
465 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
466 raise ex
468 def __gt__(self, other: Any) -> bool:
469 """
470 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for geater-than.
472 :param other: Parameter to compare against.
473 :returns: ``True``, if the wrapped value is greater than the other value.
474 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
475 """
476 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
477 return self.Value > other
478 elif isinstance(other, CallByRefIntParam): 478 ↛ 479line 478 didn't jump to line 479 because the condition on line 478 was never true
479 return self.Value > other.Value
480 else:
481 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by > operator.")
482 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
483 raise ex
485 def __ge__(self, other: Any) -> bool:
486 """
487 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for greater-than-or-equal.
489 :param other: Parameter to compare against.
490 :returns: ``True``, if the wrapped value is greater than or equal the other value.
491 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
492 """
493 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
494 return self.Value >= other
495 elif isinstance(other, CallByRefIntParam): 495 ↛ 496line 495 didn't jump to line 496 because the condition on line 495 was never true
496 return self.Value >= other.Value
497 else:
498 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by >= operator.")
499 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
500 raise ex
502 # Type conversion operators
503 def __bool__(self) -> bool:
504 """
505 Type conversion to :class:`bool`.
507 :returns: The boolean representation of the wrapped integer value.
508 """
509 return bool(self.Value)
511 def __int__(self) -> int:
512 """
513 Type conversion to :class:`int`.
515 :returns: The wrapped value."""
516 return self.Value
518 def __float__(self):
519 """
520 Type conversion to :class:`float`.
522 :returns: The float representation of the wrapped integer value.
523 """
524 return float(self.Value)