Skip to content

Plugin API Reference

The plugin system is the current extension surface for the DITA Package Processor.

These modules define the plugin contract, loading model, validation rules, registry behavior, and the built-in CorePlugin.

dita_package_processor.plugins.protocol

Plugin protocol for the DITA Package Processor.

A plugin is a vertical slice that contributes:

patterns    → discovery signals that classify artifacts
emit_actions → action dicts emitted during planning, keyed to those patterns
handlers    → execution handlers that carry out those actions
Design rules
  • One plugin = one Python installable package.
  • Registration happens automatically via Python entry points; no main code changes.
  • Conflict (duplicate pattern ID or duplicate action_type) is a startup error.
  • CorePlugin is always loaded first and serves as the reference implementation.

Entry point group: dita_package_processor.plugins

A third-party plugin declares itself in pyproject.toml::

[project.entry-points."dita_package_processor.plugins"]
my_plugin = "my_package:plugin"          # module-level instance
# or
my_plugin = "my_package:MyPlugin"        # class (will be instantiated)

DitaPlugin

Bases: ABC

Abstract base class for all DITA Package Processor plugins.

Subclass this and implement the abstract properties. All other methods have safe empty defaults.

Minimal viable plugin::

class MyPlugin(DitaPlugin):
    @property
    def name(self) -> str:
        return "acme.my_plugin"

    @property
    def version(self) -> str:
        return "1.0.0"

A plugin that adds a new detection pattern and handler::

class GlossaryPlugin(DitaPlugin):
    @property
    def name(self) -> str:
        return "acme.glossary"

    @property
    def version(self) -> str:
        return "1.0.0"

    def patterns(self) -> List[Pattern]:
        return load_normalized_patterns_from_yaml(
            Path(__file__).parent / "patterns.yaml"
        )

    def handlers(self) -> List[Type[ExecutionHandler]]:
        return [MyGlossaryHandler]

    def emit_actions(self, artifact, evidence, context):
        roles = {ev.get("asserted_role") for ev in evidence}
        if artifact.artifact_type == "map" and "glossary" in roles:
            return [{
                "type": "copy_map",
                "target": f"target/glossary/{Path(artifact.path).name}",
                "parameters": {
                    "source_path": artifact.path,
                    "target_path": f"target/glossary/{Path(artifact.path).name}",
                },
                "reason": "Glossary map routing",
                "derived_from_evidence": [
                    ev.get("pattern_id", "") for ev in evidence
                ],
            }]
        return []
name abstractmethod property

Unique plugin identifier.

Use reverse-domain notation to avoid conflicts: "com.example.my_plugin"

version abstractmethod property

Plugin version string (e.g. "1.0.0").

emit_actions(artifact, evidence, context)

Emit action dicts for a single artifact during planning.

Called once per artifact, for every loaded plugin, in load order. Return an empty list if this plugin does not handle the artifact.

Each returned dict must conform to the plan action contract::

{
    "type": str,          # One of the registered action types
    "target": str,        # Package-relative target path
    "parameters": dict,   # Action-specific parameters
    "reason": str,        # Human-readable justification
    "derived_from_evidence": List[str],  # Pattern IDs from evidence
}

Do NOT include an "id" key — the planner assigns IDs.

Parameters

artifact: The artifact being planned. Carries path, artifact_type, classification, and metadata (which includes evidence). evidence: List of serialized Evidence dicts for this artifact. Each dict has: pattern_id, asserted_role, confidence, rationale. context: Full :class:~dita_package_processor.planning.contracts.planning_input.PlanningInput for the current run — useful for relationship queries.

Returns:

Type Description
'List[Dict[str, Any]]'

List of action template dicts (no id field).

handlers()

Execution handler classes this plugin contributes.

Each class must: - inherit from :class:~dita_package_processor.execution.registry.ExecutionHandler - define a unique action_type class attribute

The action_type must be globally unique across all loaded plugins.

Returns:

Type Description
'List[Type[ExecutionHandler]]'

List of handler classes (not instances).

patterns()

Discovery patterns this plugin contributes.

Patterns are evaluated against every artifact during discovery. Each matching pattern emits an Evidence record that is forwarded to :meth:emit_actions via the evidence parameter.

Pattern IDs must be globally unique across all loaded plugins.

Returns:

Type Description
'List[Pattern]'

List of :class:~dita_package_processor.discovery.patterns.Pattern instances.

dita_package_processor.plugins.validator

Plugin validation.

Validates a DitaPlugin instance for structural correctness before it is admitted into the plugin registry.

Validation is intentionally strict: - Missing or empty name/version → error - Handler without action_type → error - Pattern without required fields → error - Duplicate IDs within a single plugin → error

Cross-plugin conflicts (duplicate pattern IDs, duplicate action_types) are detected later in PluginRegistry when all plugins are aggregated.

PluginValidationError

Bases: ValueError

Raised when a plugin fails structural validation.

validate_plugin(plugin)

Validate a plugin instance.

Parameters:

Name Type Description Default
plugin 'DitaPlugin'

Plugin instance to validate.

required

Returns:

Type Description
'DitaPlugin'

The same plugin instance (for chaining).

Raises:

Type Description
PluginValidationError

If any validation check fails.

dita_package_processor.plugins.loader

Plugin loader.

Discovers and loads plugins via Python entry points.

Load order
  1. CorePlugin — always first; provides the built-in stack.
  2. Third-party plugins — sorted alphabetically by entry point name.

Each plugin is validated before being admitted. A bad plugin causes a loud startup failure (no silent skips in production; logging for debug).

Entry point group: dita_package_processor.plugins

Third-party pyproject.toml::

[project.entry-points."dita_package_processor.plugins"]
my_plugin = "my_package:plugin"       # module-level DitaPlugin instance
# or
my_plugin = "my_package:MyPlugin"     # DitaPlugin subclass (instantiated here)

load_plugins()

Build the ordered plugin list.

Returns:

Type Description
List[DitaPlugin]

Validated plugins in load order (CorePlugin first).

Raises:

Type Description
Exception

If any plugin fails validation or cannot be imported.

dita_package_processor.plugins.registry

Plugin registry.

Aggregates all loaded plugins and provides unified access to:

  • patterns (for discovery)
  • handlers (for execution bootstrap)
  • action emission (for planning)

The registry is a lazy singleton: it is populated on the first call to get_plugin_registry() by invoking the plugin loader.

Cross-plugin conflict rules
  • Duplicate pattern ID → startup error
  • Duplicate action_type → startup error
  • CorePlugin always loads first; third-party plugins sort alphabetically.

PluginRegistry

Registry holding all loaded and validated plugins.

Do not instantiate directly. Use :func:get_plugin_registry.

all_handlers()

Aggregate handler classes from all plugins.

Handler action_types must be unique (wildcards "*" are allowed from exactly one plugin).

Returns:

Type Description
'List[Type[ExecutionHandler]]'

Flat list of handler classes.

Raises:

Type Description
PluginRegistryError

On duplicate action_type.

all_patterns()

Aggregate patterns from all plugins.

Pattern IDs must be unique across all plugins.

Returns:

Type Description
'List[Pattern]'

Flat list of all Pattern objects.

Raises:

Type Description
PluginRegistryError

On duplicate pattern ID.

emit_actions_for(artifact, planning_input)

Collect action dicts from all plugins for a single artifact.

Calls each plugin's emit_actions() in load order and aggregates the results. Evidence is extracted from artifact.metadata["evidence"].

Parameters:

Name Type Description Default
artifact 'PlanningArtifact'

The artifact being planned.

required
planning_input 'PlanningInput'

Full planning context.

required

Returns:

Type Description
'List[Dict[str, Any]]'

Flat list of action template dicts (no id field).

list_plugins()

Return ordered list of all registered plugins.

register(plugin)

Add a validated plugin to the registry.

Parameters:

Name Type Description Default
plugin 'DitaPlugin'

Plugin instance (already validated).

required

PluginRegistryError

Bases: RuntimeError

Raised when plugin conflict or structural error is detected.

get_plugin_registry()

Return the global plugin registry, populating it on first call.

This function is safe to call from anywhere. The registry is built exactly once per process.

Returns:

Type Description
PluginRegistry

Fully populated :class:PluginRegistry.

dita_package_processor.plugins.core_plugin

Built-in core plugin.

CorePlugin wraps the entire existing built-in stack:

  • Patterns → loaded from knowledge/known_patterns.yaml
  • Handlers → all filesystem and semantic handlers
  • Actions → copy_map / copy_topic / copy_media per artifact type

It is the reference implementation of the DitaPlugin protocol and is always loaded first, before any third-party plugins.

Third-party plugin developers should study this class as the canonical example. Note that emit_actions must NOT include an "id" key — the planner assigns IDs globally after collecting all actions.

CorePlugin

Bases: DitaPlugin

Built-in plugin that provides the standard DITA processing stack.

This plugin is always loaded first and covers the full standard pipeline for DITA 1.3 packages.

emit_actions(artifact, evidence, context)

Emit a single copy action for every artifact.

Routing: map → copy_map topic → copy_topic media → copy_media

The action type mirrors the artifact type so the built-in handlers handle all standard artifacts without additional configuration.

handlers()

Return all built-in execution handler classes.

patterns()

Load all built-in discovery patterns from known_patterns.yaml.