Wrapping#

class sigmaepsilon.core.wrapping.Wrapper(*args, **kwargs)[source]#

Wrapper base class that makes it easy (and safe) to extend other objects. Based on the provided arguments at initialization, the wrapper either

  1. wraps an existing object at object creation provided as a keyword argument with Wrapper.wrapkey

  2. wraps an existing object at object creation if it is a positional argument and an instance of Wrapper.wraptype

  1. wraps the object Wrapper.wraptype(*args, **kwargs) if Wrapper.wraptype is not None

The attributes and methods of the wrapped instance are all accessible through the wrapper.

Using a wrapper is a good idea if you want to easily extend the functionality provided by a class of an external library without having to worry about shadowing an important method and thus risking to break the behaviour of the wrapped object.

Examples

>>> import numpy as np
>>>
>>> arr = np.eye(3)
>>> wrapper = Wrapper(wrap=arr)

Now the wrapped NumPy array is accessible as wrapper.wrapped.

A wrapper class can be used to extend the behaviour:

>>> MyWrapper(Wrapper):
>>>     wraptype = np.ndarray
>>>
>>>     def invert() -> None:
>>>         self.wrapped = np.linalg.inv(self.wrapped)

With this solution, you don’t have to worry about shadowing an existing implementation of NumPy arrays. Not that NumPy arrays might not have a method called invert at the time of implementing the class MyWrapper, but it might change in the future. You can even check that internally and get notified:

>>> import warnings
>>>
>>> MyWrapper(Wrapper):
>>>     wraptype = np.ndarray
>>>
>>>     def invert() -> None:
>>>         if hasattr(self.wrapped, "invert"):
>>>             warnings.warn("'invert' already exists in the object")
>>>         self.wrapped = np.linalg.inv(self.wrapped)

Then, to wrap a NumPy array, you can do this (since the wraptype attribute is set, the MyWrapper class is going to catch the object to wrap as a positional argument):

>>> wrapper = MyWrapper(arr)
wrap(obj: Any | None = None) Any[source]#

Wraps the provided object and returns the wrapper instance.

property wrapped#

Retruns the wrapped object.

wraps()[source]#

Returns True if the instance wraps something or False if it doesn’t.

sigmaepsilon.core.wrapping.customwrapper(*, wrapkey: str = 'wrap', wraptype: ~typing.Any = <class 'NoneType'>) Wrapper[source]#

A factory function that returns a class decorator turning a class type into a wrapper type, that either

  1. wraps an existing object at object creation provided as a keyword argument with wrapkey

  2. wraps an existing object at object creation if it is a positional argument and an instance of wraptype

  1. wraps the object wraptype(*args, **kwargs)

See also

Wrapper

Examples

Take this example from the Wrapper class:

>>> MyWrapper(Wrapper):
>>>     wraptype = np.ndarray
>>>
>>>     def invert() -> None:
>>>         self.wrapped = np.linalg.inv(self.wrapped)

An equivalent implementation of this is the following:

>>> @customwrapper(wraptype=np.ndarray)
>>> MyWrapper:
>>>     def invert() -> None:
>>>         self.wrapped = np.linalg.inv(self.wrapped)

Notice how the class MyWrapper is not inherited from the Wrapper class.

sigmaepsilon.core.wrapping.wrap(obj: object) Wrapper[source]#

Wraps an object and returns the wrapper.

See also

Wrapper

Example

>>> import numpy as np
>>> wrapper = wrap(np.eye(3))
sigmaepsilon.core.wrapping.wrapper(BaseType: Any) Wrapper[source]#

Simple class decorator that turns a type into a wrapper with default behaviour.

Notes

This is the same as using the customwrapper() with the default values.

Example

>>> @wrapper
>>> MyWrapper:
>>>     def invert() -> None:
>>>         self.wrapped = np.linalg.inv(self.wrapped)