propertytools

This module implements property like classes with similar or additional behaviour.

Module propertytools implements the following members:


class hydpy.core.propertytools.BaseDescriptor[source]

Bases: object

Base class for defining descriptors.

objtype: type[Any]
module: ModuleType | None
name: str
set_doc(doc: str | None) None[source]

Assign the given docstring to the property instance and, if possible, to the __test__ dictionary of the module of its owner class.

class hydpy.core.propertytools.FGet(*args, **kwargs)[source]

Bases: Protocol[T_co]

Callback protocol for getter functions.

class hydpy.core.propertytools.FSet(*args, **kwargs)[source]

Bases: Protocol[T_contra]

Callback protocol for setter functions.

class hydpy.core.propertytools.FDel(*args, **kwargs)[source]

Bases: Protocol

Callback protocol for deleter functions.

class hydpy.core.propertytools.BaseProperty[source]

Bases: Generic[T_contra, T_co], BaseDescriptor

Abstract base class for deriving classes similar to property.

BaseProperty provides the abstract methods call_fget(), call_fset(), and call_fdel(), which are the appropriate places to add custom functionalities (e.g. caching). See subclass Property for an example, which mimics the behaviour of the built-in property function.

BaseProperty property uses dummy getter, setter and deleter functions to indicate than at an actual getter, setter or deleter function is missing. In case they are called due to the wrong implementation of a BaseProperty subclass, they raise a RuntimeError:

>>> from hydpy.core.propertytools import BaseProperty
>>> BaseProperty._fgetdummy(None)
Traceback (most recent call last):
...
RuntimeError
>>> BaseProperty._fsetdummy(None, None)
Traceback (most recent call last):
...
RuntimeError
>>> BaseProperty._fdeldummy(None)
Traceback (most recent call last):
...
RuntimeError
fget: FGet[T_co]
fset: FSet[T_contra]
fdel: FDel
abstract call_fget(obj: Any) T_co[source]

Method for implementing unique getter functionalities.

abstract call_fset(obj: Any, value: T_contra) None[source]

Method for implementing unique setter functionalities.

abstract call_fdel(obj: Any) None[source]

Method for implementing unique deleter functionalities.

class hydpy.core.propertytools.Property(fget: ~hydpy.core.propertytools.FGet[~hydpy.core.typingtools.T_co] = <function BaseProperty._fgetdummy>, fset: ~hydpy.core.propertytools.FSet[~hydpy.core.typingtools.T_contra] = <function BaseProperty._fsetdummy>, fdel: ~hydpy.core.propertytools.FDel = <function BaseProperty._fdeldummy>)[source]

Bases: BaseProperty[T_contra, T_co]

Class Property mimics the behaviour of the built-in function property.

The only advantage of Property over property is that it allows defining different input and output types statically. If the input and output types are identical, prefer property, which is probably faster.

The following test class implements its attribute x by defining all three “property methods” (getter/fget, setter/fset, deleter/fdel), but its attribute y by defining none of them:

>>> from hydpy.core.propertytools import Property
>>> class Test:
...
...     def __init__(self):
...         self._x = None
...         self._y = None
...
...     @Property
...     def x(self):
...         return self._x
...     @x.setter
...     def x(self, value):
...         self._x = value
...     @x.deleter
...     def x(self):
...         self._x = None
...
...     y = Property()

After initialising a test object, you can use its attribute x as expected:

>>> test = Test()
>>> test.x
>>> test.x = 2
>>> test.x
2
>>> del test.x
>>> test.x

When trying to invoke attribute y, you get the following error messages:

>>> test.y
Traceback (most recent call last):
...
AttributeError: Attribute `y` of object `test` is not gettable.
>>> test.y = 1
Traceback (most recent call last):
...
AttributeError: Attribute `y` of object `test` is not settable.
>>> del test.y
Traceback (most recent call last):
...
AttributeError: Attribute `y` of object `test` is not deletable.
call_fget(obj: Any) T_co[source]

Call fget without additional functionalities.

call_fset(obj: Any, value: T_contra) None[source]

Call fset without additional functionalities.

call_fdel(obj: Any) None[source]

Call fdel without additional functionalities.

getter(fget: FGet[T_co]) Property[T_contra, T_co][source]

Add the given getter function and its docstring to the property and return it.

setter(fset: FSet[T_contra]) Property[T_contra, T_co][source]

Add the given setter function to the property and return it.

deleter(fdel: FDel) Property[T_contra, T_co][source]

Add the given deleter function to the property and return it.

class hydpy.core.propertytools.ProtectedProperty(fget: ~hydpy.core.propertytools.FGet[~hydpy.core.typingtools.T_co] = <function BaseProperty._fgetdummy>, fset: ~hydpy.core.propertytools.FSet[~hydpy.core.typingtools.T_contra] = <function BaseProperty._fsetdummy>, fdel: ~hydpy.core.propertytools.FDel = <function BaseProperty._fdeldummy>)[source]

Bases: BaseProperty[T_contra, T_co]

A property-like class which prevents getting an attribute before setting it.

Some attributes need preparations before being accessible. Consider the case where a property of a Python class (being part of the API) links to an attribute of a Cython extension class (not part of the API). If the Cython attribute is, for example, a vector requiring memory allocation, trying to query this vector before it has been initialised results in a program crash. Using ProtectedProperty is a means to prevent such problems.

The following class Test defines most simple getter, setter, and deleter functions for its only property x:

>>> from hydpy.core.propertytools import ProtectedProperty
>>> class Test:
...
...     def __init__(self):
...         self._x = None
...
...     x = ProtectedProperty()
...     @x.getter
...     def x(self):
...         "Test"
...         return self._x
...     @x.setter
...     def x(self, value):
...         self._x = value
...     @x.deleter
...     def x(self):
...         self._x = None

Trying to query x directly after initialising a Test object results in an AttributeNotReady error:

>>> test = Test()
>>> test.x
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Attribute `x` of object `test` has not been prepared so far.

After setting a value, you can query this value as expected:

>>> test.x = 1
>>> test.x
1

After deleting the value, the protection mechanism applies again:

>>> del test.x
>>> test.x
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Attribute `x` of object `test` has not been prepared so far.
call_fget(obj: Any) T_co[source]

When ready, call fget; otherwise, raise an AttributeNotReady exception.

call_fset(obj: Any, value: T_contra) None[source]

Call fset and mark the attribute as ready.

call_fdel(obj: Any) None[source]

Call fdel and mark the attribute as not ready.

isready(obj: Any) bool[source]

Return True or False to indicate if the protected property is ready for the given object. If the object is unknown, isready() returns False.

getter(fget: FGet[T_co]) ProtectedProperty[T_contra, T_co][source]

Add the given getter function and its docstring to the property and return it.

setter(fset: FSet[T_contra]) ProtectedProperty[T_contra, T_co][source]

Add the given setter function to the property and return it.

deleter(fdel: FDel) ProtectedProperty[T_contra, T_co][source]

Add the given deleter function to the property and return it.

hydpy.core.propertytools.ProtectedPropertyStr

ProtectedProperty for handling str objects.

alias of ProtectedProperty[str, str]

class hydpy.core.propertytools.ProtectedProperties(*properties: ProtectedProperty[Any, Any])[source]

Bases: object

Iterable for ProtectedProperty objects.

You can collect an arbitrary number of ProtectedProperty objects within a ProtectedProperties object. Its allready() method allows checking the status of all properties at ones:

>>> from hydpy.core import propertytools as pt
>>> class Test:
...
...     @pt.ProtectedProperty
...     def x(self):
...         return "this is x"
...     @x.setter
...     def x(self, value):
...         pass
...
...     @pt.ProtectedProperty
...     def z(self):
...         return "this is z"
...     @z.setter
...     def z(self, value):
...         pass
...
...     protectedproperties = pt.ProtectedProperties(x, z)
>>> test1 = Test()
>>> test1.x = None
>>> test2 = Test()
>>> test2.x = None
>>> test2.z = None
>>> Test.protectedproperties.allready(test1)
False
>>> Test.protectedproperties.allready(test2)
True
allready(obj: Any) bool[source]

Return True or False to indicate whether all protected properties are ready or not.

class hydpy.core.propertytools.DependentProperty(protected: ~hydpy.core.propertytools.ProtectedProperties, fget: ~hydpy.core.propertytools.FGet[~hydpy.core.typingtools.T_co] = <function BaseProperty._fgetdummy>, fset: ~hydpy.core.propertytools.FSet[~hydpy.core.typingtools.T_contra] = <function BaseProperty._fsetdummy>, fdel: ~hydpy.core.propertytools.FDel = <function BaseProperty._fdeldummy>)[source]

Bases: BaseProperty[T_contra, T_co]

property-like class which prevents accessing a dependent attribute before preparing certain other attributes.

Please read the documentation on class ProtectedProperty, from which we take the following example. x is a simple ProtectedProperty again, but time we add the DependentProperty y:

>>> from hydpy.core import propertytools as pt
>>> class Test:
...
...     def __init__(self):
...         self._x = None
...         self._y = None
...
...     @pt.ProtectedProperty
...     def x(self):
...         return self._x
...     @x.setter
...     def x(self, value):
...         self._x = value
...     @x.deleter
...     def x(self):
...         self._x = None
...
...     y = pt.DependentProperty(protected=(x,))
...
...     @y.getter
...     def y(self):
...         return self._y
...     @y.setter
...     def y(self, value):
...         self._y = value
...     @y.deleter
...     def y(self):
...         self._y = None

Initially, due to x not being prepared, there is no way to get, set, or delete attribute y:

>>> test = Test()
>>> test.y
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Attribute `y` of object `test` is not usable so far.  At least, you have to prepare attribute `x` first.
>>> test.y = 1
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Attribute `y` of object `test` is not usable so far.  At least, you have to prepare attribute `x` first.
>>> del test.y
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: Attribute `y` of object `test` is not usable so far.  At least, you have to prepare attribute `x` first.

After assigning a value to x, y behaves like a common property:

>>> test.x = "anything"
>>> test.y = 1
>>> test.y
1
>>> del test.y
>>> test.y
protected: ProtectedProperties
call_fget(obj: Any) T_co[source]

Call fget when all required attributes are ready; otherwise, raise an AttributeNotReady error.

call_fset(obj: Any, value: T_contra) None[source]

Call fset when all required attributes are ready; otherwise, raise an AttributeNotReady error.

call_fdel(obj: Any) None[source]

Call fdel when all required attributes are ready; otherwise, raise an AttributeNotReady error.

getter(fget: FGet[T_co]) DependentProperty[T_contra, T_co][source]

Add the given getter function and its docstring to the property and return it.

setter(fset: FSet[T_contra]) DependentProperty[T_contra, T_co][source]

Add the given setter function to the property and return it.

deleter(fdel: FDel) DependentProperty[T_contra, T_co][source]

Add the given deleter function to the property and return it.

class hydpy.core.propertytools.DefaultProperty(fget: ~hydpy.core.propertytools.FGet[~hydpy.core.typingtools.T_co] = <function BaseProperty._fgetdummy>)[source]

Bases: BaseProperty[T_contra, T_co]

property-like class which uses the getter function to return a default value unless a custom value is available.

In the following example, the default value of property x is one:

>>> from hydpy.core.propertytools import DefaultProperty
>>> class Test:
...
...     @DefaultProperty
...     def x(self):
...         "Default property x."
...         return 1

Initially, property x returns the default value defined by its getter function:

>>> test = Test()
>>> test.x
1

Assigned custom values override such default values:

>>> test.x = 3
>>> test.x
3

After removing the custom value, it is again up to the getter function to return the default value:

>>> del test.x
>>> test.x
1

Trying to delete a not existing custom value does not harm:

>>> del test.x

The documentation string of the getter functions serves as the documentation string of the default property:

>>> Test.x.__doc__
'Default property x.'
call_fget(obj: Any) T_co[source]

If available, return the predefined custom value; otherwise, return the value defined by the getter function.

call_fset(obj: Any, value: T_contra) None[source]

Store the given custom value.

call_fdel(obj: Any) None[source]

Remove the predefined custom value.

hydpy.core.propertytools.DefaultPropertyBool

DefaultProperty for handling bool objects.

alias of DefaultProperty[bool, bool]

hydpy.core.propertytools.DefaultPropertyStr

DefaultProperty for handling str objects.

alias of DefaultProperty[str, str]

hydpy.core.propertytools.DefaultPropertySeriesFileType

DefaultProperty for handling SeriesFileType literals.

alias of DefaultProperty[Literal[‘npy’, ‘asc’, ‘nc’], Literal[‘npy’, ‘asc’, ‘nc’]]

hydpy.core.propertytools.DefaultPropertySeriesAggregationType

DefaultProperty for handling SeriesAggregationType literals.

alias of DefaultProperty[Literal[‘none’, ‘mean’], Literal[‘none’, ‘mean’]]