The property_manager module

Useful property variants for Python programming.

Introduction

The property_manager module defines several property variants that implement Python’s descriptor protocol to provide decorators that turn methods into computed properties with several additional features.

Custom property types

Here’s an overview of the predefined property variants and their supported operations:

Variant Assignment Reassignment Deletion Caching
custom_property No No No No
writable_property Yes Yes No No
mutable_property Yes Yes Yes No
required_property Yes Yes No No
key_property Yes No No No
lazy_property No No No Yes
cached_property No No Yes Yes

If you want a different combination of supported options (for example a cached property that supports assignment) this is also possible, please take a look at custom_property.__new__().

The following inheritance diagram shows how the predefined property variants relate to each other:

Inheritance diagram of property_manager.custom_property, property_manager.writable_property, property_manager.mutable_property, property_manager.required_property, property_manager.key_property, property_manager.lazy_property, property_manager.cached_property

The property manager superclass

In addition to these property variants the property_manager module also defines a PropertyManager class which implements several related enhancements:

  • Keyword arguments to the constructor can be used to set writable properties created using any of the property variants defined by the property_manager module.
  • Required properties without an assigned value will cause the constructor to raise an appropriate exception (TypeError).
  • The repr() of PropertyManager objects shows the names and values of all properties. Individual properties can be omitted from the repr() output by setting the repr option to False.

Logging

The property_manager module emits log messages at the custom log level SPAM which is considered more verbose than DEBUG, so if you want these messages to be logged make sure they’re not being ignored based on their level.

Classes

property_manager.SPHINX_ACTIVE = True

True when Sphinx is running, False otherwise.

We detect whether Sphinx is running by checking for the presence of the ‘sphinx’ key in sys.modules. The result determines the default value of USAGE_NOTES_ENABLED.

property_manager.USAGE_NOTES_VARIABLE = 'PROPERTY_MANAGER_USAGE_NOTES'

The name of the environment variable that controls whether usage notes are enabled (a string).

property_manager.USAGE_NOTES_ENABLED = True

True if usage notes are enabled, False otherwise.

This defaults to the environment variable USAGE_NOTES_VARIABLE (coerced using coerce_boolean()) when available, otherwise SPHINX_ACTIVE determines the default value.

Usage notes are only injected when Sphinx is running because of performance. It’s nothing critical of course, but modifying hundreds or thousands of docstrings that no one is going to look at seems rather pointless :-).

property_manager.NOTHING = <object object>

A unique object instance used to detect missing attributes.

property_manager.set_property(obj, name, value)[source]

Set or override the value of a property.

Parameters:
  • obj – The object that owns the property.
  • name – The name of the property (a string).
  • value – The new value for the property.

This function directly modifies the __dict__ of the given object and as such it avoids any interaction with object properties. This is intentional: set_property() is meant to be used by extensions of the property-manager project and by user defined setter methods.

property_manager.clear_property(obj, name)[source]

Clear the assigned or cached value of a property.

Parameters:
  • obj – The object that owns the property.
  • name – The name of the property (a string).

This function directly modifies the __dict__ of the given object and as such it avoids any interaction with object properties. This is intentional: clear_property() is meant to be used by extensions of the property-manager project and by user defined deleter methods.

property_manager.format_property(obj, name)[source]

Format an object property’s dotted name.

Parameters:
  • obj – The object that owns the property.
  • name – The name of the property (a string).
Returns:

The dotted path (a string).

class property_manager.PropertyManager(**kw)[source]

Optional superclass for classes that use the computed properties from this module.

Provides support for required properties, setting of properties in the constructor and generating a useful textual representation of objects with properties.

__init__(**kw)[source]

Initialize a PropertyManager object.

Parameters:kw – Any keyword arguments are passed on to set_properties().
set_properties(**kw)[source]

Set instance properties from keyword arguments.

Parameters:kw – Every keyword argument is used to assign a value to the instance property whose name matches the keyword argument.
Raises:TypeError when a keyword argument doesn’t match a property on the given object.
missing_properties

The names of key and/or required properties that are missing.

This is a list of strings with the names of key and/or required properties that either haven’t been set or are set to None.

required_properties

A sorted list of strings with the names of any required properties.

key_properties

A sorted list of strings with the names of any key properties.

key_values

A tuple of tuples with (name, value) pairs for each name in key_properties.

find_properties(**options)[source]

Find an object’s properties (of a certain type).

Parameters:options – Passed on to have_property() to enable filtering properties by the operations they support.
Returns:A sorted list of strings with the names of properties.
have_property(name, **options)[source]

Check if the object has a property (of a certain type).

Parameters:
  • name – The name of the property (a string).
  • options – Any keyword arguments give the name of an option (one of writable, resettable, cached, required, key, repr) and an expected value (True or False). Filtering on more than one option is supported.
Returns:

True if the object has a property with the expected options enabled/disabled, False otherwise.

clear_cached_properties()[source]

Clear cached properties so that their values are recomputed.

__eq__(other)[source]

Enable equality comparison and hashing for PropertyManager subclasses.

__ne__(other)[source]

Enable non-equality comparison for PropertyManager subclasses.

__lt__(other)[source]

Enable “less than” comparison for PropertyManager subclasses.

__le__(other)[source]

Enable “less than or equal” comparison for PropertyManager subclasses.

__gt__(other)[source]

Enable “greater than” comparison for PropertyManager subclasses.

__ge__(other)[source]

Enable “greater than or equal” comparison for PropertyManager subclasses.

__hash__()[source]

Enable hashing for PropertyManager subclasses.

This method makes it possible to add PropertyManager objects to sets and use them as dictionary keys. The hashes computed by this method are based on the values in key_values.

__repr__()[source]

Render a human friendly string representation of an object with computed properties.

This method generates a user friendly textual representation for objects that use computed properties created using the property_manager module.

If one or more key_properties are defined, their names and values are used to generate a compact object representation. When key_properties is empty __repr__() assumes that all of the object’s properties are idempotent and may be evaluated at any given time without worrying too much about performance (refer to the repr option for an escape hatch).

class property_manager.custom_property(*args, **kw)[source]

Custom property subclass that supports additional features.

The custom_property class implements Python’s descriptor protocol to provide a decorator that turns methods into computed properties with several additional features.

The additional features are controlled by attributes defined on the custom_property class. These attributes (documented below) are intended to be changed by the constructor (__new__()) and/or classes that inherit from custom_property.

cached = False

If this attribute is set to True the property’s value is computed only once and then cached in an object’s __dict__. The next time you access the attribute’s value the cached value is automatically returned. By combining the cached and resettable options you get a cached property whose cached value can be cleared. If the value should never be recomputed then don’t enable the resettable option.

See also:cached_property and lazy_property.
dynamic = False

True when the custom_property subclass was dynamically constructed by __new__(), False otherwise. Used by compose_usage_notes() to decide whether to link to the documentation of the subclass or not (because it’s impossible to link to anonymous classes).

environment_variable = None

If this attribute is set to the name of an environment variable the property’s value will default to the value of the environment variable. If the environment variable isn’t set the property falls back to its computed value.

key = False

If this attribute is True the property’s name is included in the value of key_properties which means that the property’s value becomes part of the “key” that is used to compare, sort and hash PropertyManager objects. There are a few things to be aware of with regards to key properties and their values:

  • The property’s value must be set during object initialization (the same as for required properties) and it cannot be changed after it is initially assigned a value (because allowing this would “compromise” the results of the __hash__() method).
  • The property’s value must be hashable (otherwise it can’t be used by the __hash__() method).
See also:key_property.
repr = True

By default PropertyManager.__repr__() includes the names and values of all properties that aren’t None in repr() output. If you want to omit a certain property you can set repr to False.

Examples of why you would want to do this include property values that contain secrets or are expensive to calculate and data structures with cycles which cause repr() to die a slow and horrible death :-).

required = False

If this attribute is set to True the property requires a value to be set during the initialization of the object that owns the property. For this to work the class that owns the property needs to inherit from PropertyManager.

See also:required_property.

The constructor of PropertyManager will ensure that required properties are set to values that aren’t None. Required properties must be set by providing keyword arguments to the constructor of the class that inherits from PropertyManager. When PropertyManager.__init__() notices that required properties haven’t been set it raises a TypeError similar to the type error raised by Python when required arguments are missing in a function call. Here’s an example:

from property_manager import PropertyManager, required_property, mutable_property

class Example(PropertyManager):

    @required_property
    def important(self):
        "A very important attribute."

    @mutable_property
    def optional(self):
        "A not so important attribute."
        return 13

Let’s construct an instance of the class defined above:

>>> Example()
Traceback (most recent call last):
  File "property_manager/__init__.py", line 107, in __init__
    raise TypeError("%s (%s)" % (msg, concatenate(missing_properties)))
TypeError: missing 1 required argument ('important')

As expected it complains that a required property hasn’t been initialized. Here’s how it’s supposed to work:

>>> Example(important=42)
Example(important=42, optional=13)
resettable = False

If this attribute is set to True the property can be reset to its default or computed value using del and delattr(). This works by removing the assigned or cached value from the object’s __dict__.

See also:mutable_property and cached_property.
usage_notes = True

If this attribute is True inject_usage_notes() is used to inject usage notes into the documentation of the property. You can set this attribute to False to disable inject_usage_notes().

writable = False

If this attribute is set to True the property supports assignment. The assigned value is stored in the __dict__ of the object that owns the property.

See also:writable_property, mutable_property and required_property.

A relevant note about how Python looks up attributes: When an attribute is looked up and exists in an object’s __dict__ Python ignores any property (descriptor) by the same name and immediately returns the value that was found in the object’s __dict__.

static __new__(*args, **options)[source]

Constructor for custom_property subclasses and instances.

To construct a subclass:

Parameters:
Returns:

A dynamically constructed subclass of custom_property with the given options.

To construct an instance:

Parameters:args – The first positional argument is the function that’s called to compute the value of the property.
Returns:A custom_property instance corresponding to the class whose constructor was called.

Here’s an example of how the subclass constructor can be used to dynamically construct custom properties with specific options:

from property_manager import custom_property

class WritableCachedPropertyDemo(object):

    @custom_property(cached=True, writable=True)
    def customized_test_property(self):
        return 42

The example above defines and uses a property whose computed value is cached and which supports assignment of new values. The example could have been made even simpler:

from property_manager import cached_property

class WritableCachedPropertyDemo(object):

    @cached_property(writable=True)
    def customized_test_property(self):
        return 42

Basically you can take any of the custom property classes defined in the property_manager module and call the class with keyword arguments corresponding to the options you’d like to change.

__init__(*args, **kw)[source]

Initialize a custom_property object.

Parameters:
  • args – Any positional arguments are passed on to the initializer of the property class.
  • kw – Any keyword arguments are passed on to the initializer of the property class.

Automatically calls inject_usage_notes() during initialization (only if USAGE_NOTES_ENABLED is True).

ensure_callable(role)[source]

Ensure that a decorated value is in fact callable.

Parameters:role – The value’s role (one of ‘fget’, ‘fset’ or ‘fdel’).
Raises:exceptions.ValueError when the value isn’t callable.
inject_usage_notes()[source]

Inject the property’s semantics into its documentation.

Calls compose_usage_notes() to get a description of the property’s semantics and appends this to the property’s documentation. If the property doesn’t have documentation it will not be added.

compose_usage_notes()[source]

Get a description of the property’s semantics to include in its documentation.

Returns:A list of strings describing the semantics of the custom_property in reStructuredText format with Sphinx directives.
__get__(obj, type=None)[source]

Get the assigned, cached or computed value of the property.

Parameters:
  • obj – The instance that owns the property.
  • type – The class that owns the property.
Returns:

The value of the property.

__set__(obj, value)[source]

Override the computed value of the property.

Parameters:
  • obj – The instance that owns the property.
  • value – The new value for the property.
Raises:

AttributeError if writable is False.

__delete__(obj)[source]

Reset the assigned or cached value of the property.

Parameters:obj – The instance that owns the property.
Raises:AttributeError if resettable is False.

Once the property has been deleted the next read will evaluate the decorated function to compute the value.

class property_manager.writable_property(*args, **kw)[source]

A computed property that supports assignment.

This is a variant of custom_property that has the writable option enabled by default.

class property_manager.required_property(*args, **kw)[source]

A property that requires a value to be set.

This is a variant of writable_property that has the required option enabled by default. Refer to the documentation of the required option for an example.

class property_manager.key_property(*args, **kw)[source]

A property whose value is used for comparison and hashing.

This is a variant of custom_property that has the key and required options enabled by default.

class property_manager.mutable_property(*args, **kw)[source]

A computed property that can be assigned and reset.

This is a variant of writable_property that has the resettable option enabled by default.

class property_manager.lazy_property(*args, **kw)[source]

A computed property whose value is computed once and cached.

This is a variant of custom_property that has the cached option enabled by default.

class property_manager.cached_property(*args, **kw)[source]

A computed property whose value is computed once and cached, but can be reset.

This is a variant of lazy_property that has the resettable option enabled by default.