# ==================================================================================================================== #
# _____ _ _ ____ _ _ ____ ____ __ #
# _ __ _ |_ _|__ ___ | (_)_ __ __ _ / ___|__ _| | | __ ) _ _| _ \ ___ / _| #
# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` || | / _` | | | _ \| | | | |_) / _ \ |_ #
# | |_) | |_| || | (_) | (_) | | | | | | (_| || |__| (_| | | | |_) | |_| | _ < __/ _| #
# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)____\__,_|_|_|____/ \__, |_| \_\___|_| #
# |_| |___/ |___/ |___/ #
# ==================================================================================================================== #
# Authors: #
# Patrick Lehmann #
# #
# License: #
# ==================================================================================================================== #
# Copyright 2017-2024 Patrick Lehmann - Bötzingen, Germany #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. #
# You may obtain a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
# #
# SPDX-License-Identifier: Apache-2.0 #
# ==================================================================================================================== #
#
"""
Auxiliary classes to implement call-by-reference.
.. hint:: See :ref:`high-level help <COMMON/CallByRef>` for explanations and usage examples.
"""
from decimal import Decimal
from sys import version_info # needed for versions before Python 3.11
from typing import Any, Generic, TypeVar, Optional as Nullable
try:
from pyTooling.Decorators import export
from pyTooling.MetaClasses import ExtendedType
except (ImportError, ModuleNotFoundError): # pragma: no cover
print("[pyTooling.CallByRef] Could not import from 'pyTooling.*'!")
try:
from Decorators import export
from MetaClasses import ExtendedType
except (ImportError, ModuleNotFoundError) as ex: # pragma: no cover
print("[pyTooling.CallByRef] Could not import directly!")
raise ex
T = TypeVar("T")
[docs]
@export
class CallByRefParam(Generic[T], metaclass=ExtendedType, slots=True):
"""
Implements a *call-by-reference* parameter.
.. seealso::
* :class:`CallByRefBoolParam` |br|
|rarr| A special *call-by-reference* implementation for boolean reference types.
* :class:`CallByRefIntParam` |br|
|rarr| A special *call-by-reference* implementation for integer reference types.
"""
Value: T #: internal value
[docs]
def __init__(self, value: Nullable[T] = None) -> None:
"""Constructs a *call-by-reference* object for any type.
:param value: The value to be set as an initial value.
"""
self.Value = value
[docs]
def __ilshift__(self, other: T) -> 'CallByRefParam[T]': # Starting with Python 3.11+, use typing.Self as return type
"""Assigns a value to the *call-by-reference* object.
:param other: The value to be assigned to this *call-by-reference* object.
:returns: Itself.
"""
self.Value = other
return self
# binary operators - comparison
[docs]
def __eq__(self, other: Any) -> bool:
"""
Compare a CallByRefParam wrapped value with another instances (CallbyRefParam) or non-wrapped value for equality.
:param other: Parameter to compare against.
:returns: ``True``, if both values are equal.
"""
if isinstance(other, CallByRefParam):
return self.Value == other.Value
else:
return self.Value == other
[docs]
def __ne__(self, other) -> bool:
"""
Compare a CallByRefParam wrapped value with another instances (CallbyRefParam) or non-wrapped value for inequality.
:param other: Parameter to compare against.
:returns: ``True``, if both values are unequal.
"""
if isinstance(other, CallByRefParam):
return self.Value != other.Value
else:
return self.Value != other
# Type conversion operators
[docs]
def __repr__(self) -> str:
"""
Returns the wrapped object's string representation.
:returns: The string representation of the wrapped value.
"""
return repr(self.Value)
[docs]
def __str__(self) -> str:
"""
Returns the wrapped object's string equivalent.
:returns: The string equivalent of the wrapped value.
"""
return str(self.Value)
[docs]
@export
class CallByRefBoolParam(CallByRefParam):
"""A special *call-by-reference* implementation for boolean reference types."""
# Binary operators - comparison
[docs]
def __eq__(self, other: Any) -> bool:
"""
Compare a CallByRefBoolParam wrapped boolean value with another instances (CallByRefBoolParam) or non-wrapped boolean value for equality.
:param other: Parameter to compare against.
:returns: ``True``, if both values are equal.
:raises TypeError: If parameter ``other`` is not of type :class:`bool` or :class:`CallByRefBoolParam`.
"""
if isinstance(other, bool):
return self.Value == other
elif isinstance(other, CallByRefBoolParam):
return self.Value == other.Value
else:
ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.")
if version_info >= (3, 11): # pragma: no cover
ex.add_note(f"Supported types for second operand: bool, CallByRefBoolParam")
raise ex
[docs]
def __ne__(self, other) -> bool:
"""
Compare a CallByRefBoolParam wrapped boolean value with another instances (CallByRefBoolParam) or non-wrapped boolean value for inequality.
:param other: Parameter to compare against.
:returns: ``True``, if both values are unequal.
:raises TypeError: If parameter ``other`` is not of type :class:`bool` or :class:`CallByRefBoolParam`.
"""
if isinstance(other, bool):
return self.Value != other
elif isinstance(other, CallByRefBoolParam):
return self.Value != other.Value
else:
ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by != operator.")
if version_info >= (3, 11): # pragma: no cover
ex.add_note(f"Supported types for second operand: bool, CallByRefBoolParam")
raise ex
# Type conversion operators
[docs]
def __bool__(self) -> bool:
"""
Type conversion to :class:`bool`.
:returns: The wrapped value.
"""
return self.Value
[docs]
def __int__(self) -> int:
"""
Type conversion to :class:`int`.
:returns: The integer representation of the wrapped boolean value.
"""
return int(self.Value)
[docs]
@export
class CallByRefIntParam(CallByRefParam):
"""A special *call-by-reference* implementation for integer reference types."""
# Unary operators
[docs]
def __neg__(self) -> int:
"""Negate: -self."""
return -self.Value
[docs]
def __pos__(self) -> int:
"""Positive: +self."""
return +self.Value
[docs]
def __invert__(self) -> int:
"""Invert: ~self."""
return ~self.Value
# Binary operators - logical
[docs]
def __and__(self, other: Any) -> int:
"""And: self & other."""
if isinstance(other, int):
return self.Value & other
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by and operator.")
[docs]
def __or__(self, other: Any) -> int:
"""Or: self | other."""
if isinstance(other, int):
return self.Value | other
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by or operator.")
[docs]
def __xor__(self, other: Any) -> int:
"""Xor: self ^ other."""
if isinstance(other, int):
return self.Value ^ other
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
# Binary inplace operators
[docs]
def __iand__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type
"""Inplace and: self &= other."""
if isinstance(other, int):
self.Value &= other
return self
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by &= operator.")
[docs]
def __ior__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type
r"""Inplace or: self \|= other."""
if isinstance(other, int):
self.Value |= other
return self
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by |= operator.")
[docs]
def __ixor__(self, other: Any) -> 'CallByRefIntParam': # Starting with Python 3.11+, use typing.Self as return type
r"""Inplace or: self \|= other."""
if isinstance(other, int):
self.Value ^= other
return self
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by ^= operator.")
# Binary operators - arithmetic
[docs]
def __add__(self, other: Any) -> int:
"""Addition: self + other."""
if isinstance(other, int):
return self.Value + other
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by + operator.")
[docs]
def __sub__(self, other: Any) -> int:
"""Subtraction: self - other."""
if isinstance(other, int):
return self.Value - other
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by - operator.")
[docs]
def __truediv__(self, other: Any) -> int:
"""Division: self / other."""
if isinstance(other, int):
return self.Value / other
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by / operator.")
[docs]
def __floordiv__(self, other: Any) -> int:
"""Floor division: self // other."""
if isinstance(other, int):
return self.Value // other
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by // operator.")
[docs]
def __mul__(self, other: Any) -> int:
"""Multiplication: self * other."""
if isinstance(other, int):
return self.Value * other
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by * operator.")
[docs]
def __mod__(self, other: Any) -> int:
"""Modulo: self % other."""
if isinstance(other, int):
return self.Value % other
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by % operator.")
[docs]
def __pow__(self, other: Any) -> int:
"""Power: self ** other."""
if isinstance(other, int):
return self.Value ** other
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by ** operator.")
# Binary inplace operators - arithmetic
[docs]
def __iadd__(self, other: Any) -> 'CallByRefIntParam':
"""Addition: self += other."""
if isinstance(other, int):
self.Value += other
return self
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
[docs]
def __isub__(self, other: Any) -> 'CallByRefIntParam':
"""Subtraction: self -= other."""
if isinstance(other, int):
self.Value -= other
return self
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
[docs]
def __idiv__(self, other: Any) -> 'CallByRefIntParam':
"""Division: self /= other."""
if isinstance(other, int):
self.Value /= other
return self
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
[docs]
def __ifloordiv__(self, other: Any) -> 'CallByRefIntParam':
"""Floor division: self // other."""
if isinstance(other, int):
self.Value //= other
return self
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
[docs]
def __imul__(self, other: Any) -> 'CallByRefIntParam':
r"""Multiplication: self \*= other."""
if isinstance(other, int):
self.Value *= other
return self
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
[docs]
def __imod__(self, other: Any) -> 'CallByRefIntParam':
"""Modulo: self %= other."""
if isinstance(other, int):
self.Value %= other
return self
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
[docs]
def __ipow__(self, other: Any) -> 'CallByRefIntParam':
r"""Power: self \*\*= other."""
if isinstance(other, int):
self.Value **= other
return self
else:
raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by xor operator.")
# Binary operators - comparison
[docs]
def __eq__(self, other: Any) -> bool:
"""
Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for equality.
:param other: Parameter to compare against.
:returns: ``True``, if both values are equal.
:raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
"""
if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
return self.Value == other
elif isinstance(other, CallByRefIntParam):
return self.Value == other.Value
else:
ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.")
if version_info >= (3, 11): # pragma: no cover
ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
raise ex
[docs]
def __ne__(self, other) -> bool:
"""
Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for inequality.
:param other: Parameter to compare against.
:returns: ``True``, if both values are unequal.
:raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
"""
if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
return self.Value != other
elif isinstance(other, CallByRefIntParam):
return self.Value != other.Value
else:
ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by != operator.")
if version_info >= (3, 11): # pragma: no cover
ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
raise ex
[docs]
def __lt__(self, other: Any) -> bool:
"""
Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for less-than.
:param other: Parameter to compare against.
:returns: ``True``, if the wrapped value is less than the other value.
:raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
"""
if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
return self.Value < other
elif isinstance(other, CallByRefIntParam):
return self.Value < other.Value
else:
ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by < operator.")
if version_info >= (3, 11): # pragma: no cover
ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
raise ex
[docs]
def __le__(self, other: Any) -> bool:
"""
Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for less-than-or-equal.
:param other: Parameter to compare against.
:returns: ``True``, if the wrapped value is less than or equal the other value.
:raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
"""
if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
return self.Value <= other
elif isinstance(other, CallByRefIntParam):
return self.Value <= other.Value
else:
ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by <= operator.")
if version_info >= (3, 11): # pragma: no cover
ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
raise ex
[docs]
def __gt__(self, other: Any) -> bool:
"""
Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for geater-than.
:param other: Parameter to compare against.
:returns: ``True``, if the wrapped value is greater than the other value.
:raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
"""
if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
return self.Value > other
elif isinstance(other, CallByRefIntParam):
return self.Value > other.Value
else:
ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by > operator.")
if version_info >= (3, 11): # pragma: no cover
ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
raise ex
[docs]
def __ge__(self, other: Any) -> bool:
"""
Compare a CallByRefIntParam wrapped integer value with another instances (CallByRefIntParam) or non-wrapped integer value for greater-than-or-equal.
:param other: Parameter to compare against.
:returns: ``True``, if the wrapped value is greater than or equal the other value.
:raises TypeError: If parameter ``other`` is not of type :class:`int`, :class:`float`, :class:`complex`, :class:`Decimal` or :class:`CallByRefParam`.
"""
if isinstance(other, (int, float, complex, Decimal)) and not isinstance(other, bool):
return self.Value >= other
elif isinstance(other, CallByRefIntParam):
return self.Value >= other.Value
else:
ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by >= operator.")
if version_info >= (3, 11): # pragma: no cover
ex.add_note(f"Supported types for second operand: int, float, complex, Decimal, CallByRefIntParam")
raise ex
# Type conversion operators
[docs]
def __bool__(self) -> bool:
"""
Type conversion to :class:`bool`.
:returns: The boolean representation of the wrapped integer value.
"""
return bool(self.Value)
[docs]
def __int__(self) -> int:
"""
Type conversion to :class:`int`.
:returns: The wrapped value."""
return self.Value
[docs]
def __float__(self):
"""
Type conversion to :class:`float`.
:returns: The float representation of the wrapped integer value.
"""
return float(self.Value)