Skip to content

Extensions

Asterios Raptis edited this page Mar 27, 2026 · 1 revision

Extensions

PluginForge supports an extension point pattern for querying active plugins by interface, inspired by PF4J's extension system.

Concept

While pluggy hooks use a fire-and-forget call pattern (call_hook("on_save", ...)), extension points let you query and interact with plugins directly:

Hooks:       pm.call_hook("on_save", document=doc)  -> list of results
Extensions:  pm.get_extensions(ExportFormat)          -> list of plugin instances

Defining Extension Points

An extension point is any Python class or ABC:

from abc import ABC, abstractmethod

class ExportFormat(ABC):
    """Extension point for document export formats."""

    @abstractmethod
    def export(self, document: dict, output_path: str) -> str:
        """Export a document and return the output file path."""
        ...

    @abstractmethod
    def supported_extensions(self) -> list[str]:
        """Return supported file extensions."""
        ...

Implementing Extensions

Plugins implement extension points by inheriting from both BasePlugin and the extension point:

from pluginforge import BasePlugin

class EpubExport(BasePlugin, ExportFormat):
    name = "epub_export"
    version = "1.0.0"

    def export(self, document: dict, output_path: str) -> str:
        # ... epub export logic ...
        return f"{output_path}.epub"

    def supported_extensions(self) -> list[str]:
        return [".epub"]


class PdfExport(BasePlugin, ExportFormat):
    name = "pdf_export"
    version = "1.0.0"

    def export(self, document: dict, output_path: str) -> str:
        # ... pdf export logic ...
        return f"{output_path}.pdf"

    def supported_extensions(self) -> list[str]:
        return [".pdf"]

Querying Extensions

Use get_extensions() to find all active plugins implementing an extension point:

pm = PluginManager("config/app.yaml")
pm.register_plugins([EpubExport, PdfExport, AnalyticsPlugin])

# Only returns plugins that implement ExportFormat
exporters = pm.get_extensions(ExportFormat)
# [<EpubExport>, <PdfExport>]

# Use them directly
for exporter in exporters:
    path = exporter.export(document, "/tmp/output")
    print(f"Exported to {path}")

# Get all supported formats
all_extensions = []
for exporter in exporters:
    all_extensions.extend(exporter.supported_extensions())
# [".epub", ".pdf"]

Extension Points vs. Hooks

Aspect Hooks Extensions
Pattern Fire-and-forget Query and interact
Returns List of results List of plugin instances
Use case Events, notifications Capabilities, strategies
Example "Document was saved" "Which export formats exist?"

Use hooks when you want to notify plugins about events. Use extensions when you want to discover capabilities and interact with specific plugins.

Combining with Hooks

A plugin can be both an extension and a hook implementer:

class EpubExport(BasePlugin, ExportFormat):
    name = "epub_export"

    # Extension point implementation
    def export(self, document: dict, output_path: str) -> str:
        return self._do_export(document, output_path)

    def supported_extensions(self) -> list[str]:
        return [".epub"]

    # Hook implementation
    @hookimpl
    def on_document_save(self, document: dict) -> None:
        # Auto-export on save if configured
        if self.config.get("auto_export"):
            self.export(document, self.config["output_dir"])

Clone this wiki locally