API: Plugins Package

The plugins package exposes the stable registry surface used by declarative config builders and plugin catalog UIs.

PluginCategory

Bases: StrEnum

Canonical category identifiers for all pluggable pipeline components.

Design rationale

Replacing raw strings with a StrEnum eliminates typo-induced KeyErrors at plugin registration and lookup time, provides IDE autocompletion, and makes mypy able to catch category mismatches statically.

StrEnum (Python 3.11+) means each member compares equal to its string value, so existing code that uses the string literal (e.g. "analyzer") continues to work without modification during migration.

PluginDefinition dataclass

Immutable descriptor for a registered plugin.

The descriptor is intentionally serializable except for factory. as_dict() exposes factory_path instead of the callable so diagnostics and UIs can render plugin catalogs without leaking implementation objects.

Attributes:

Name Type Description
category str

Canonical plugin category.

name str

Canonical plugin name used in configs.

factory Callable[..., Any]

Callable used by PluginRegistry.create.

description str

Human-readable summary for docs and UI catalogs.

version str

Plugin implementation version. This is independent from pipeline config schema versioning.

aliases tuple[str, ...]

Backwards-compatible names accepted by lookup and config builders.

metadata Mapping[str, Any]

Immutable mapping for UI tags, ownership, expected inputs, hardware requirements, or adapter-specific catalog data.

factory_path property

factory_path: str

Return a stable dotted path for diagnostics and exported descriptors.

as_dict

as_dict() -> dict[str, Any]

Return a JSON-serializable descriptor without exposing the factory object.

PluginRegistry

Lightweight, centralised registry for pluggable pipeline components.

Design rationale

The registry is the single source of truth for available components. It decouples builders (which only know category + name) from concrete implementations (which only need to call register() once).

Thread safety

Registration usually happens at startup, but the registry still protects reads and writes with a re-entrant lock. Snapshots returned by public inspection methods are immutable.

register

register(
    category: str | PluginCategory,
    name: str,
    factory: Callable[..., Any],
    description: str = "",
    *,
    version: str = _DEFAULT_PLUGIN_VERSION,
    aliases: Iterable[str] = (),
    metadata: Mapping[str, Any] | None = None,
) -> PluginDefinition

Register a new plugin.

Parameters:

Name Type Description Default
category str | PluginCategory

Public plugin category, usually a PluginCategory member.

required
name str

Stable canonical name used in declarative configs.

required
factory Callable[..., Any]

Callable used to instantiate the component.

required
description str

Optional human-readable description for diagnostics and catalogs.

''
version str

Plugin implementation version.

_DEFAULT_PLUGIN_VERSION
aliases Iterable[str]

Alternative lookup names for backwards compatibility.

()
metadata Mapping[str, Any] | None

Optional immutable metadata exposed through describe() and snapshot().

None

Returns:

Type Description
PluginDefinition

Immutable descriptor stored by the registry.

Raises:

Type Description
InvalidPluginRegistrationError

If category/name/aliases/factory/version are malformed.

DuplicatePluginRegistrationError

If a plugin with the same (category, name) pair is already registered.

register_definition

register_definition(
    definition: PluginDefinition,
) -> PluginDefinition

Register an already-built plugin definition.

This method applies the same validation and duplicate checks as register(); it does not bypass registry invariants.

get

get(
    category: str | PluginCategory, name: str
) -> PluginDefinition

Return the plugin definition for a category and name or alias.

Raises:

Type Description
KeyError

If the category/name pair cannot be resolved. Builders wrap this into PluginResolutionError so config errors include a path.

create

create(
    category: str | PluginCategory,
    name: str,
    *args,
    **kwargs,
) -> Any

Instantiate the plugin identified by category and name or alias.

Side Effects

Calls the registered factory with the provided positional and keyword arguments. Factory exceptions are intentionally not swallowed here so builders can attach configuration context.

list

list(
    category: str | PluginCategory | None = None,
) -> list[PluginDefinition]

Return canonical plugin definitions, optionally filtered by category.

contains

contains(category: str | PluginCategory, name: str) -> bool

Return whether a plugin name or alias is registered in a category.

available_names

available_names(
    category: str | PluginCategory,
    *,
    include_aliases: bool = False,
) -> tuple[str, ...]

Return registered names for a category.

Names are sorted to keep diagnostics, docs, and UI catalogs deterministic.

categories

categories() -> tuple[str, ...]

Return categories that currently contain at least one plugin.

snapshot

snapshot() -> Mapping[str, Mapping[str, PluginDefinition]]

Return an immutable category/name snapshot.

The snapshot contains canonical plugin definitions only; aliases remain lookup concerns and are visible through each definition.

describe

describe(
    category: str | PluginCategory | None = None,
) -> list[dict[str, Any]]

Return JSON-serializable plugin descriptors for diagnostics and UIs.

create_builtin_registry

create_builtin_registry() -> PluginRegistry

Register the built-in OpenCV and Matplotlib components and return the registry.

This factory is the canonical entry-point for production use. Custom plugins can be added to the returned registry before passing it to a builder.

Example

registry = create_builtin_registry() registry.register(PluginCategory.ANALYZER, "my_analyzer", MyAnalyzer) builder = ConfigPipelineBuilder(registry)