Coverage for pyTooling / CallByRef / __init__.py: 90%
240 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-21 22:22 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-21 22:22 +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 typing import Any, Generic, TypeVar, Optional as Nullable
39try:
40 from pyTooling.Decorators import export
41 from pyTooling.MetaClasses import ExtendedType
42except (ImportError, ModuleNotFoundError): # pragma: no cover
43 print("[pyTooling.CallByRef] Could not import from 'pyTooling.*'!")
45 try:
46 from Decorators import export
47 from MetaClasses import ExtendedType
48 except (ImportError, ModuleNotFoundError) as ex: # pragma: no cover
49 print("[pyTooling.CallByRef] Could not import directly!")
50 raise ex
53T = TypeVar("T")
56@export
57class CallByRefParam(Generic[T], metaclass=ExtendedType, slots=True):
58 """
59 Implements a *call-by-reference* parameter.
61 .. seealso::
63 * :class:`CallByRefBoolParam` |br|
64 |rarr| A special *call-by-reference* implementation for boolean reference types.
65 * :class:`CallByRefIntParam` |br|
66 |rarr| A special *call-by-reference* implementation for integer reference types.
67 """
69 Value: T #: internal value
71 def __init__(self, value: Nullable[T] = None) -> None:
72 """Constructs a *call-by-reference* object for any type.
74 :param value: The value to be set as an initial value.
75 """
76 self.Value = value
78 def __ilshift__(self, other: T) -> 'CallByRefParam[T]': # Starting with Python 3.11+, use typing.Self as return type
79 """Assigns a value to the *call-by-reference* object.
81 :param other: The value to be assigned to this *call-by-reference* object.
82 :returns: Itself.
83 """
84 self.Value = other
85 return self
87 # binary operators - comparison
88 def __eq__(self, other: Any) -> bool:
89 """
90 Compare a CallByRefParam wrapped value with another instances (CallbyRefParam) or non-wrapped value for equality.
92 :param other: Parameter to compare against.
93 :returns: ``True``, if both values are equal.
94 """
95 if isinstance(other, CallByRefParam): 95 ↛ 96line 95 didn't jump to line 96 because the condition on line 95 was never true
96 return self.Value == other.Value
97 else:
98 return self.Value == other
100 def __ne__(self, other) -> bool:
101 """
102 Compare a CallByRefParam wrapped value with another instances (CallbyRefParam) or non-wrapped value for inequality.
104 :param other: Parameter to compare against.
105 :returns: ``True``, if both values are unequal.
106 """
107 if isinstance(other, CallByRefParam): 107 ↛ 108line 107 didn't jump to line 108 because the condition on line 107 was never true
108 return self.Value != other.Value
109 else:
110 return self.Value != other
112 # Type conversion operators
113 def __repr__(self) -> str:
114 """
115 Returns the wrapped object's string representation.
117 :returns: The string representation of the wrapped value.
118 """
119 return repr(self.Value)
121 def __str__(self) -> str:
122 """
123 Returns the wrapped object's string equivalent.
125 :returns: The string equivalent of the wrapped value.
126 """
127 return str(self.Value)
130@export
131class CallByRefBoolParam(CallByRefParam):
132 """A special *call-by-reference* implementation for boolean reference types."""
134 # Binary operators - comparison
135 def __eq__(self, other: Any) -> bool:
136 """
137 Compare a CallByRefBoolParam wrapped boolean value with another instances (CallByRefBoolParam) or non-wrapped boolean value for equality.
139 :param other: Parameter to compare against.
140 :returns: ``True``, if both values are equal.
141 :raises TypeError: If parameter ``other`` is not of type :class:`bool` or :class:`CallByRefBoolParam`.
142 """
143 if isinstance(other, bool):
144 return self.Value == other
145 elif isinstance(other, CallByRefBoolParam): 145 ↛ 146line 145 didn't jump to line 146 because the condition on line 145 was never true
146 return self.Value == other.Value
147 else:
148 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.")
149 ex.add_note("Supported types for second operand: bool, CallByRefBoolParam")
150 raise ex
152 def __ne__(self, other) -> bool:
153 """
154 Compare a CallByRefBoolParam wrapped boolean value with another instances (CallByRefBoolParam) or non-wrapped boolean value for inequality.
156 :param other: Parameter to compare against.
157 :returns: ``True``, if both values are unequal.
158 :raises TypeError: If parameter ``other`` is not of type :class:`bool` or :class:`CallByRefBoolParam`.
159 """
160 if isinstance(other, bool):
161 return self.Value != other
162 elif isinstance(other, CallByRefBoolParam): 162 ↛ 163line 162 didn't jump to line 163 because the condition on line 162 was never true
163 return self.Value != other.Value
164 else:
165 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by != operator.")
166 ex.add_note(f"Supported types for second operand: bool, CallByRefBoolParam")
167 raise ex
169 # Type conversion operators
170 def __bool__(self) -> bool:
171 """
172 Type conversion to :class:`bool`.
174 :returns: The wrapped value.
175 """
176 return self.Value
178 def __int__(self) -> int:
179 """
180 Type conversion to :class:`int`.
182 :returns: The integer representation of the wrapped boolean value.
183 """
184 return int(self.Value)
187@export
188class CallByRefIntParam(CallByRefParam):
189 """A special *call-by-reference* implementation for integer reference types."""
191 # Unary operators
192 def __neg__(self) -> int:
193 """Negate: -self."""
194 return -self.Value
196 def __pos__(self) -> int:
197 """Positive: +self."""
198 return +self.Value
200 def __invert__(self) -> int:
201 """Invert: ~self."""
202 return ~self.Value
204 # Binary operators - logical
205 def __and__(self, other: Any) -> int:
206 """And: self & other."""
207 if isinstance(other, int):
208 return self.Value & other
209 else:
210 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by and operator.")
211 ex.add_note(f"Supported types for second operand: int")
212 raise ex
214 def __or__(self, other: Any) -> int:
215 """Or: self | other."""
216 if isinstance(other, int):
217 return self.Value | other
218 else:
219 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by or operator.")
220 ex.add_note(f"Supported types for second operand: int")
221 raise ex
223 def __xor__(self, other: Any) -> int:
224 """Xor: self ^ other."""
225 if isinstance(other, int):
226 return self.Value ^ other
227 else:
228 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
229 ex.add_note(f"Supported types for second operand: int")
230 raise ex
232 # Binary inplace operators
233 def __iand__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type
234 """Inplace and: self &= other."""
235 if isinstance(other, int):
236 self.Value &= other
237 return self
238 else:
239 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by &= operator.")
240 ex.add_note(f"Supported types for second operand: int")
241 raise ex
243 def __ior__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type
244 r"""Inplace or: self \|= other."""
245 if isinstance(other, int):
246 self.Value |= other
247 return self
248 else:
249 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by |= operator.")
250 ex.add_note(f"Supported types for second operand: int")
251 raise ex
253 def __ixor__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type
254 r"""Inplace or: self \|= other."""
255 if isinstance(other, int):
256 self.Value ^= other
257 return self
258 else:
259 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by ^= operator.")
260 ex.add_note(f"Supported types for second operand: int")
261 raise ex
263 # Binary operators - arithmetic
264 def __add__(self, other: Any) -> int:
265 """Addition: self + other."""
266 if isinstance(other, int):
267 return self.Value + other
268 else:
269 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by + operator.")
270 ex.add_note(f"Supported types for second operand: int")
271 raise ex
273 def __sub__(self, other: Any) -> int:
274 """Subtraction: self - other."""
275 if isinstance(other, int):
276 return self.Value - other
277 else:
278 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by - operator.")
279 ex.add_note(f"Supported types for second operand: int")
280 raise ex
282 def __truediv__(self, other: Any) -> int:
283 """Division: self / other."""
284 if isinstance(other, int):
285 return self.Value / other
286 else:
287 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by / operator.")
288 ex.add_note(f"Supported types for second operand: int")
289 raise ex
291 def __floordiv__(self, other: Any) -> int:
292 """Floor division: self // other."""
293 if isinstance(other, int):
294 return self.Value // other
295 else:
296 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by // operator.")
297 ex.add_note(f"Supported types for second operand: int")
298 raise ex
300 def __mul__(self, other: Any) -> int:
301 """Multiplication: self * other."""
302 if isinstance(other, int):
303 return self.Value * other
304 else:
305 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by * operator.")
306 ex.add_note(f"Supported types for second operand: int")
307 raise ex
309 def __mod__(self, other: Any) -> int:
310 """Modulo: self % other."""
311 if isinstance(other, int):
312 return self.Value % other
313 else:
314 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by % operator.")
315 ex.add_note(f"Supported types for second operand: int")
316 raise ex
318 def __pow__(self, other: Any) -> int:
319 """Power: self ** other."""
320 if isinstance(other, int):
321 return self.Value ** other
322 else:
323 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by ** operator.")
324 ex.add_note(f"Supported types for second operand: int")
325 raise ex
327 # Binary inplace operators - arithmetic
328 def __iadd__(self, other: Any) -> 'CallByRefIntParam':
329 """Addition: self += other."""
330 if isinstance(other, int):
331 self.Value += other
332 return self
333 else:
334 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
335 ex.add_note(f"Supported types for second operand: int")
336 raise ex
338 def __isub__(self, other: Any) -> 'CallByRefIntParam':
339 """Subtraction: self -= other."""
340 if isinstance(other, int):
341 self.Value -= other
342 return self
343 else:
344 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
345 ex.add_note(f"Supported types for second operand: int")
346 raise ex
348 def __idiv__(self, other: Any) -> 'CallByRefIntParam':
349 """Division: self /= other."""
350 if isinstance(other, int):
351 self.Value /= other
352 return self
353 else:
354 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
355 ex.add_note(f"Supported types for second operand: int")
356 raise ex
358 def __ifloordiv__(self, other: Any) -> 'CallByRefIntParam':
359 """Floor division: self // other."""
360 if isinstance(other, int):
361 self.Value //= other
362 return self
363 else:
364 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
365 ex.add_note(f"Supported types for second operand: int")
366 raise ex
368 def __imul__(self, other: Any) -> 'CallByRefIntParam':
369 r"""Multiplication: self \*= other."""
370 if isinstance(other, int):
371 self.Value *= other
372 return self
373 else:
374 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
375 ex.add_note(f"Supported types for second operand: int")
376 raise ex
378 def __imod__(self, other: Any) -> 'CallByRefIntParam':
379 """Modulo: self %= other."""
380 if isinstance(other, int):
381 self.Value %= other
382 return self
383 else:
384 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
385 ex.add_note(f"Supported types for second operand: int")
386 raise ex
388 def __ipow__(self, other: Any) -> 'CallByRefIntParam':
389 r"""Power: self \*\*= other."""
390 if isinstance(other, int):
391 self.Value **= other
392 return self
393 else:
394 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
395 ex.add_note(f"Supported types for second operand: int")
396 raise ex
398 # Binary operators - comparison
399 def __eq__(self, other: Any) -> bool:
400 """
401 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for equality.
403 :param other: Parameter to compare against.
404 :returns: ``True``, if both values are equal.
405 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
406 """
407 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
408 return self.Value == other
409 elif isinstance(other, CallByRefIntParam): 409 ↛ 410line 409 didn't jump to line 410 because the condition on line 409 was never true
410 return self.Value == other.Value
411 else:
412 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.")
413 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
414 raise ex
416 def __ne__(self, other) -> bool:
417 """
418 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for inequality.
420 :param other: Parameter to compare against.
421 :returns: ``True``, if both values are unequal.
422 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
423 """
424 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
425 return self.Value != other
426 elif isinstance(other, CallByRefIntParam): 426 ↛ 427line 426 didn't jump to line 427 because the condition on line 426 was never true
427 return self.Value != other.Value
428 else:
429 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by != operator.")
430 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
431 raise ex
433 def __lt__(self, other: Any) -> bool:
434 """
435 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for less-than.
437 :param other: Parameter to compare against.
438 :returns: ``True``, if the wrapped value is less than the other value.
439 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
440 """
441 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
442 return self.Value < other
443 elif isinstance(other, CallByRefIntParam): 443 ↛ 444line 443 didn't jump to line 444 because the condition on line 443 was never true
444 return self.Value < other.Value
445 else:
446 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by < operator.")
447 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
448 raise ex
450 def __le__(self, other: Any) -> bool:
451 """
452 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for less-than-or-equal.
454 :param other: Parameter to compare against.
455 :returns: ``True``, if the wrapped value is less than or equal the other value.
456 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
457 """
458 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
459 return self.Value <= other
460 elif isinstance(other, CallByRefIntParam): 460 ↛ 461line 460 didn't jump to line 461 because the condition on line 460 was never true
461 return self.Value <= other.Value
462 else:
463 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by <= operator.")
464 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
465 raise ex
467 def __gt__(self, other: Any) -> bool:
468 """
469 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for geater-than.
471 :param other: Parameter to compare against.
472 :returns: ``True``, if the wrapped value is greater than the other value.
473 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
474 """
475 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
476 return self.Value > other
477 elif isinstance(other, CallByRefIntParam): 477 ↛ 478line 477 didn't jump to line 478 because the condition on line 477 was never true
478 return self.Value > other.Value
479 else:
480 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by > operator.")
481 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
482 raise ex
484 def __ge__(self, other: Any) -> bool:
485 """
486 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for greater-than-or-equal.
488 :param other: Parameter to compare against.
489 :returns: ``True``, if the wrapped value is greater than or equal the other value.
490 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
491 """
492 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
493 return self.Value >= other
494 elif isinstance(other, CallByRefIntParam): 494 ↛ 495line 494 didn't jump to line 495 because the condition on line 494 was never true
495 return self.Value >= other.Value
496 else:
497 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by >= operator.")
498 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
499 raise ex
501 # Type conversion operators
502 def __bool__(self) -> bool:
503 """
504 Type conversion to :class:`bool`.
506 :returns: The boolean representation of the wrapped integer value.
507 """
508 return bool(self.Value)
510 def __int__(self) -> int:
511 """
512 Type conversion to :class:`int`.
514 :returns: The wrapped value."""
515 return self.Value
517 def __float__(self):
518 """
519 Type conversion to :class:`float`.
521 :returns: The float representation of the wrapped integer value.
522 """
523 return float(self.Value)