pyplugin.base

class pyplugin.base.Plugin(plugin, name=<object object>, unload_callable=<function void_args>, bind=False, requires=(), callbacks=(), **kwargs)[source]

Bases: Generic[_R]

Plugins are arbitrary callables. They can declare other plugins as requirements while operating under certain guarantees:

  • A plugin can be loaded (i.e. called) exactly once until it is unloaded.

  • A plugin’s dependencies will be loaded before.

  • A plugin’s loaded dependents will be reloaded after.

  • When a plugin is unloaded, its loaded dependents will be unloaded before.

name

The (relative) name of the plugin

Type:

str

full_name

The fully qualified dot-delimited name of the plugin

Type:

str

load_args

The most-recent positional arguments passed to the plugin when loading

Type:

tuple | None

load_kwargs

The most-recent keyword arguments passed to the plugin when loading

Type:

dict | None

instance

The return-value of the last load. Is plugin.utils.empty if not loaded

Type:

Any | empty

type

The return-value type of the plugin. (default: None)

Type:

type | None

is_class_type

If True, this indicates the return-value is a subclass of type. (default: False)

Type:

bool

infer_type

If type is not given upon initialization, will attempt to infer the type from type annotations of the callable or the type of the return value upon first load. (default: True)

Type:

bool

enforce_type

If True, will error if any load attempt that does not match type. (default: False)

Type:

bool

requirements

The dependencies this plugin requires before loading. Requirements will be passed via keyword argument using the PluginRequirement.dest name.

Type:

dict[str, PluginRequirement]

dependencies

A map from PluginRequirement.dest to the resolved plugin. This map is populated upon loading along with the corresponding dependents list of the required Plugin.

Type:

OrderedDict[str, Plugin]

dependents

A list of Plugins that depend on this Plugin. This list is populated when the dependent Plugin is loaded and the dependent Plugin is guaranteed to have this Plugin in its dependencies map.

Type:

list[Plugin]

callbacks

Functions that will be called in order that modify the loaded plugin instance after loading.

Type:

Iterable[Callable[[_R], _R]]

Parameters:
  • plugin (Callable) – This is the base form where the Plugin class will “wrap” this underlying callable and call this function upon load().

  • name (str | empty) – The name to assign the plugin. If not provided, determined by get_plugin_name()

  • unload_callable (Callable) – A Callable that takes one argument, the instance, and is called when unload() is called.

  • bind (bool) – If True, passes self as the first argument into the load callable and unload callable. (default: False)

  • anonymous (bool) – If True, will not globally register the plugin under its full_name (default: False).

  • type (type | None) – The return type of the underlying callable. (default: None)

  • infer_type (bool) – If type is not given upon initialization, will attempt to infer the type from type annotations of the callable or the type of the return value upon first load. (default: True)

  • enforce_type (bool) – If True, will error if any load attempt that does not match type. (default: False)

  • is_class_type (bool) – If True, this indicates the return-value is a subclass of type. (default: False)

  • requires (PluginLike | PluginRequirement | tuple[PluginLike, str] | Iterable[...]) – Any plugin dependencies to load beforehand.

  • callbacks (Iterable[Callable[[_R], _R]]) – Functions that will be called in order that modify the loaded plugin instance after loading.

add_callback(callback)[source]

Add a callback to plugin which will modify the loaded plugin instance after loading.

Parameters:

callback (Callable[[_R], _R]) – The callback.

add_requirement(requirement, conflict_strategy='error')[source]
Parameters:
  • requirement (PluginLike | str | PluginRequirement | tuple[PluginLike, str]) – The plugin that this plugin depends on and that will be passed upon load().

  • conflict_strategy ("replace" | "keep_existing" | "error") –

    Handles the case where PluginRequirement.dest conflicts with a current requirement:

    • ”replace”: Remove the existing requirement

    • ”keep_existing”: Keep the existing requirement

    • ”error”: Raise PluginRequirementError

Returns:

The formatted PluginRequirement

Return type:

PluginRequirement

Raises:

PluginRequirementError – If there was an issue registering the requirement

copy(dest=None)[source]
Parameters:

dest (str | None) – the new name to give the copy, defaults to the current name

Returns:

An anonymous, non-loaded copy of this plugin.

Return type:

Plugin

property full_name

Returns: str: The fully-qualified name

get_full_name()[source]
Returns:

The fully-qualified name

Return type:

str

get_name()[source]
is_loaded()[source]

This is equivalent to checking that instance is not empty.

Returns:

True if the plugin is currently loaded, false otherwise

Return type:

bool

is_registered(name=None)[source]

Checks if this plugin is registered under ANY name in the plugin registry. Optionally, we can specify an exact name to check for.

Parameters:

name (str) – The name to check for equality under (defaults to any).

Returns:

True if this plugin is registered (optionally, under the specific name), False otherwise.

Return type:

bool

load(*args, conflict_strategy='replace', default_previous_args=True, safe_args=False, **kwargs)[source]

The main method of the Plugin class. This eventually calls the underlying load callable but keeps state of dependencies and dependents before and after, as well as type checking. In order:

  1. Requirements are resolved and used to populate the dependencies map, in addition to populating each dependency’s dependents list. If dynamic requirements are enabled, that will also be handled.

  2. Dependencies are loaded if the argument is not passed.

  3. Check if this plugin is already loaded based on previous load args and resolve the conflict if any.

  4. Call the underlying callable and call any callbacks in order, do type checking if enabled.

  5. Force reload loaded dependents with the new plugin value.

Parameters:
  • args – varargs passed to the load callable.

  • conflict_strategy ("keep_existing", "replace", "force", "error") –

    How to handle the case this Plugin is already loaded:

    • ”keep_existing”: Ignore the load request

    • ”replace”: First unload() before attempting to load

    • ”force”: Like “replace” but also will apply if load_args and load_kwargs match.

    • ”error”: raises PluginLoadError

    (default: “replace”)

  • default_previous_args (bool) – If True, will fill kwargs with defaults from load_kwargs. (default: True)

  • safe_args (bool) – If True, will only pass arguments to the underlying callable if it matches the signature (default: False).

  • kwargs – varkwargs passed to the load callable.

Raises:
property name
replace_with(plugin, unload_callable=None, replace_type=False)[source]

Replaces the underlying callables with the underlying callables of the given plugin. This is useful if you wish to preserve the dependency tree, typing information, and name.

This will automatically unload and then reload the plugin if it is already loaded.

Parameters:
  • plugin (PluginLike) – The plugin to replace the underlying callables with.

  • unload_callable (Callable) – Used if plugin is a non-Plugin Callable.

  • replace_type (bool) – Replace the typing information as well (default: False)

unload(conflict_strategy='ignore')[source]

This calls the underlying unload callable in the following steps:

  1. Check if this plugin is unloaded already and resolve the conflict if any.

  2. Call the underlying unload callable with the previously loaded instance (which is the return value of the load callable).

Parameters:

conflict_strategy ("ignore", "error") –

How to handle the case this Plugin is already unloaded:

  • ”ignore”: Ignore the unload request

  • ”error”: raises PluginAlreadyUnloadedError

(default: “ignore”)

Raises:
class pyplugin.base.PluginRequirement(plugin, dest)[source]

Bases: object

plugin

The plugin dependency, if this is a string, will perform a lookup_plugin() before loading.

Type:

Plugin | str

dest

The keyword name to call Plugin.load() with.

Type:

str

dest
classmethod from_tuple(value)[source]
plugin
pyplugin.base.get_aliases(plugin)[source]
Parameters:

plugin (Plugin) – The plugin to get aliases for

Returns:

A list of names that this plugin is registered to.

Return type:

list[str]

pyplugin.base.get_plugin_name(plugin, name=<object object>)[source]

Finds a name for the given plugin-like object. For a function this is a fully qualified package-module dot-delimited name. Otherwise, takes the override name argument, and finally resorts to the __name__ attribute if defined.

Parameters:
  • plugin (PluginLike) – The plugin-like object to find a name for.

  • name (str | plugin.utils.empty) – The override name to take if given.

Returns:

The resolved name of the plugin

Return type:

str

Raises:

ValueError – If a name could not be resolved

pyplugin.base.get_registered_plugin(name)[source]
Parameters:

name (str) – The name of the plugin

Returns:

The plugin registered with the given name

Return type:

Plugin

Raises:

PluginNotFoundError – If the plugin with the given name is not found

pyplugin.base.get_registered_plugins()[source]
Returns:

A map from plugin name to plugin in the order which they were registered.

Return type:

OrderedDict[str, Plugin]

pyplugin.base.lookup_plugin(name, import_lookup=None)[source]
Parameters:
  • name (str) – The plugin name to lookup

  • import_lookup (bool) – If True and name is not registered, will attempt to import the name and wrap in Plugin.

Returns:

The plugin with the registered name, falling back to an import lookup that wraps

Return type:

Plugin

pyplugin.base.register(plugin, name=None, conflict_strategy='error', transient=False, **kwargs)[source]
Parameters:
  • plugin (PluginLike) – The plugin to register

  • name (str) – The name to register the plugin under if not the plugin’s full name (default: the plugin’s full name)

  • conflict_strategy ("replace" | "keep_existing" | "error") –

    Handle the case that a different plugin is already registered under the same name:

    • ”keep_existing”: Ignore the incoming register request

    • ”replace”: Unregister the existing plugin first (if it’s not loaded).

    • ”error”: raises PluginRegisterError

  • transient (bool) – Calls to register under the same name will behave as if conflict_strategy == “replace”.

Raises:

PluginRegisterError – If there was an error in registering the plugin (e.g. trying to replace an already loaded plugin)

Returns:

The newly registered plugin or the existing plugin if conflict_strategy is “keep_existing”

Return type:

Plugin

pyplugin.base.replace_registered_plugin(name, plugin, **kwargs)[source]

Will replace the registered plugin with the given name in-place with the given plugin.

See replace_with().

Parameters:
  • name (str) – The plugin name to replace

  • plugin (PluginLike) – The plugin to replace the curent plugin with

  • kwargs – See replace_with()

pyplugin.base.unregister(plugin, conflict_strategy='error')[source]
Parameters:
  • plugin (Plugin | str) – The plugin to unregister

  • conflict_strategy ("ignore" | "error") –

    Handle the case that the name is not registered.

    • ”ignore”: Ignore the incoming unregister request

    • ”error”: raises PluginRegisterError

Raises:

PluginRegisterError – If there was an error in unregistering the plugin

Returns:

The unregistered plugin or None

Return type:

Plugin | None