Skip to content

Plugins service

consortium.server.services.plugins_service

PluginsService(release_service, paths_service)

get_plugin_from_directory(directory, ignore_enabled_flag=False)

Instantiates a plugin from a root directory without registering it.

Disabled plugins (as indicated by enabled: false in their manifest.json) are not instantiated unless ignore_enabled_flag is True.

Parameters:

Name Type Description Default
directory Path

Path to the directory containing the plugin project files and manifest.json.

required
ignore_enabled_flag bool

When True, bypasses the enabled check in the manifest. Defaults to False.

False

Returns:

Type Description
BasePlugin | None

The instantiated plugin, or None if the plugin is disabled and the

BasePlugin | None

enabled check is not overridden.

Raises:

Type Description
PluginManifestFileNotFoundError

If manifest.json is missing.

InvalidPluginManifestFileJSONError

If manifest.json contains invalid JSON.

InvalidPluginManifestFileSchemaError

If manifest.json does not follow the expected schema.

InvalidPluginPyProjectFileTOMLError

If pyproject.toml exists but is not valid TOML.

InvalidPluginPyProjectFileDependencyError

If pyproject.toml declares a dependency entry that cannot be parsed as a requirement.

ThirdPartyDependencyNotFoundError

If a third-party dependency declared in pyproject.toml is not installed.

IncompatibleThirdPartyDependencyVersionError

If a third-party dependency declared in pyproject.toml is installed but its version does not satisfy the required specifier.

PluginEntryPointModuleNotFoundError

If the entry-point module cannot be found.

PluginSymbolNotFoundError

If the symbol specified in the manifest is not found.

PluginInterfaceError

If the class does not inherit from the expected base class.

IncompatiblePluginFrameworkVersionError

If the plugin is incompatible with the current framework version.

InternalPluginError

If an unhandled exception occurs while loading the plugin.

get_all_plugins_from_directory(directory, ignore_enabled_flag=False)

Recursively scans a directory for plugin root directories and instantiates them.

Disabled plugins (as indicated by enabled: false in their manifest.json) are skipped unless ignore_enabled_flag is True. Plugins that fail to load are collected in the returned error list rather than aborting the scan.

Parameters:

Name Type Description Default
directory Path

The directory to scan for plugin root directories.

required
ignore_enabled_flag bool

When True, bypasses the enabled check in each plugin's manifest. Defaults to False.

False

Returns:

Type Description
list[BasePlugin]

A three-element tuple: (1) a list of successfully instantiated plugins, (2)

list[Path]

a list of paths skipped because the plugin was disabled, and (3) a list of

list[tuple[Path, PluginLoadingError]]

(path, error) tuples for plugins that failed to load.

Raises:

Type Description
PluginDiscoveryFileSystemError

If the recursive filesystem scan of directory fails, for example because a subdirectory is removed mid-scan or cannot be read due to a permissions error. This happens before any individual plugin is loaded, so it is not one of the per-plugin errors collected in the returned error list, it propagates to the caller.

register_plugin(plugin)

Registers an already-instantiated plugin with the service.

Each plugin is uniquely identified by its plugin_id. Registration validates that the plugin's ID and label are unique and that all of its declared component dependencies are satisfied. Registering a plugin does not start it.

Parameters:

Name Type Description Default
plugin BasePlugin

The plugin instance to register.

required

Raises:

Type Description
PluginAlreadyRegisteredError

If a plugin with the same ID is already registered.

DuplicatePluginLabelError

If a plugin with the same label is already registered.

ComponentDependencyNotFoundError

If the plugin declares a dependency on another component that is not registered.

IncompatibleComponentDependencyVersionError

If the plugin declares a dependency on a registered component whose version does not satisfy the required specifier.

register_plugin_from_directory(directory, ignore_enabled_flag=False)

Instantiates and registers a plugin from a root directory.

Disabled plugins are skipped unless ignore_enabled_flag is True. This method registers the plugin but does not start it.

Parameters:

Name Type Description Default
directory Path

Path to the directory containing the plugin project files and manifest.json.

required
ignore_enabled_flag bool

When True, bypasses the enabled check in the manifest. Defaults to False.

False

Returns:

Type Description
BasePlugin | None

The registered plugin, or None if the plugin is disabled and the enabled

BasePlugin | None

check is not overridden.

Raises:

Type Description
PluginManifestFileNotFoundError

If manifest.json is missing.

InvalidPluginManifestFileJSONError

If manifest.json contains invalid JSON.

InvalidPluginManifestFileSchemaError

If manifest.json does not follow the expected schema.

InvalidPluginPyProjectFileTOMLError

If pyproject.toml exists but is not valid TOML.

InvalidPluginPyProjectFileDependencyError

If pyproject.toml declares a dependency entry that cannot be parsed as a requirement.

ThirdPartyDependencyNotFoundError

If a third-party dependency declared in pyproject.toml is not installed.

IncompatibleThirdPartyDependencyVersionError

If a third-party dependency declared in pyproject.toml is installed but its version does not satisfy the required specifier.

PluginEntryPointModuleNotFoundError

If the entry-point module cannot be found.

PluginSymbolNotFoundError

If the symbol specified in the manifest is not found.

PluginInterfaceError

If the class does not inherit from the expected base class.

IncompatiblePluginFrameworkVersionError

If the plugin is incompatible with the current framework version.

InternalPluginError

If an unhandled exception occurs while loading the plugin.

PluginAlreadyRegisteredError

If a plugin with the same ID is already registered.

DuplicatePluginLabelError

If a plugin with the same label is already registered.

ComponentDependencyNotFoundError

If the plugin declares a dependency on another component that is not registered.

IncompatibleComponentDependencyVersionError

If the plugin declares a dependency on a registered component whose version does not satisfy the required specifier.

load_plugin_from_directory(directory, ignore_enabled_flag=False, timeout=5) async

Loads a plugin from a root directory, registering it and starting it if it autostarts.

Disabled plugins are skipped unless ignore_enabled_flag is True. After registration, the plugin is started when its autostart attribute is True.

Parameters:

Name Type Description Default
directory Path

Path to the directory containing the plugin project files and manifest.json.

required
ignore_enabled_flag bool

When True, bypasses the enabled check in the manifest. Defaults to False.

False
timeout int | None

The maximum number of seconds to wait for the plugin to start when it autostarts. When None, waits indefinitely. Defaults to 5.

5

Returns:

Type Description
BasePlugin | None

The loaded plugin, or None if the plugin is disabled and the enabled

BasePlugin | None

check is not overridden.

Raises:

Type Description
PluginManifestFileNotFoundError

If manifest.json is missing.

InvalidPluginManifestFileJSONError

If manifest.json contains invalid JSON.

InvalidPluginManifestFileSchemaError

If manifest.json does not follow the expected schema.

InvalidPluginPyProjectFileTOMLError

If pyproject.toml exists but is not valid TOML.

InvalidPluginPyProjectFileDependencyError

If pyproject.toml declares a dependency entry that cannot be parsed as a requirement.

ThirdPartyDependencyNotFoundError

If a third-party dependency declared in pyproject.toml is not installed.

IncompatibleThirdPartyDependencyVersionError

If a third-party dependency declared in pyproject.toml is installed but its version does not satisfy the required specifier.

PluginEntryPointModuleNotFoundError

If the entry-point module cannot be found.

PluginSymbolNotFoundError

If the symbol specified in the manifest is not found.

PluginInterfaceError

If the class does not inherit from the expected base class.

IncompatiblePluginFrameworkVersionError

If the plugin is incompatible with the current framework version.

InternalPluginError

If an unhandled exception occurs while loading the plugin.

PluginAlreadyRegisteredError

If a plugin with the same ID is already registered.

PluginStartError

If the plugin autostarts but fails to start.

PluginFatalError

If the plugin autostarts and an unhandled exception escapes its on_started hook, leaving the plugin in a fatal state.

unload_plugin_by_plugin_id(plugin_id, timeout=5, force_unload=False) async

Stops (if running) and deregisters a loaded plugin by its ID.

A running plugin is stopped before it is removed from the registry. A plugin that does not stop within timeout is either forcibly cancelled (when force_unload is True) or causes the unload to fail (when force_unload is False).

Parameters:

Name Type Description Default
plugin_id str | UUID

The ID of the plugin to unload.

required
timeout int | None

The maximum number of seconds to wait for the plugin to stop. When None, waits indefinitely. Defaults to 5.

5
force_unload bool

When True, a plugin that fails to stop cleanly within timeout is forcibly cancelled and still unloaded. When False (default), a failure to stop cleanly aborts the unload.

False

Raises:

Type Description
PluginNotFoundError

If no plugin with the given ID is registered.

PluginStopError

If the plugin fails to stop and force_unload is False.

PluginStopTimeoutError

If the plugin does not stop within timeout and force_unload is False.

PluginFatalError

If an unhandled exception escapes the plugin's on_stopped hook and force_unload is False, leaving the plugin in a fatal state.

reload_plugin_by_plugin_id(plugin_id, ignore_enabled_flag=False, load_timeout=5, unload_timeout=5, force_unload=False) async

Unloads a plugin then reloads it from its original root directory.

The plugin is stopped and deregistered, then loaded again from the root directory it was originally loaded from, starting it again if it autostarts. If the plugin is disabled after reload and ignore_enabled_flag is False, the plugin will only be unloaded, not reloaded.

Parameters:

Name Type Description Default
plugin_id str | UUID

The ID of the plugin to reload.

required
ignore_enabled_flag bool

When True, bypasses the enabled check in the manifest during reload. Defaults to False.

False
load_timeout int | None

The maximum number of seconds to wait for the plugin to start when it autostarts on reload. When None, waits indefinitely. Defaults to 5.

5
unload_timeout int | None

The maximum number of seconds to wait for the plugin to stop during unload. When None, waits indefinitely. Defaults to 5.

5
force_unload bool

When True, a plugin that fails to stop cleanly within unload_timeout is forcibly cancelled during the unload step. When False (default), a failure to stop cleanly aborts the reload.

False

Returns:

Type Description
BasePlugin | None

The reloaded plugin, or None if the plugin is disabled and the enabled

BasePlugin | None

check is not overridden.

Raises:

Type Description
PluginNotFoundError

If no plugin with the given ID is registered.

PluginStopError

If the plugin fails to stop during unload and force_unload is False.

PluginStopTimeoutError

If the plugin does not stop within unload_timeout and force_unload is False.

PluginManifestFileNotFoundError

If manifest.json is missing.

InvalidPluginManifestFileJSONError

If manifest.json contains invalid JSON.

InvalidPluginManifestFileSchemaError

If manifest.json does not follow the expected schema.

InvalidPluginPyProjectFileTOMLError

If pyproject.toml exists but is not valid TOML.

InvalidPluginPyProjectFileDependencyError

If pyproject.toml declares a dependency entry that cannot be parsed as a requirement.

ThirdPartyDependencyNotFoundError

If a third-party dependency declared in pyproject.toml is not installed.

IncompatibleThirdPartyDependencyVersionError

If a third-party dependency declared in pyproject.toml is installed but its version does not satisfy the required specifier.

PluginEntryPointModuleNotFoundError

If the entry-point module cannot be found.

PluginSymbolNotFoundError

If the symbol specified in the manifest is not found.

PluginInterfaceError

If the class does not inherit from the expected base class.

IncompatiblePluginFrameworkVersionError

If the plugin is incompatible with the current framework version.

InternalPluginError

If an unhandled exception occurs while loading the plugin.

PluginAlreadyRegisteredError

If a plugin with the same ID is already registered when it is reloaded.

PluginStartError

If the reloaded plugin autostarts but fails to start.

PluginFatalError

If an unhandled exception escapes the plugin's on_stopped hook during unload and force_unload is False, or escapes its on_started hook when the reloaded plugin autostarts, leaving the plugin in a fatal state.

load_framework_plugins(ignore_enabled_flag=False) async

Discovers and loads all plugins from the framework's plugins directory.

Every plugin root directory under the framework plugins directory is discovered, resolved into a dependency-respecting load order, then registered and (when the plugin has autostart set) started. Disabled plugins are skipped unless ignore_enabled_flag is set. Discovery errors, unresolved dependencies, circular dependencies, and per-plugin load failures are logged rather than raised so that a single bad plugin does not abort loading the rest.

Parameters:

Name Type Description Default
ignore_enabled_flag bool

When True, plugins are loaded even if they are marked as disabled. When False (default), disabled plugins are skipped.

False

Raises:

Type Description
PluginDiscoveryFileSystemError

If the initial recursive scan of the framework plugins directory fails at the filesystem level. This happens before any individual plugin is loaded, so unlike per-plugin load failures it is not logged and swallowed, it propagates to the caller.

unload_framework_plugins(force_unload=False, timeout=5) async

Unloads every loaded plugin that lives under the framework plugins directory.

Each matching plugin is unloaded concurrently. Failures to unload individual plugins are logged rather than raised so that one failing plugin does not prevent the others from being unloaded.

Parameters:

Name Type Description Default
force_unload bool

When True, plugins are unloaded even if they do not stop cleanly within timeout. When False (default), an unclean stop causes that plugin's unload to fail.

False
timeout None | int

The number of seconds to wait for each plugin to stop before its unload is considered to have timed out. When None, waits indefinitely.

5

reload_framework_plugins(force_reload=False, timeout=5, ignore_enabled_flag=False) async

Unloads all currently loaded plugins and reloads them from disk.

Every currently loaded plugin is unloaded concurrently, then the framework plugins directory is rescanned and any plugin that is not already loaded (for example one that failed to unload) is loaded again. Per-plugin unload and load failures are logged rather than raised so that one failing plugin does not prevent the others from being reloaded.

Parameters:

Name Type Description Default
force_reload bool

When True, plugins are unloaded even if they do not stop cleanly within timeout before being loaded again. When False (default), an unclean stop causes that plugin's unload to fail.

False
timeout None | int

The number of seconds to wait for each plugin to stop before its unload is considered to have timed out. When None, waits indefinitely.

5
ignore_enabled_flag bool

When True, plugins are loaded even if they are marked as disabled. When False (default), disabled plugins are skipped when reloading.

False

Raises:

Type Description
PluginDiscoveryFileSystemError

If the recursive scan of the framework plugins directory (used to find plugins that are not already loaded, for example one that failed to unload) fails at the filesystem level. This happens after plugins are unloaded but before any are loaded again, so unlike per-plugin unload and load failures it is not logged and swallowed, it propagates to the caller.

start_plugin_by_plugin_id(plugin_id, blocking=False) async

Starts a loaded plugin by its ID.

Parameters:

Name Type Description Default
plugin_id str | UUID

The ID of the plugin to start.

required
blocking bool

When True, blocks until the plugin has finished starting. When False (default), returns immediately after starting the plugin.

False

Raises:

Type Description
PluginNotFoundError

If no plugin with the given ID is registered.

PluginAlreadyRunningError

If the plugin is already running.

PluginStartError

If the plugin fails to start.

PluginFatalError

If an unhandled exception escapes the plugin's on_started hook, leaving the plugin in a fatal state.

stop_plugin_by_plugin_id(plugin_id, blocking=False) async

Stops a loaded plugin by its ID.

Parameters:

Name Type Description Default
plugin_id str | UUID

The ID of the plugin to stop.

required
blocking bool

When True, blocks until the plugin has finished stopping. When False (default), returns immediately after stopping the plugin.

False

Raises:

Type Description
PluginNotFoundError

If no plugin with the given ID is registered.

PluginNotRunningError

If the plugin is not running.

PluginStopError

If the plugin fails to stop.

PluginFatalError

If an unhandled exception escapes the plugin's on_stopped hook, leaving the plugin in a fatal state.

restart_plugin_by_plugin_id(plugin_id, blocking=False) async

Restarts a loaded plugin by its ID, stopping it and then starting it again.

When blocking is False, the stop-then-start sequence runs in a background task, so any error raised while stopping or starting the plugin is not propagated to the caller.

Parameters:

Name Type Description Default
plugin_id str | UUID

The ID of the plugin to restart.

required
blocking bool

When True, blocks until the plugin has stopped and started again. When False (default), schedules the restart in the background and returns immediately.

False

Raises:

Type Description
PluginNotFoundError

If no plugin with the given ID is registered.

PluginNotRunningError

If blocking is True and the plugin is not running when the restart attempts to stop it.

PluginStopError

If blocking is True and the plugin fails to stop.

PluginAlreadyRunningError

If blocking is True and the plugin is already running when the restart attempts to start it.

PluginStartError

If blocking is True and the plugin fails to start.

PluginFatalError

If blocking is True and an unhandled exception escapes the plugin's on_stopped or on_started hook, leaving the plugin in a fatal state.

cancel_plugin_by_plugin_id(plugin_id, blocking=False) async

Cancels a loaded plugin by its ID.

Parameters:

Name Type Description Default
plugin_id str | UUID

The ID of the plugin to cancel.

required
blocking bool

When True, blocks until the plugin has been cancelled. When False (default), returns immediately after cancelling the plugin.

False

Raises:

Type Description
PluginNotFoundError

If no plugin with the given ID is registered.

PluginNotRunningError

If the plugin is not running.

PluginFatalError

If an unhandled exception escapes the plugin's on_cancelled hook, leaving the plugin in a fatal state.

get_plugin_by_plugin_id(plugin_id)

Returns a loaded plugin by its ID.

Parameters:

Name Type Description Default
plugin_id str | UUID

The ID of the plugin to retrieve.

required

Returns:

Type Description
BasePlugin

The requested plugin.

Raises:

Type Description
PluginNotFoundError

If no plugin with the given ID is registered.

get_plugins_by_label(label)

Returns all loaded plugins with the given label.

Parameters:

Name Type Description Default
label str

The label to filter by.

required

Returns:

Type Description
list[BasePlugin]

All loaded plugins whose label matches. Empty if none match.

get_all_plugins()

Returns a list of all plugins loaded in the service.

Returns:

Type Description
list[BasePlugin]

A list of all plugins loaded in the service.