The Plugin System

PyRolL is mainly built on the plugin system pluggy, which is also used in well known projects like pytest and pytask. Many core functionalities are also implemented as plugins. The PyRolL Core project only implements a minimal set of model approaches, look into the various official and unofficial plugins available for more.

Unlike the other mentioned projects, PyRolL has not only one plugin system, but several. Many main classes of PyRolL hold class attributes used to maintain plugins on that class, these are in detail:

Attribute

Description

plugin_manager

A pluggy.PluginManager instance used to maintain the plugins on this class.

hookspec

A wrapper around a pluggy.HookspecMarker instance for defining new hook specifications. Supports only a subset of the original arguments.

hookimpl

A wrapper around a pluggy.HookimplMarker instance for defining new hook implementations.

This is implemented using the pyroll.plugin_host.PluginHost class and the pyroll.plugin_host.PluginHostMeta metaclass.

class PluginHostMeta(name, bases, dct)

Metaclass that provides plugin functionality to a class.

Not for direct uses but through PluginHost base class.

hookimpl: HookimplMarker

A wrapper around a pluggy.HookimplMarker instance for defining new hook implementations.

hookspec: HookspecMarker

A wrapper around a pluggy.HookspecMarker instance for defining new hook specifications. Supports only a subset of the original arguments.

plugin_manager: PluginManager

A pluggy.PluginManager instance used to maintain the plugins on this class.

root_hooks: Set[str]

Set of hooks to call in every solution iteration.

class PluginHost(hook_args: Dict[str, Any])

A base class providing plugin functionality using the PluginHostMeta metaclass.

The get_from_hook() method is also callable through the attribute syntax (. notation), where the key equals the attributes name.

Parameters

hook_args – keyword arguments to pass to hook calls

__getattr__(key: str)

Call a hook through attribute syntax if there is no explicit attribute with that name by use of get_from_hook().

delete_hook_result_attributes()

Deletes the attributes created by get_from_hook() calls, except those present in root_hooks.

get_from_hook(key: str)

Explicitly tries to get a value from a hook specified on this class. Returns and caches the result of the hook call as attribute. Use clear_hook_results() to clear the cache. Hook calls done by this function are not cleared, only those by attribute syntax.

If the plugin manager does not know a hook of name key, the function dispatches to eventual base classes.

Parameters

key (str) – the hook name to call

Raises
  • AttributeError – if the hook call resulted in None

  • AttributeError – if the hook name is not known to this class, nor to base classes

  • ValueError – if the hook call resulted in an infinite value

get_root_hook_results()

Call necessary root hooks of this instance and return an array of their results.

hook_args

Keyword arguments to pass to hook calls.

hook_result_attributes: Set[str]

Set remembering all hooks that were called on this class, used by delete_hook_result_attributes().

The hookspec markers of all classes derived from Unit (RollPass and Transport) and Profile are preconfigured as firstresult. That means, that the first hook implementation, that returns not None is used as only result of the hook call. This offers the possibility of implementing many specialized versions of a hook and fall back to general ones if no special one applies.

Almost every attribute on the mentioned classes can be represented by a hook. This is achieved by overriding __getattr__, so that if no attribute with a desired name is present on an object, the framework searches for a hook of equal name. If there is no such hook, or the hook call results in None, an error is raised. Therefore, it is easy to specify new hooks, just use the hookspec marker on a dummy function and add it to the plugin_manager by use of plugin_manager.add_hookspecs(). It is common in writing plugins for PyRolL to specify hooks for all intermediate and result values on profiles and units you want to calculate, and then to provide at least one general implementation of them. Afterwards you can proceed providing more specialized implementations in the same plugin package, or maybe also in another one if you need more flexibility in loading different implementations.

The classes Reporter and Exporter are also maintaining a plugin system, to allow plugins to contribute their own results to the output. But those hooks are not firstresult per default and specifying new hooks is not as easy as with units and profiles.

Details affecting only the distinct classes are described in their documentation.

For examples on specifying and implementing hooks, please read the pluggy documentation and look into the source code of PyRolL.