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 onassignrepr_tuple
andassignrepr_list
.
assignrepr_tuple
“Double Singleton class”, see the documentation onassignrepr_tuple
andassignrepr_list
.
repr_
Modifiesrepr()
for strings and floats, mainly for supporting clean float and path representations that are compatible withdoctest
.
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) masterNode
orElement
instance, if not possible return ?.
elementphrase()
Return the phrase used in exception messages to indicate whichElement
is affected.
nodephrase()
Return the phrase used in exception messages to indicate whichNode
is affected.
devicephrase()
Try to return the phrase used in exception messages to indicate whichElement
or whichNode
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 anValueError
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 standardprint()
function.
decorator()
Functiondecorator()
adds type hints to function decorator of the site-package wrapt without changing its functionality.
excmessage_decorator()
Wrap a function withaugment_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 functionrepr_
.
repr_numbers()
Return comma separated representations of the given numbers using functionrepr_
.
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 functionrepr()
.
repr_list()
Return a list representation of the given values using functionrepr()
.
assignrepr_value()
Return a prefixed string representation of the given value using functionrepr()
.
assignrepr_values()
Return a prefixed, wrapped and properly aligned string representation of the given values using functionrepr()
.
assignrepr_values2()
Return a prefixed and properly aligned string representation of the given 2-dimensional value matrix using functionrepr()
.
assignrepr_tuple2()
Return a prefixed, wrapped and properly aligned tuple string representation of the given 2-dimensional value matrix using functionrepr()
.
assignrepr_list2()
Return a prefixed, wrapped and properly aligned list string representation of the given 2-dimensional value matrix using functionrepr()
.
assignrepr_tuple3()
Return a prefixed, wrapped and properly aligned tuple string representation of the given 3-dimensional value matrix using functionrepr()
.
assignrepr_list3()
Return a prefixed, wrapped and properly aligned list string representation of the given 3-dimensional value matrix using functionrepr()
.
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
orElement
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 whichNode
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 functionaugment_excmessage()
more efficiently. Suppose you would apply functionaugment_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 modulesobjecttools
:>>> @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 functionscopy_()
anddeepcopy_()
:>>> 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 withdoctest
.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 classNode
or use functionflatten_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 modelkinw_williams
relies on functionflatten_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 optionellipsis
:>>> 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. ForNone
, 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)]}])