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.emptyif 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.destname.- Type:
dict[str, PluginRequirement]
- dependencies
A map from
PluginRequirement.destto the resolved plugin. This map is populated upon loading along with the correspondingdependentslist 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
dependenciesmap.- 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 whenunload()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.destconflicts with a current requirement:”replace”: Remove the existing requirement
”keep_existing”: Keep the existing requirement
”error”: Raise PluginRequirementError
- Returns:
The formatted PluginRequirement
- Return type:
- 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:
- property full_name
Returns: str: The fully-qualified name
- is_loaded()[source]
This is equivalent to checking that
instanceis notempty.- 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:
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.
Dependencies are loaded if the argument is not passed.
Check if this plugin is already loaded based on previous load args and resolve the conflict if any.
Call the underlying callable and call any callbacks in order, do type checking if enabled.
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_argsandload_kwargsmatch.”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:
PluginPartiallyLoadedError – If this method was called while inside the underlying callable.
PluginTypeError – If
enforce_typeis True, and the returned value from the underlying callable does not matchtype.PluginLoadError – If there was an error in loading dependencies or dependents
- 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
pluginis 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:
Check if this plugin is unloaded already and resolve the conflict if any.
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:
PluginPartiallyLoadedError – If this method was called while inside the underlying callable.
PluginAlreadyUnloadedError – If conflict_strategy is “error” and this plugin is already unloaded.
- 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
- 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:
- 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.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:
- 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