Coverage for pyTooling/CallByRef/__init__.py: 90%
213 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-25 22:22 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-25 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 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 if version_info >= (3, 11): # pragma: no cover
151 ex.add_note(f"Supported types for second operand: bool, CallByRefBoolParam")
152 raise ex
154 def __ne__(self, other) -> bool:
155 """
156 Compare a CallByRefBoolParam wrapped boolean value with another instances (CallByRefBoolParam) or non-wrapped boolean value for inequality.
158 :param other: Parameter to compare against.
159 :returns: ``True``, if both values are unequal.
160 :raises TypeError: If parameter ``other`` is not of type :class:`bool` or :class:`CallByRefBoolParam`.
161 """
162 if isinstance(other, bool):
163 return self.Value != other
164 elif isinstance(other, CallByRefBoolParam): 164 ↛ 165line 164 didn't jump to line 165 because the condition on line 164 was never true
165 return self.Value != other.Value
166 else:
167 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by != operator.")
168 if version_info >= (3, 11): # pragma: no cover
169 ex.add_note(f"Supported types for second operand: bool, CallByRefBoolParam")
170 raise ex
172 # Type conversion operators
173 def __bool__(self) -> bool:
174 """
175 Type conversion to :class:`bool`.
177 :returns: The wrapped value.
178 """
179 return self.Value
181 def __int__(self) -> int:
182 """
183 Type conversion to :class:`int`.
185 :returns: The integer representation of the wrapped boolean value.
186 """
187 return int(self.Value)
190@export
191class CallByRefIntParam(CallByRefParam):
192 """A special *call-by-reference* implementation for integer reference types."""
194 # Unary operators
195 def __neg__(self) -> int:
196 """Negate: -self."""
197 return -self.Value
199 def __pos__(self) -> int:
200 """Positive: +self."""
201 return +self.Value
203 def __invert__(self) -> int:
204 """Invert: ~self."""
205 return ~self.Value
207 # Binary operators - logical
208 def __and__(self, other: Any) -> int:
209 """And: self & other."""
210 if isinstance(other, int):
211 return self.Value & other
212 else:
213 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by and operator.")
214 if version_info >= (3, 11): # pragma: no cover
215 ex.add_note(f"Supported types for second operand: int")
216 raise ex
218 def __or__(self, other: Any) -> int:
219 """Or: self | other."""
220 if isinstance(other, int):
221 return self.Value | other
222 else:
223 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by or operator.")
224 if version_info >= (3, 11): # pragma: no cover
225 ex.add_note(f"Supported types for second operand: int")
226 raise ex
228 def __xor__(self, other: Any) -> int:
229 """Xor: self ^ other."""
230 if isinstance(other, int):
231 return self.Value ^ other
232 else:
233 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
234 if version_info >= (3, 11): # pragma: no cover
235 ex.add_note(f"Supported types for second operand: int")
236 raise ex
238 # Binary inplace operators
239 def __iand__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type
240 """Inplace and: self &= other."""
241 if isinstance(other, int):
242 self.Value &= other
243 return self
244 else:
245 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by &= operator.")
246 if version_info >= (3, 11): # pragma: no cover
247 ex.add_note(f"Supported types for second operand: int")
248 raise ex
250 def __ior__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type
251 r"""Inplace or: self \|= other."""
252 if isinstance(other, int):
253 self.Value |= other
254 return self
255 else:
256 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by |= operator.")
257 if version_info >= (3, 11): # pragma: no cover
258 ex.add_note(f"Supported types for second operand: int")
259 raise ex
261 def __ixor__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type
262 r"""Inplace or: self \|= other."""
263 if isinstance(other, int):
264 self.Value ^= other
265 return self
266 else:
267 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by ^= operator.")
268 if version_info >= (3, 11): # pragma: no cover
269 ex.add_note(f"Supported types for second operand: int")
270 raise ex
272 # Binary operators - arithmetic
273 def __add__(self, other: Any) -> int:
274 """Addition: 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 if version_info >= (3, 11): # pragma: no cover
280 ex.add_note(f"Supported types for second operand: int")
281 raise ex
283 def __sub__(self, other: Any) -> int:
284 """Subtraction: 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 if version_info >= (3, 11): # pragma: no cover
290 ex.add_note(f"Supported types for second operand: int")
291 raise ex
293 def __truediv__(self, other: Any) -> int:
294 """Division: self / other."""
295 if isinstance(other, int):
296 return self.Value / other
297 else:
298 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by / operator.")
299 if version_info >= (3, 11): # pragma: no cover
300 ex.add_note(f"Supported types for second operand: int")
301 raise ex
303 def __floordiv__(self, other: Any) -> int:
304 """Floor division: self // other."""
305 if isinstance(other, int):
306 return self.Value // other
307 else:
308 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by // operator.")
309 if version_info >= (3, 11): # pragma: no cover
310 ex.add_note(f"Supported types for second operand: int")
311 raise ex
313 def __mul__(self, other: Any) -> int:
314 """Multiplication: self * other."""
315 if isinstance(other, int):
316 return self.Value * other
317 else:
318 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by * operator.")
319 if version_info >= (3, 11): # pragma: no cover
320 ex.add_note(f"Supported types for second operand: int")
321 raise ex
323 def __mod__(self, other: Any) -> int:
324 """Modulo: self % other."""
325 if isinstance(other, int):
326 return self.Value % other
327 else:
328 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by % operator.")
329 if version_info >= (3, 11): # pragma: no cover
330 ex.add_note(f"Supported types for second operand: int")
331 raise ex
333 def __pow__(self, other: Any) -> int:
334 """Power: self ** other."""
335 if isinstance(other, int):
336 return self.Value ** other
337 else:
338 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by ** operator.")
339 if version_info >= (3, 11): # pragma: no cover
340 ex.add_note(f"Supported types for second operand: int")
341 raise ex
343 # Binary inplace operators - arithmetic
344 def __iadd__(self, other: Any) -> 'CallByRefIntParam':
345 """Addition: self += other."""
346 if isinstance(other, int):
347 self.Value += other
348 return self
349 else:
350 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
351 if version_info >= (3, 11): # pragma: no cover
352 ex.add_note(f"Supported types for second operand: int")
353 raise ex
355 def __isub__(self, other: Any) -> 'CallByRefIntParam':
356 """Subtraction: self -= other."""
357 if isinstance(other, int):
358 self.Value -= other
359 return self
360 else:
361 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
362 if version_info >= (3, 11): # pragma: no cover
363 ex.add_note(f"Supported types for second operand: int")
364 raise ex
366 def __idiv__(self, other: Any) -> 'CallByRefIntParam':
367 """Division: self /= other."""
368 if isinstance(other, int):
369 self.Value /= other
370 return self
371 else:
372 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
373 if version_info >= (3, 11): # pragma: no cover
374 ex.add_note(f"Supported types for second operand: int")
375 raise ex
377 def __ifloordiv__(self, other: Any) -> 'CallByRefIntParam':
378 """Floor division: self // other."""
379 if isinstance(other, int):
380 self.Value //= other
381 return self
382 else:
383 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
384 if version_info >= (3, 11): # pragma: no cover
385 ex.add_note(f"Supported types for second operand: int")
386 raise ex
388 def __imul__(self, other: Any) -> 'CallByRefIntParam':
389 r"""Multiplication: 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 if version_info >= (3, 11): # pragma: no cover
396 ex.add_note(f"Supported types for second operand: int")
397 raise ex
399 def __imod__(self, other: Any) -> 'CallByRefIntParam':
400 """Modulo: self %= other."""
401 if isinstance(other, int):
402 self.Value %= other
403 return self
404 else:
405 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
406 if version_info >= (3, 11): # pragma: no cover
407 ex.add_note(f"Supported types for second operand: int")
408 raise ex
410 def __ipow__(self, other: Any) -> 'CallByRefIntParam':
411 r"""Power: self \*\*= other."""
412 if isinstance(other, int):
413 self.Value **= other
414 return self
415 else:
416 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
417 if version_info >= (3, 11): # pragma: no cover
418 ex.add_note(f"Supported types for second operand: int")
419 raise ex
421 # Binary operators - comparison
422 def __eq__(self, other: Any) -> bool:
423 """
424 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for equality.
426 :param other: Parameter to compare against.
427 :returns: ``True``, if both values are equal.
428 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
429 """
430 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
431 return self.Value == other
432 elif isinstance(other, CallByRefIntParam): 432 ↛ 433line 432 didn't jump to line 433 because the condition on line 432 was never true
433 return self.Value == other.Value
434 else:
435 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.")
436 if version_info >= (3, 11): # pragma: no cover
437 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
438 raise ex
440 def __ne__(self, other) -> bool:
441 """
442 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for inequality.
444 :param other: Parameter to compare against.
445 :returns: ``True``, if both values are unequal.
446 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
447 """
448 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
449 return self.Value != other
450 elif isinstance(other, CallByRefIntParam): 450 ↛ 451line 450 didn't jump to line 451 because the condition on line 450 was never true
451 return self.Value != other.Value
452 else:
453 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by != operator.")
454 if version_info >= (3, 11): # pragma: no cover
455 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
456 raise ex
458 def __lt__(self, other: Any) -> bool:
459 """
460 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for less-than.
462 :param other: Parameter to compare against.
463 :returns: ``True``, if the wrapped value is less than the other value.
464 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
465 """
466 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
467 return self.Value < other
468 elif isinstance(other, CallByRefIntParam): 468 ↛ 469line 468 didn't jump to line 469 because the condition on line 468 was never true
469 return self.Value < other.Value
470 else:
471 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by < operator.")
472 if version_info >= (3, 11): # pragma: no cover
473 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
474 raise ex
476 def __le__(self, other: Any) -> bool:
477 """
478 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for less-than-or-equal.
480 :param other: Parameter to compare against.
481 :returns: ``True``, if the wrapped value is less than or equal the other value.
482 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
483 """
484 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
485 return self.Value <= other
486 elif isinstance(other, CallByRefIntParam): 486 ↛ 487line 486 didn't jump to line 487 because the condition on line 486 was never true
487 return self.Value <= other.Value
488 else:
489 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by <= operator.")
490 if version_info >= (3, 11): # pragma: no cover
491 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
492 raise ex
494 def __gt__(self, other: Any) -> bool:
495 """
496 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for geater-than.
498 :param other: Parameter to compare against.
499 :returns: ``True``, if the wrapped value is greater than the other value.
500 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
501 """
502 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
503 return self.Value > other
504 elif isinstance(other, CallByRefIntParam): 504 ↛ 505line 504 didn't jump to line 505 because the condition on line 504 was never true
505 return self.Value > other.Value
506 else:
507 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by > operator.")
508 if version_info >= (3, 11): # pragma: no cover
509 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
510 raise ex
512 def __ge__(self, other: Any) -> bool:
513 """
514 Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for greater-than-or-equal.
516 :param other: Parameter to compare against.
517 :returns: ``True``, if the wrapped value is greater than or equal the other value.
518 :raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
519 """
520 if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
521 return self.Value >= other
522 elif isinstance(other, CallByRefIntParam): 522 ↛ 523line 522 didn't jump to line 523 because the condition on line 522 was never true
523 return self.Value >= other.Value
524 else:
525 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by >= operator.")
526 if version_info >= (3, 11): # pragma: no cover
527 ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
528 raise ex
530 # Type conversion operators
531 def __bool__(self) -> bool:
532 """
533 Type conversion to :class:`bool`.
535 :returns: The boolean representation of the wrapped integer value.
536 """
537 return bool(self.Value)
539 def __int__(self) -> int:
540 """
541 Type conversion to :class:`int`.
543 :returns: The wrapped value."""
544 return self.Value
546 def __float__(self):
547 """
548 Type conversion to :class:`float`.
550 :returns: The float representation of the wrapped integer value.
551 """
552 return float(self.Value)