Skip to content

Plugin System

Overview

The Athomic Plugin System provides a powerful and clean way to extend the core functionality of the application without modifying its source code. Plugins can introduce new API routes, register custom services into the lifecycle, or execute logic at specific points in the application's startup sequence.

The system is built on Python's native importlib.metadata and entry points, making it a standard and robust way to create a pluggable architecture.

How It Works

  1. Entry Point: A plugin is a separate, installable Python package that defines an entry point in its pyproject.toml under the [project.entry-points."nala.plugins"] group.
  2. Discovery: At startup, the PluginManager discovers all installed packages that declare this entry point.
  3. Hooks: Plugins define functions decorated with @hookimpl. The PluginManager registers these functions as implementations for specific, predefined hooks.
  4. Execution: When the core application reaches a certain point, it calls plugin_manager.call_hook("hook_name"), which executes all registered implementations for that hook.

Available Hooks

  • on_athomic_startup(athomic: Athomic): Called early in the startup sequence. Ideal for registering custom services with the LifecycleRegistry.
  • get_api_routers() -> List[APIRouter]: Called by the web server layer. Each plugin can return a list of FastAPI APIRouter objects, which will be automatically mounted into the main application.

Creating a Plugin

See the plugins/nala-plugin-greetings directory for a complete example of a simple plugin.

API Reference

nala.athomic.plugins.hooks.hookimpl(func=None, *, priority=100)

hookimpl(func: Callable) -> Callable
hookimpl(
    func: None = None, *, priority: int = 100
) -> Callable[[Callable], Callable]

Marks a function as a hook implementation.

May be used either as a bare decorator (@hookimpl) or as a factory (@hookimpl(priority=50)). Lower priority values run first when multiple plugins implement the same hook; the default is 100.

Parameters:

Name Type Description Default
func Optional[Callable]

When called without parentheses, the decorated function is passed here directly by the Python decorator protocol.

None
priority int

Execution order relative to other implementations of the same hook. Lower = earlier. Defaults to 100.

100

nala.athomic.plugins.manager.PluginManager

Bases: PluginManagerProtocol

Mediates the interaction between the Athomic core and external plugins.

It implements the Mediator, Observer, and Registry patterns to handle plugin discovery, registration, and resilient hook execution.

__init__(settings=None, notification_provider=None)

Initializes the PluginManager and its internal hook registry.

Parameters:

Name Type Description Default
settings Optional[PluginsSettings]

Plugin configuration. Defaults to the global settings' plugins section, but can be injected for isolated testing or for running multiple independently-configured Athomic instances in the same process.

None
notification_provider Optional[NotificationProtocol]

Optional pre-built notification provider used when hook errors trigger alerts. When None, a provider is created lazily via NotificationFactory at the first error — keeping plugins/ free of a hard load-time dependency on notification/ for deployments that never configure alerts.

None

call_hook(hook_name, **kwargs) async

Executes all registered implementations for a given hook with resilience, observability (tracing/metrics), and failure notification.

describe_plugin(name)

Returns introspection data for a loaded plugin.

Parameters:

Name Type Description Default
name str

The entry-point name of the plugin (as reported in loaded_plugins).

required

Returns:

Type Description
Optional[Dict[str, Any]]

A dict with name and hooks keys, or None if no

Optional[Dict[str, Any]]

plugin with that name has been loaded.

discover_and_load() async

Discovers plugins using Python's entry points (group="nala.plugins"), loads their modules, and registers all functions marked with @hookimpl.

get_api_routers() async

Public contract method for the API layer. It calls the 'get_api_routers' hook, aggregates the results (a list of lists), and flattens them into a single list of router objects.