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 |
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.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¶
CorePlugin— always first; provides the built-in stack.- 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 |
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: |
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.