Writing Plugins¶
Signified provides a plugin system built on top of pluggy that allows you to extend and customize its behavior. This guide explains the basics of creating plugins for signified.
Plugin Hooks¶
The plugin system provides hooks for key events in a reactive value's lifecycle:
read
: Called whenever a reactive value's current value is accessedcreated
: Called when a new reactive value is instantiatedupdated
: Called when a reactive value's content changesnamed
: Called when a reactive value is given a name
These hooks allow plugins to observe and respond to the complete lifecycle of reactive values.
Additional hooks may be added in the future. If you have a good idea for a plugin that would benefit from additional hooks, please let me know!
Creating a Plugin¶
To create a plugin:
- Create a class that will contain your hook implementations
- Implement any desired hooks using the
@hookimpl
decorator (available fromsignified.plugin
) - Register your plugin with the global plugin manager
pm
(also available insignified.plugin
)
Here's a minimal example:
from signified.plugin import hookimpl, pm
from signified import ReactiveValue
from typing import Any
class MyPlugin:
@hookimpl
def created(self, value: ReactiveValue[Any]) -> None:
# Do something when a reactive value is created
pass
# Register the plugin
plugin = MyPlugin()
pm.register(plugin)
Plugin Management¶
The library maintains a global plugin manager instance in signified.plugin.pm
. Plugins can be registered and unregistered at runtime:
from signified.plugin import pm
# Register a plugin
pm.register(my_plugin)
# Remove a plugin
pm.unregister(my_plugin)
More complex example¶
I created a simple logging plugin.
from __future__ import annotations
import logging
from typing import Any
from signified import Variable
from signified.plugin import hookimpl, pm
class ReactiveLogger:
"""A logger plugin for tracking reactive value lifecycle."""
def __init__(self, logger: Any | None = None):
"""Initialize with optional logger, defaulting to standard logging."""
if logger is None:
_logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
formatter = logging.Formatter("%(message)s")
handler.setFormatter(formatter)
_logger.addHandler(handler)
_logger.setLevel(logging.INFO)
else:
_logger = logger
self.logger = _logger
@hookimpl
def created(self, value: Variable[Any, Any]) -> None:
"""Log when a reactive value is created.
Args:
value: The created reactive value
"""
self.logger.info(f"Created {value:d}")
@hookimpl
def updated(self, value: Variable[Any, Any]) -> None:
"""Log when a reactive value is updated.
Args:
value: The updated reactive value
"""
self.logger.info(f"Updated {value:n} to {value.value}")
@hookimpl
def named(self, value: Variable[Any, Any]) -> None:
"""Log when a reactive value is named.
Args:
value: The reactive value that was assigned a name.
"""
self.logger.info(f"Named {type(value).__name__}(id={id(value)}) as {value:n}")
DEFAULT_LOGGING_PLUGIN = ReactiveLogger()
pm.register(DEFAULT_LOGGING_PLUGIN)
Here, we implement logging behavior for the created
, updated
, and named
hooks.
Finally, at the end, we create the plugin and register it to the plugin manager.