objecttools

This module implements tools to help to standardize the functionality of the different objects defined by the HydPy framework.

Module objecttools implements the following members:

  • F Type variable.

  • assignrepr_list “Double Singleton class”, see the documentation on assignrepr_tuple and assignrepr_list.

  • assignrepr_tuple “Double Singleton class”, see the documentation on assignrepr_tuple and assignrepr_list.

  • repr_ Modifies repr() for strings and floats, mainly for supporting clean float and path representations that are compatible with doctest.

  • classname() Return the class name of the given instance object or class.

  • value_of_type() Returns a string containing both the informal string and the type of the given value.

  • modulename() Return the module name of the given instance object.

  • devicename() Try to return the name of the (indirect) master Node or Element instance, if not possible return ?.

  • elementphrase() Return the phrase used in exception messages to indicate which Element is affected.

  • nodephrase() Return the phrase used in exception messages to indicate which Node is affected.

  • devicephrase() Try to return the phrase used in exception messages to indicate which Element or which Node is affected. If not possible, return just the name of the given object.

  • submodelphrase() Return a standard phrase for listing a model and its submodels in exception messages.

  • valid_variable_identifier() Raises an ValueError if the given name is not a valid Python identifier.

  • augment_excmessage() Augment an exception message with additional information while keeping the original traceback.

  • get_printtarget() Get a suitable file object reading for writing text useable as the file argument of the standard print() function.

  • decorator() Function decorator() adds type hints to function decorator of the site-package wrapt without changing its functionality.

  • excmessage_decorator() Wrap a function with augment_excmessage().

  • ResetAttrFuncs Reset all attribute related methods of the given class temporarily.

  • copy_() Copy function for classes with modified attribute functions.

  • deepcopy_() Deepcopy function for classes with modified attribute functions.

  • repr_values() Return comma separated representations of the given values using function repr_.

  • repr_numbers() Return comma separated representations of the given numbers using function repr_.

  • print_vector() Print the given vector in multiple lines with a certain maximum width.

  • print_matrix() Print the given matrix in multiple lines with a certain maximum width.

  • repr_tuple() Return a tuple representation of the given values using function repr().

  • repr_list() Return a list representation of the given values using function repr().

  • assignrepr_value() Return a prefixed string representation of the given value using function repr().

  • assignrepr_values() Return a prefixed, wrapped and properly aligned string representation of the given values using function repr().

  • assignrepr_values2() Return a prefixed and properly aligned string representation of the given 2-dimensional value matrix using function repr().

  • assignrepr_tuple2() Return a prefixed, wrapped and properly aligned tuple string representation of the given 2-dimensional value matrix using function repr().

  • assignrepr_list2() Return a prefixed, wrapped and properly aligned list string representation of the given 2-dimensional value matrix using function repr().

  • assignrepr_tuple3() Return a prefixed, wrapped and properly aligned tuple string representation of the given 3-dimensional value matrix using function repr().

  • assignrepr_list3() Return a prefixed, wrapped and properly aligned list string representation of the given 3-dimensional value matrix using function repr().

  • flatten_repr() Remove the newline characters from the string representation of the given object.

  • round_() Prints values with a maximum number of digits in doctests.

  • extract() Return a generator that extracts certain objects from values.

  • enumeration() Return an enumeration string based on the given values.

  • description() Returns the first “paragraph” of the docstring of the given object.

  • apply_black() Return a string representation of an instance of a class based on the given name, positional arguments and keyword arguments.

  • value2bool() Convert the given string or integer value to a boolean and return it.

  • is_equal() Check if the given nested data objects agree in their structure and values.


hydpy.core.objecttools.classname(self: object) str[source]

Return the class name of the given instance object or class.

>>> from hydpy import classname
>>> from hydpy import pub
>>> classname(float)
'float'
>>> classname(pub.options)
'Options'
hydpy.core.objecttools.value_of_type(value: object) str[source]

Returns a string containing both the informal string and the type of the given value.

This function is intended to simplifying writing HydPy exceptions, which frequently contain the following phrase:

>>> from hydpy.core.objecttools import value_of_type
>>> value_of_type(999)
'value `999` of type `int`'
hydpy.core.objecttools.modulename(self: object) str[source]

Return the module name of the given instance object.

>>> from hydpy.core.objecttools import modulename
>>> from hydpy import pub
>>> print(modulename(pub.options))
optiontools
hydpy.core.objecttools.devicename(self: object) str[source]

Try to return the name of the (indirect) master Node or Element instance, if not possible return ?.

>>> from hydpy import prepare_model
>>> model = prepare_model("hland_96")
>>> from hydpy.core.objecttools import devicename
>>> devicename(model)
'?'
>>> from hydpy import Element
>>> e1 = Element("e1", outlets="n1")
>>> e1.model = model
>>> devicename(e1)
'e1'
>>> devicename(model)
'e1'
hydpy.core.objecttools.elementphrase(self: object) str[source]

Return the phrase used in exception messages to indicate which Element is affected.

>>> from hydpy.core.modeltools import Model
>>> class Test(Model):
...     def connect(self):
...         pass
>>> model = Test()
>>> from hydpy.core.objecttools import elementphrase
>>> elementphrase(model)
'`test` of element `?`'
>>> from hydpy import Element
>>> model.element = Element("e1")
>>> elementphrase(model)
'`test` of element `e1`'
hydpy.core.objecttools.nodephrase(self: object) str[source]

Return the phrase used in exception messages to indicate which Node is affected.

>>> from hydpy.core.sequencetools import Sequences
>>> sequences = Sequences(None)
>>> from hydpy.core.objecttools import nodephrase
>>> nodephrase(sequences)
'`sequences` of node `?`'
>>> sequences.name = "test"
>>> nodephrase(sequences)
'`test` of node `?`'
>>> from hydpy import Node
>>> n1 = Node("n1")
>>> nodephrase(n1.sequences.sim)
'`sim` of node `n1`'
hydpy.core.objecttools.devicephrase(self: object) str[source]

Try to return the phrase used in exception messages to indicate which Element or which Node is affected. If not possible, return just the name of the given object.

>>> from hydpy.core.modeltools import Model
>>> class Test(Model):
...     def connect(self):
...         return None
>>> model = Test()
>>> from hydpy.core.objecttools import devicephrase
>>> devicephrase(model)
'`test`'
>>> from hydpy import Element
>>> model.element = Element("e1")
>>> devicephrase(model)
'`test` of element `e1`'
>>> from hydpy import Node
>>> n1 = Node("n1")
>>> devicephrase(n1.sequences.sim)
'`sim` of node `n1`'
hydpy.core.objecttools.submodelphrase(model: modeltools.Model, include_subsubmodels: bool = True) str[source]

Return a standard phrase for listing a model and its submodels in exception messages.

>>> from hydpy import prepare_model
>>> from hydpy.core.objecttools import submodelphrase
>>> model = prepare_model("lland_dd")
>>> submodelphrase(model)
'model `lland_dd`'
>>> model.aetmodel = prepare_model("evap_aet_minhas")
>>> submodelphrase(model)
'model `lland_dd` and its submodel (`aetmodel/evap_aet_minhas`)'
>>> model.soilmodel = prepare_model("ga_garto_submodel1")
>>> submodelphrase(model)
'model `lland_dd` and its submodels (`aetmodel/evap_aet_minhas` and `soilmodel/ga_garto_submodel1`)'
hydpy.core.objecttools.valid_variable_identifier(string: str) None[source]

Raises an ValueError if the given name is not a valid Python identifier.

For example, the string test_1 (with underscore) is valid…

>>> from hydpy.core.objecttools import valid_variable_identifier
>>> valid_variable_identifier("test_1")

…but the string test 1 (with white space) is not:

>>> valid_variable_identifier("test 1")
Traceback (most recent call last):
...
ValueError: The given name string `test 1` does not define a valid variable identifier.  Valid identifiers do not contain characters like `-` or empty spaces, do not start with numbers, cannot be mistaken with Python built-ins like `for`...)

Also, names of Python built ins are not allowed:

>>> valid_variable_identifier("print")   
Traceback (most recent call last):
...
ValueError: The given name string `print` does not define...
hydpy.core.objecttools.augment_excmessage(prefix: str | None = None, suffix: str | None = None) NoReturn[source]

Augment an exception message with additional information while keeping the original traceback.

You can prefix and/or suffix text. If you prefix something (which happens much more often in the HydPy framework), the sub-clause “, the following error occurred:” is automatically included:

>>> from hydpy.core import objecttools
>>> import textwrap
>>> try:
...     1 + "1"
... except BaseException:
...     prefix = "While showing how prefixing works"
...     suffix = "(This is a final remark.)"
...     objecttools.augment_excmessage(prefix, suffix)
Traceback (most recent call last):
...
TypeError: While showing how prefixing works, the following error occurred: unsupported operand type(s) for +: 'int' and 'str' (This is a final remark.)

Some exceptions derived by site-packages do not support exception chaining due to requiring multiple initialisation arguments. In such cases, augment_excmessage() generates an exception with the same name on the fly and raises it afterwards:

>>> class WrongError(BaseException):
...     def __init__(self, arg1, arg2):
...         pass
>>> try:
...     raise WrongError("info 1", "info 2")
... except BaseException:
...     objecttools.augment_excmessage("While showing how prefixing works")
Traceback (most recent call last):
...
hydpy.core.objecttools.WrongError: While showing how prefixing works, the following error occurred: ('info 1', 'info 2')

Never use function augment_excmessage() outside except clauses:

>>> objecttools.augment_excmessage("While trying to do something")
Traceback (most recent call last):
...
RuntimeError: No exception available.  (Call function `augment_excmessage` only inside except clauses.)
hydpy.core.objecttools.decorator(wrapper: Callable[[...], Any]) Callable[[F], F][source]

Function decorator() adds type hints to function decorator of the site-package wrapt without changing its functionality.

hydpy.core.objecttools.excmessage_decorator(description_: str) Callable[[Callable[[P], T]], Callable[[P], T]][source]

Wrap a function with augment_excmessage().

Function excmessage_decorator() is a means to apply function augment_excmessage() more efficiently. Suppose you would apply function augment_excmessage() in a function that adds and returns to numbers:

>>> from  hydpy.core import objecttools
>>> def add(x, y):
...     try:
...         return x + y
...     except BaseException:
...         objecttools.augment_excmessage("While trying to add `x` and `y`")

This works as excepted…

>>> add(1, 2)
3
>>> add(1, [])
Traceback (most recent call last):
...
TypeError: While trying to add `x` and `y`, the following error occurred: unsupported operand type(s) for +: 'int' and 'list'

…but can be achieved with much less code using excmessage_decorator():

>>> @objecttools.excmessage_decorator("add `x` and `y`")
... def add(x, y):
...     return x+y
>>> add(1, 2)
3
>>> add(1, [])
Traceback (most recent call last):
...
TypeError: While trying to add `x` and `y`, the following error occurred: unsupported operand type(s) for +: 'int' and 'list'

Additionally, exception messages related to wrong function calls are now also augmented:

>>> add(1)
Traceback (most recent call last):
...
TypeError: While trying to add `x` and `y`, the following error occurred: add() missing 1 required positional argument: 'y'

excmessage_decorator() evaluates the given string like an f-string, allowing to mention the argument values of the called function and to make use of all string modification functions provided by modules objecttools:

>>> @objecttools.excmessage_decorator(
...     "add `x` ({repr_(x, 2)}) and `y` ({repr_(y, 2)})")
... def add(x, y):
...     return x+y
>>> add(1.1111, "wrong")
Traceback (most recent call last):
...
TypeError: While trying to add `x` (1.11) and `y` (wrong), the following error occurred: unsupported operand type(s) for +: 'float' and 'str'
>>> add(1)
Traceback (most recent call last):
...
TypeError: While trying to add `x` (1) and `y` (?), the following error occurred: add() missing 1 required positional argument: 'y'
>>> add(y=1)
Traceback (most recent call last):
...
TypeError: While trying to add `x` (?) and `y` (1), the following error occurred: add() missing 1 required positional argument: 'x'

Apply excmessage_decorator() on methods also works fine:

>>> class Adder:
...     def __init__(self):
...         self.value = 0
...     @objecttools.excmessage_decorator(
...         "add an instance of class `{classname(self)}` with value "
...         "`{repr_(other, 2)}` of type `{classname(other)}`")
...     def __iadd__(self, other):
...         self.value += other
...         return self
>>> adder = Adder()
>>> adder += 1
>>> adder.value
1
>>> adder += "wrong"
Traceback (most recent call last):
...
TypeError: While trying to add an instance of class `Adder` with value `wrong` of type `str`, the following error occurred: unsupported operand type(s) for +=: 'int' and 'str'

It is made sure that no information of the decorated function is lost:

>>> add.__name__
'add'
class hydpy.core.objecttools.ResetAttrFuncs(obj: object)[source]

Bases: object

Reset all attribute related methods of the given class temporarily.

The “related methods” are defined in class attribute funcnames.

There are (at least) two use cases for class ResetAttrFuncs, initialisation and copying, which are described below.

In HydPy, some classes define a __setattr__ method which raises exceptions when one tries to set “improper” instance attributes. The problem is, that such customized setattr methods often prevent from defining instance attributes within __init__ methods in the usual manner. Working on instance dictionaries instead can confuse some automatic tools (e.g. pylint). Class ResetAttrFuncs implements a trick to circumvent this problem.

To show how ResetAttrFuncs works, we first define a class with a __setattr__ method that does not allow to set any attribute:

>>> class Test:
...     def __setattr__(self, name, value):
...         raise AttributeError
>>> test = Test()
>>> test.var1 = 1
Traceback (most recent call last):
...
AttributeError

Assigning this class to ResetAttrFuncs allows for setting attributes to all its instances inside a with block in the usual manner:

>>> from hydpy.core.objecttools import ResetAttrFuncs
>>> with ResetAttrFuncs(test):
...     test.var1 = 1
>>> test.var1
1

After the end of the with block, the custom __setattr__ method of the test class works again and prevents from setting attributes:

>>> test.var2 = 2
Traceback (most recent call last):
...
AttributeError

The second use case is related to method __getattr__ and copying. The following test class stores its attributes (for whatever reasons) in a special dictionary called “dic” (note that how ResetAttrFuncs is used in the __init__ method):

>>> class Test:
...     def __init__(self):
...         with ResetAttrFuncs(self):
...             self.dic = {}
...     def __setattr__(self, name, value):
...         self.dic[name] = value
...     def __getattr__(self, name):
...         try:
...             return self.dic[name]
...         except KeyError:
...             raise AttributeError

Principally, this simple implementation does its job but its instances are not easily copyable under all Python versions:

>>> test = Test()
>>> test.var1 = 1
>>> test.var1
1
>>> import copy
>>> copy.deepcopy(test)   
Traceback (most recent call last):
...
RecursionError: maximum recursion depth exceeded ...

ResetAttrFuncs can be used to implement specialized __copy__ and __deepcopy__ methods, which rely on the temporary disabling of __getattr__. For simple cases, one can import the predefined functions copy_() and deepcopy_():

>>> from hydpy.core.objecttools import copy_, deepcopy_
>>> Test.__copy__ = copy_
>>> test2 = copy.copy(test)
>>> test2.var1
1
>>> Test.__deepcopy__ = deepcopy_
>>> test3 = copy.deepcopy(test)
>>> test3.var1
1

Note that an infinite recursion is avoided by also disabling methods __copy__ and __deepcopy__ themselves.

funcnames = ('__getattr__', '__setattr__', '__delattr__', '__copy__', '__deepcopy__')
cls
name2func
hydpy.core.objecttools.copy_(self: T) T[source]

Copy function for classes with modified attribute functions.

See the documentation on class ResetAttrFuncs for further information.

hydpy.core.objecttools.deepcopy_(self: T, memo: dict[int, object] | None) T[source]

Deepcopy function for classes with modified attribute functions.

See the documentation on class ResetAttrFuncs for further information.

hydpy.core.objecttools.repr_ = <hydpy.core.objecttools._Repr object>

Modifies repr() for strings and floats, mainly for supporting clean float and path representations that are compatible with doctest.

Use the already available instance repr_ instead of initialising a new repr_ object.

When value is a string, it is returned without any modification, except that the path separator “" (Windows) is replaced with “/” (Linux):

>>> from hydpy.core.objecttools import repr_
>>> print(r"directory\file")
directory\file
>>> print(repr(r"directory\file"))
'directory\\file'
>>> print(repr_(r"directory\file"))
directory/file

You can change this behaviour of function object repr(), when necessary:

>>> with repr_.preserve_strings(True):
...     print(repr_(r"directory\file"))
"directory/file"

Behind the with block, repr_ works as before (even in case of an error):

>>> print(repr_(r"directory\file"))
directory/file

When value is a float, the result depends on how the option reprdigits is set. Without defining a special value, repr() defines the number of digits in the usual, system dependent manner:

>>> from hydpy import pub
>>> with pub.options.reprdigits(-1):
...     repr(1.0/3.0) == repr_(1.0/3.0)
True

Through setting reprdigits to a positive integer value, one defines the maximum number of decimal places, which allows for doctesting across different systems and Python versions:

>>> repr_(1.0/3.0)
'0.333333'
>>> repr_(2.0/3.0)
'0.666667'
>>> repr_(1.0/2.0)
'0.5'

Changing the number of decimal places can be done via a with block:

>>> with pub.options.reprdigits(3):
...     print(repr_(1.0/3.0))
0.333

Such a change is only temporary (even in case of an error):

>>> repr_(1.0/3.0)
'0.333333'

repr() can also be applied on numpy’s float types:

>>> import numpy
>>> repr_(numpy.float64(1.0/3.0))
'0.333333'
>>> repr_(numpy.float32(1.0/3.0))
'0.333333'
>>> repr_(numpy.float16(1.0/3.0))
'0.333252'

Note that the deviation from the true result in the last example is due to the low precision of float16.

For scalar ndarray objects, repr_ returns its item’s string representation:

>>> repr_(numpy.array(1.0/3.0))
'0.333333'

On all types not mentioned above, the usual repr() function is applied, e.g.:

>>> repr([1, 2, 3])
'[1, 2, 3]'
>>> repr_([1, 2, 3])
'[1, 2, 3]'
hydpy.core.objecttools.repr_values(values: Sequence[object] | ndarray[Any, dtype[generic]]) str[source]

Return comma separated representations of the given values using function repr_.

>>> from hydpy.core.objecttools import repr_values
>>> repr_values([1.0/1.0, 1.0/2.0, 1.0/3.0])
'1.0, 0.5, 0.333333'

Note that the returned string is not wrapped.

hydpy.core.objecttools.repr_numbers(values: Number | Iterable[Number] | Iterable[Iterable[Number]]) str[source]

Return comma separated representations of the given numbers using function repr_.

Currently, function repr_numbers() can handle scalar values, 1-dimensional vectors, and 2-dimensional matrices:

>>> from hydpy.core.objecttools import repr_numbers
>>> repr_numbers(1.0/3.0)
'0.333333'
>>> repr_numbers([1.0/1.0, 1.0/2.0, 1.0/3.0])
'1.0, 0.5, 0.333333'
>>> repr_numbers([[1.0/1.0, 1.0/2.0, 1.0/3.0], [1.0/4.0, 1.0/5.0, 1.0/6.0]])
'1.0, 0.5, 0.333333; 0.25, 0.2, 0.166667'

Note that the returned string is not wrapped.

hydpy.core.objecttools.print_vector(values: Sequence[object] | ndarray[Any, dtype[generic]], width: int = 70) None[source]

Print the given vector in multiple lines with a certain maximum width.

By default, each line contains at most 70 characters:

>>> from hydpy import print_vector
>>> print_vector(range(21))
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20

You can change this default behaviour by passing an alternative number of characters:

>>> print_vector(range(21), width=30)
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20
hydpy.core.objecttools.print_matrix(values: Sequence[Sequence[object] | ndarray[Any, dtype[generic]]] | ndarray[Any, dtype[generic]], width: int = 70) None[source]

Print the given matrix in multiple lines with a certain maximum width.

By default, each line contains at most 70 characters:

>>> from hydpy import print_matrix
>>> print_matrix([range(5), range(20), range(10)])
| 0, 1, 2, 3, 4 |
| 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, >
> 19 |
| 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 |

You can change this default behaviour by passing an alternative number of characters:

>>> print_matrix([range(5), range(20), range(10)], width=30)
| 0, 1, 2, 3, 4 |
| 0, 1, 2, 3, 4, 5, 6, 7, 8, >
> 9, 10, 11, 12, 13, 14, 15, >
> 16, 17, 18, 19 |
| 0, 1, 2, 3, 4, 5, 6, 7, 8, >
> 9 |
hydpy.core.objecttools.repr_tuple(values: Sequence[object] | ndarray[Any, dtype[generic]]) str[source]

Return a tuple representation of the given values using function repr().

>>> from hydpy.core.objecttools import repr_tuple
>>> repr_tuple([1./1., 1./2., 1./3.])
'(1.0, 0.5, 0.333333)'

Note that the returned string is not wrapped.

In the special case of an iterable with only one entry, the returned string is still a valid tuple:

>>> repr_tuple([1.])
'(1.0,)'
hydpy.core.objecttools.repr_list(values: Sequence[object] | ndarray[Any, dtype[generic]]) str[source]

Return a list representation of the given values using function repr().

>>> from hydpy.core.objecttools import repr_list
>>> repr_list([1./1., 1./2., 1./3.])
'[1.0, 0.5, 0.333333]'

Note that the returned string is not wrapped.

hydpy.core.objecttools.assignrepr_value(value: object, prefix: str) str[source]

Return a prefixed string representation of the given value using function repr().

Note that the argument has no effect. It is thought for increasing usage compatibility with functions like assignrepr_list only.

>>> from hydpy.core.objecttools import assignrepr_value
>>> print(assignrepr_value(1./3., "test = "))
test = 0.333333
hydpy.core.objecttools.assignrepr_values(values: Sequence[object] | ndarray[Any, dtype[generic]], prefix: str, width: int | None = None, _fakeend: int = 0) str[source]

Return a prefixed, wrapped and properly aligned string representation of the given values using function repr().

>>> from hydpy.core.objecttools import assignrepr_values
>>> print(assignrepr_values(range(1, 13), "test(", 20) + ")")
test(1, 2, 3, 4, 5,
     6, 7, 8, 9, 10,
     11, 12)

If no width is given, no wrapping is performed:

>>> print(assignrepr_values(range(1, 13), "test(") + ")")
test(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)

To circumvent defining too long string representations, make use of the ellipsis option:

>>> from hydpy import pub
>>> with pub.options.ellipsis(1):
...     print(assignrepr_values(range(1, 13), "test(", 20) + ")")
test(1, ...,12)
>>> with pub.options.ellipsis(5):
...     print(assignrepr_values(range(1, 13), "test(", 20) + ")")
test(1, 2, 3, 4, 5,
     ...,8, 9, 10,
     11, 12)
>>> with pub.options.ellipsis(6):
...     print(assignrepr_values(range(1, 13), "test(", 20) + ")")
test(1, 2, 3, 4, 5,
     6, 7, 8, 9, 10,
     11, 12)
hydpy.core.objecttools.assignrepr_tuple = <hydpy.core.objecttools._AssignReprBracketed object>

Return a prefixed, wrapped and properly aligned tuple string representation of the given values using function repr().

>>> from hydpy.core.objecttools import assignrepr_tuple
>>> print(assignrepr_tuple(range(10), "test = ", 22))
test = (0, 1, 2, 3, 4,
        5, 6, 7, 8, 9)

If no width is given, no wrapping is performed:

>>> print(assignrepr_tuple(range(10), "test = "))
test = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

Functions assignrepr_tuple works also on empty iterables and those which possess only one entry:

>>> print(assignrepr_tuple([], "test = "))
test = ()
>>> print(assignrepr_tuple([10], "test = "))
test = (10,)

Optionally, bracketing single values can be prevented:

>>> with assignrepr_tuple.always_bracketed(False):
...     print(assignrepr_tuple([], "test = "))
...     print(assignrepr_tuple([10], "test = "))
...     print(assignrepr_tuple([10, 10], "test = "))
test = ()
test = 10
test = (10, 10)

Behind the with block, assignrepr_tuple works as before (even in case of an error):

>>> print(assignrepr_tuple([10], "test = "))
test = (10,)
hydpy.core.objecttools.assignrepr_list = <hydpy.core.objecttools._AssignReprBracketed object>

Return a prefixed, wrapped and properly aligned list string representation of the given values using function repr().

>>> from hydpy.core.objecttools import assignrepr_list
>>> print(assignrepr_list(range(10), "test = ", 22))
test = [0, 1, 2, 3, 4,
        5, 6, 7, 8, 9]

If no width is given, no wrapping is performed:

>>> print(assignrepr_list(range(10), "test = "))
test = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Functions assignrepr_list works also on empty iterables:

>>> print(assignrepr_list((), "test = "))
test = []

Optionally, bracketing single values can be prevented:

>>> with assignrepr_list.always_bracketed(False):
...     print(assignrepr_list([], "test = "))
...     print(assignrepr_list([10], "test = "))
...     print(assignrepr_list([10, 10], "test = "))
test = []
test = 10
test = [10, 10]

Behind the with block, assignrepr_list works as before (even in case of an error):

>>> print(assignrepr_list([10], "test = "))
test = [10,]
hydpy.core.objecttools.assignrepr_values2(values: Sequence[Sequence[object] | ndarray[Any, dtype[generic]]] | ndarray[Any, dtype[generic]], prefix: str, width: int | None = None) str[source]

Return a prefixed and properly aligned string representation of the given 2-dimensional value matrix using function repr().

>>> from hydpy.core.objecttools import assignrepr_values2
>>> import numpy
>>> print(assignrepr_values2(numpy.eye(3), "test(") + ")")
test(1.0, 0.0, 0.0,
     0.0, 1.0, 0.0,
     0.0, 0.0, 1.0)

Functions assignrepr_values2() works also on empty iterables:

>>> print(assignrepr_values2([[]], "test(") + ")")
test()
hydpy.core.objecttools.assignrepr_tuple2(values: Sequence[Sequence[object] | ndarray[Any, dtype[generic]]] | ndarray[Any, dtype[generic]], prefix: str, width: int | None = None) str[source]

Return a prefixed, wrapped and properly aligned tuple string representation of the given 2-dimensional value matrix using function repr().

>>> from hydpy.core.objecttools import assignrepr_tuple2
>>> import numpy
>>> print(assignrepr_tuple2(numpy.eye(3), "test = ", 18))
test = ((1.0, 0.0,
         0.0),
        (0.0, 1.0,
         0.0),
        (0.0, 0.0,
         1.0))

If no width is given, no wrapping is performed:

>>> print(assignrepr_tuple2(numpy.eye(3), "test = "))
test = ((1.0, 0.0, 0.0),
        (0.0, 1.0, 0.0),
        (0.0, 0.0, 1.0))

Functions assignrepr_tuple2() works also on empty iterables and those which possess only one entry:

>>> print(assignrepr_tuple2([[]], "test = "))
test = ((),)
>>> print(assignrepr_tuple2([], "test = "))
test = ()
>>> print(assignrepr_tuple2([[], [1]], "test = "))
test = ((),
        (1,))
hydpy.core.objecttools.assignrepr_list2(values: Sequence[Sequence[object] | ndarray[Any, dtype[generic]]] | ndarray[Any, dtype[generic]], prefix: str, width: int | None = None) str[source]

Return a prefixed, wrapped and properly aligned list string representation of the given 2-dimensional value matrix using function repr().

>>> from hydpy.core.objecttools import assignrepr_list2
>>> import numpy
>>> print(assignrepr_list2(numpy.eye(3), "test = ", 18))
test = [[1.0, 0.0,
         0.0],
        [0.0, 1.0,
         0.0],
        [0.0, 0.0,
         1.0]]

If no width is given, no wrapping is performed:

>>> print(assignrepr_list2(numpy.eye(3), "test = "))
test = [[1.0, 0.0, 0.0],
        [0.0, 1.0, 0.0],
        [0.0, 0.0, 1.0]]

Functions assignrepr_list2() works also on empty iterables:

>>> print(assignrepr_list2([[]], "test = "))
test = [[]]
>>> print(assignrepr_list2([], "test = "))
test = []
>>> print(assignrepr_list2([[], [1]], "test = "))
test = [[],
        [1]]
hydpy.core.objecttools.assignrepr_tuple3(values: Sequence[Sequence[Sequence[object] | ndarray[Any, dtype[generic]]] | ndarray[Any, dtype[generic]]] | ndarray[Any, dtype[generic]], prefix: str, width: int | None = None) str[source]

Return a prefixed, wrapped and properly aligned tuple string representation of the given 3-dimensional value matrix using function repr().

>>> from hydpy.core.objecttools import assignrepr_tuple3
>>> import numpy
>>> values = [numpy.eye(3), numpy.ones((3, 3))]
>>> print(assignrepr_tuple3(values, "test = ", 18))
test = (((1.0,
          0.0,
          0.0),
         (0.0,
          1.0,
          0.0),
         (0.0,
          0.0,
          1.0)),
        ((1.0,
          1.0,
          1.0),
         (1.0,
          1.0,
          1.0),
         (1.0,
          1.0,
          1.0)))

If no width is given, no wrapping is performed:

>>> print(assignrepr_tuple3(values, "test = "))
test = (((1.0, 0.0, 0.0),
         (0.0, 1.0, 0.0),
         (0.0, 0.0, 1.0)),
        ((1.0, 1.0, 1.0),
         (1.0, 1.0, 1.0),
         (1.0, 1.0, 1.0)))

Functions assignrepr_tuple3() works also on empty iterables and those which possess only one entry:

>>> print(assignrepr_tuple3([[[]]], "test = "))
test = (((),),)
>>> print(assignrepr_tuple3([[]], "test = "))
test = ((),)
>>> print(assignrepr_tuple3([], "test = "))
test = ()
>>> print(assignrepr_tuple3([[[], [1]]], "test = "))
test = (((),
         (1,)),)
hydpy.core.objecttools.assignrepr_list3(values: Sequence[Sequence[Sequence[object] | ndarray[Any, dtype[generic]]] | ndarray[Any, dtype[generic]]] | ndarray[Any, dtype[generic]], prefix: str, width: int | None = None) str[source]

Return a prefixed, wrapped and properly aligned list string representation of the given 3-dimensional value matrix using function repr().

>>> from hydpy.core.objecttools import assignrepr_list3
>>> import numpy
>>> values = [numpy.eye(3), numpy.ones((3, 3))]
>>> print(assignrepr_list3(values, "test = ", 18))
test = [[[1.0,
          0.0,
          0.0],
         [0.0,
          1.0,
          0.0],
         [0.0,
          0.0,
          1.0]],
        [[1.0,
          1.0,
          1.0],
         [1.0,
          1.0,
          1.0],
         [1.0,
          1.0,
          1.0]]]

If no width is given, no wrapping is performed:

>>> print(assignrepr_list3(values, "test = "))
test = [[[1.0, 0.0, 0.0],
         [0.0, 1.0, 0.0],
         [0.0, 0.0, 1.0]],
        [[1.0, 1.0, 1.0],
         [1.0, 1.0, 1.0],
         [1.0, 1.0, 1.0]]]

Functions assignrepr_list3() works also on empty iterables and those which possess only one entry:

>>> print(assignrepr_list3([[[]]], "test = "))
test = [[[]]]
>>> print(assignrepr_list3([[]], "test = "))
test = [[]]
>>> print(assignrepr_list3([], "test = "))
test = []
>>> print(assignrepr_list3([[[], [1]]], "test = "))
test = [[[],
         [1]]]
hydpy.core.objecttools.flatten_repr(self: object) str[source]

Remove the newline characters from the string representation of the given object.

Complex string representations like the following one convenient when working interactively but cause line breaks when included in strings like in exception messages:

>>> from hydpy import Node
>>> node = Node("name", keywords="test")
>>> node
Node("name", variable="Q",
     keywords="test")

Use function flatten_repr() to prevent any line breaks:

>>> from hydpy.core.objecttools import flatten_repr
>>> print(flatten_repr(node))
Node("name", variable="Q", keywords="test")

When implementing a new class into the HydPy framework requiring a complex “repr() string”, either customise a simpler “str string” manually (as already done for the class Node or use function flatten_repr():

>>> print(f"We print {node}!")
We print name!
>>> __str__ = Node.__str__
>>> Node.__str__ = flatten_repr
>>> print(f"We print {node}!")
We print Node("name", variable="Q", keywords="test")!
>>> Node.__str__ = __str__

The named tuple subclass Characteristics of application model kinw_williams relies on function flatten_repr():

>>> from hydpy.models.kinw_williams import Characteristics
>>> characteristics = Characteristics(
...     waterstage=1.0,
...     discharge=5.0,
...     derivative=0.1,
...     length_orig=3.0,
...     nmb_subsections=4,
...     length_adj=2.0,
... )
>>> characteristics
Characteristics(
    waterstage=1.0,
    discharge=5.0,
    derivative=0.1,
    length_orig=3.0,
    nmb_subsections=4,
    length_adj=2.0,
)
>>> print(characteristics)
Characteristics(waterstage=1.0, discharge=5.0, derivative=0.1, length_orig=3.0, nmb_subsections=4, length_adj=2.0)

You can apply function flatten_repr() on arbitrary objects on the fly, but without any guarantee, the result always looks good. For the following simple examples on some built-in types, everything seems to work:

>>> flatten_repr(1)
'1'
>>> flatten_repr((1, 2))
'(1, 2)'
>>> flatten_repr([(1,2),(3,4)])
'[(1, 2), (3, 4)]'
hydpy.core.objecttools.round_(values: object | Sequence[object] | ndarray[Any, dtype[generic]], decimals: int | None = None, *, width: int = 0, lfill: str | None = None, rfill: str | None = None, sep: str = ' ', end: str = '\n', file_: TextIO | None = None) None[source]

Prints values with a maximum number of digits in doctests.

See the documentation on function repr() for more details, and note that the optional keyword arguments are passed to the print function.

Usually one would apply function round_() on a single or a vector of numbers:

>>> from hydpy import round_
>>> round_(1.0/3.0, decimals=6)
0.333333
>>> round_((1.0/2.0, 1.0/3.0, 1.0/4.0), decimals=4)
0.5, 0.3333, 0.25

The special case of 0-dimensional numpy ndarray objects does not cause a problem:

>>> from numpy import array
>>> round_(array(1.0/3.0))
0.333333

Additionally, one can supply a width and a rfill argument:

>>> round_(1.0, width=6, rfill="0")
1.0000

Alternatively, one can use the lfill arguments, which might e.g. be usefull for aligning different strings:

>>> round_("test", width=6, lfill="_")
__test

Using both the lfill and the rfill argument raises an error:

>>> round_(1.0, lfill="_", rfill="0")
Traceback (most recent call last):
...
ValueError: For function `round_` values are passed for both arguments `lfill` and `rfill`.  This is not allowed.
hydpy.core.objecttools.extract(values: Iterable[object] | object, types_: tuple[type[T1]] | tuple[type[T1], type[T2]] | tuple[type[T1], type[T2], type[T3]], skip: bool = False) Iterator[T1 | T2 | T3][source]

Return a generator that extracts certain objects from values.

This function is thought for supporting the definition of functions with arguments, that can be objects of certain types or that can be iterables containing these objects.

The following examples show that function extract() basically implements a type specific flattening mechanism:

>>> from hydpy.core.objecttools import extract
>>> tuple(extract("str1", (str, int)))
('str1',)
>>> tuple(extract(["str1", "str2"], (str, int)))
('str1', 'str2')
>>> tuple(extract((["str1", "str2"], [1,]), (str, int)))
('str1', 'str2', 1)

If an object is neither iterable nor of the required type, the following exception is raised:

>>> tuple(extract("str1", (int,)))
Traceback (most recent call last):
...
TypeError: The given (sub)value `'str1'` is not an instance of the following classes: int.
>>> tuple(extract((["str1", "str2"], [None, 1]), (str, int)))
Traceback (most recent call last):
...
TypeError: The given (sub)value `None` is not an instance of the following classes: str and int.

Optionally, None values can be skipped:

>>> tuple(extract(None, (str, int), True))
()
>>> tuple(extract((["str1", "str2"], [None, 1]), (str, int), True))
('str1', 'str2', 1)
hydpy.core.objecttools.enumeration(values: ~collections.abc.Iterable[~hydpy.core.typingtools.T], converter: ~collections.abc.Callable[[~hydpy.core.typingtools.T], str] = <class 'str'>, default: str = '', conjunction: str = 'and') str[source]

Return an enumeration string based on the given values.

The following four examples show the standard output of function enumeration():

>>> from hydpy.core.objecttools import enumeration
>>> enumeration(("text", 3, []))
'text, 3, and []'
>>> enumeration(('text', 3))
'text and 3'
>>> enumeration(('text',))
'text'
>>> enumeration(())
''

All given objects are converted to strings by function str, as shown by the first two examples. A callback function expecting a single argument and returning a string can change this behaviour.

>>> from hydpy import classname
>>> enumeration(("text", 3, []), converter=classname)
'str, int, and list'

You can define a default value that enumeration() will return if it receives an empty iterable:

>>> enumeration((), default="nothing")
'nothing'

Function enumeration() respects option ellipsis:

>>> from hydpy import pub
>>> with pub.options.ellipsis(3):
...     enumeration(range(10))
'0, 1, 2, ..., 7, 8, and 9'

You can replace the conjunction “and” with any other word:

>>> enumeration(range(1), conjunction="or")
'0'
>>> enumeration(range(2), conjunction="or")
'0 or 1'
>>> with pub.options.ellipsis(3):
...     enumeration(range(10), conjunction="or")
'0, 1, 2, ..., 7, 8, or 9'
hydpy.core.objecttools.description(self: object) str[source]

Returns the first “paragraph” of the docstring of the given object.

Note that ugly things like multiple whitespaces and newline characters are removed:

>>> from hydpy.core.objecttools import description, augment_excmessage
>>> description(augment_excmessage)
'Augment an exception message with additional information while keeping the original traceback.'

In case the given object does not define a docstring, the following is returned: >>> description(type(“Test”, (), {})) ‘no description available’

hydpy.core.objecttools.get_printtarget(file_: TextIO | str | None) Generator[TextIO, None, None][source]

Get a suitable file object reading for writing text useable as the file argument of the standard print() function.

Function get_printtarget() supports three types of arguments. For None, it returns sys.stdout:

>>> from hydpy.core.objecttools import get_printtarget
>>> import sys
>>> with get_printtarget(None) as printtarget:
...     print("printtarget = stdout", file=printtarget)
printtarget = stdout

It passes already opened file objects, flushing but not closing them:

>>> from hydpy import TestIO
>>> with TestIO():
...     with open("testfile1.txt", "w") as testfile1:
...         with get_printtarget(testfile1) as printtarget:
...             print("printtarget = testfile1", file=printtarget, end="")
>>> with TestIO():
...     with open("testfile1.txt", "r") as testfile1:
...         print(testfile1.read())
printtarget = testfile1

It creates a new file and closes it after leaving the with block when receiving a file name:

>>> with TestIO():
...     with get_printtarget("testfile2.txt") as printtarget:
...         print("printtarget = testfile2", file=printtarget, end="")
>>> with TestIO():
...     with open("testfile2.txt", "r") as testfile2:
...         print(testfile2.read())
printtarget = testfile2
hydpy.core.objecttools.apply_black(name: str, *args: object, **kwargs: object) str[source]

Return a string representation of an instance of a class based on the given name, positional arguments and keyword arguments.

apply_black() helps to define __repr__ methods that agree with PEP 8 by using the code formatter black:

>>> from hydpy.core.objecttools import apply_black
>>> print(apply_black("Tester"))
Tester()
>>> print(apply_black("Tester", 1, "test"))
Tester(1, "test")
>>> print(apply_black("Tester", number=1, string="test"))
Tester(number=1, string="test")
>>> print(apply_black("Tester", 1, "test", number=2, string=f"a {10*'very '}long test"))
Tester(
    1,
    "test",
    number=2,
    string="a very very very very very very very very very very long test",
)
hydpy.core.objecttools.value2bool(argument: str, value: str | int) bool[source]

Convert the given string or integer value to a boolean and return it.

>>> from hydpy.core.objecttools import value2bool
>>> value2bool("x", 0), value2bool("x", 1)
(False, True)
>>> for value in ("1", "tRue", "T", "yEs", "y", "oN"):
...     assert value2bool("x", value)
>>> for value in ("0 ", "False", "f", "No", "N", "OfF"):
...     assert not value2bool("x", value)
>>> value2bool("x", "Tr ue")
Traceback (most recent call last):
...
ValueError: The value `Tr ue` given for argument `x` cannot be interpreted as a boolean.
hydpy.core.objecttools.is_equal(xs: float | ndarray[Any, dtype[float64]] | Mapping[str, NestedFloat] | Sequence[NestedFloat], ys: float | ndarray[Any, dtype[float64]] | Mapping[str, NestedFloat] | Sequence[NestedFloat], /) bool[source]

Check if the given nested data objects agree in their structure and values.

is_equal() always considers numpy arrays as unequal to lists and tuples and nan values as equal to other nan values.

>>> from hydpy.core.objecttools import is_equal
>>> from numpy import array, nan, ones

Scalars:

>>> assert not is_equal(1, [1])
>>> assert is_equal(1, 1)
>>> assert not is_equal(1, 2)
>>> assert is_equal(nan, nan)
>>> assert not is_equal(1, nan)
>>> assert not is_equal(nan, 2)

Arrays:

>>> assert not is_equal(ones(2), [1, 1])
>>> assert is_equal(ones(2), ones(2))
>>> assert not is_equal(ones(2), ones(3))
>>> assert is_equal(array([1, nan, 3]), array([1, nan, 3]))
>>> assert not is_equal(array([1, nan, 3]), array([1, nan, nan]))

Dictionarie:

>>> assert not is_equal({"a": 1}, 1)
>>> assert is_equal({}, {})
>>> assert not is_equal({"a": 1}, {"b": 1})
>>> assert is_equal({"a": 1}, {"a": 1})

Lists and tuples:

>>> assert not is_equal([1], 1)
>>> assert is_equal([], ())
>>> assert is_equal([1, 2], (1, 2))
>>> assert not is_equal([1, 2], (1, 3))
>>> assert not is_equal([1, 2], (1,))

Combinations:

>>> assert is_equal([1, {"a": [2, ones(3)]}], [1, {"a": [2, ones(3)]}])
>>> assert not is_equal([1, {"a": [2, ones(3)]}], [1, {"a": [2, ones(4)]}])