Writing Plugins¶
Signified provides a plugin system built on top of pluggy.
Important: Hooks Are Disabled by Default¶
By default, signified.plugins.pm is a no-op manager. To activate real hooks:
-
Install plugin support:
-
Run with:
Without this environment variable set, plugin hooks will not execute.
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.
Creating a Plugin¶
Implement hooks with @hookimpl, then register with pm:
from typing import Any
from signified import Signal, Variable
from signified.plugins import hookimpl, pm
class MyPlugin:
def __init__(self) -> None:
self.created_count = 0
@hookimpl
def created(self, value: Variable[Any]) -> None:
self.created_count += 1
print(f"created: {value:d}")
plugin = MyPlugin()
pm.register(plugin)
x = Signal(1)
y = x + 1
print(plugin.created_count) # 2 when hooks are enabled
pm.unregister(plugin)
Plugin Management¶
The global manager lives at signified.plugins.pm:
Logging Example¶
from __future__ import annotations
import logging
from typing import Any
from signified import Signal, Variable
from signified.plugins import hookimpl, pm
class ReactiveLogger:
def __init__(self, logger: Any | None = None):
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]) -> None:
self.logger.info(f"Created {value:d}")
@hookimpl
def updated(self, value: Variable[Any]) -> None:
self.logger.info(f"Updated {value:n} to {value.value}")
@hookimpl
def named(self, value: Variable[Any]) -> None:
self.logger.info(f"Named {type(value).__name__}(id={id(value)}) as {value:n}")
logger_plugin = ReactiveLogger()
pm.register(logger_plugin)
x = Signal(1).add_name("x")
y = (x + 1).add_name("y")
x.value = 5
print(y.value) # 6
pm.unregister(logger_plugin)