Skip to content

README

liblaf.cherries.core.plugin

Type Aliases:

Classes:

  • ImplInfo

    Ordering metadata attached to a plugin hook implementation.

  • Plugin

    Base class for Cherries plugins.

  • PluginManager

    Register plugins and delegate hook calls in dependency order.

Functions:

  • get_impl_info

    Return hook metadata previously attached with impl.

  • impl

    Mark a method as a plugin hook implementation.

MethodName

MethodName = str

PluginName

PluginName = str

ImplInfo

Ordering metadata attached to a plugin hook implementation.

Parameters:

  • after (Iterable[PluginName], default: () ) –

    Plugin names that should run before this implementation.

  • before (Iterable[PluginName], default: () ) –

    Plugin names that should run after this implementation.

Attributes:

after class-attribute instance-attribute

after: Iterable[PluginName] = ()

Plugin names that should run before this implementation.

before class-attribute instance-attribute

before: Iterable[PluginName] = ()

Plugin names that should run after this implementation.

Plugin

Base class for Cherries plugins.

Attributes:

Parameters:

name class-attribute instance-attribute

name: PluginName = field(
    default=Factory(_default_name, takes_self=True),
    kw_only=True,
)

PluginManager

Register plugins and delegate hook calls in dependency order.

Only methods decorated with impl are invoked. Hook order is cached per method and recalculated whenever a plugin is registered.

Parameters:

  • registry (dict[PluginName, Plugin], default: <class 'dict'> ) –

    dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

Methods:

  • __getattr__

    Return a callable that delegates hook name.

  • delegate

    Call hook method on every plugin that implements it.

  • register

    Register or replace plugin.

Attributes:

registry class-attribute instance-attribute

registry: dict[PluginName, Plugin] = field(
    factory=dict, kw_only=True
)

Plugins keyed by their unique plugin name.

__getattr__

__getattr__(name: str) -> Any

Return a callable that delegates hook name.

This allows plugin delegates to satisfy protocol methods such as log_metric() without defining each hook explicitly.

Source code in src/liblaf/cherries/core/plugin/_manager.py
def __getattr__(self, name: str) -> Any:
    """Return a callable that delegates hook `name`.

    This allows plugin delegates to satisfy protocol methods such as
    `log_metric()` without defining each hook explicitly.
    """
    return functools.partial(self.delegate, name)

delegate

delegate(method: MethodName, *args, **kwargs) -> Any

Call hook method on every plugin that implements it.

Hook methods must be decorated with impl. Exceptions are logged and later plugins still run.

Source code in src/liblaf/cherries/core/plugin/_manager.py
def delegate(self, method: MethodName, *args, **kwargs) -> Any:
    """Call hook `method` on every plugin that implements it.

    Hook methods must be decorated with [`impl`][liblaf.cherries.core.impl].
    Exceptions are logged and later plugins still run.
    """
    for plugin in self._sort_plugins(method):
        try:
            getattr(plugin, method)(*args, **kwargs)
        except Exception:
            logger.exception("Plugin %s failed in %s", plugin.name, method)

register

register(plugin: Plugin) -> None

Register or replace plugin.

The cached hook order is cleared so future delegations include the new plugin and any changed ordering constraints.

Source code in src/liblaf/cherries/core/plugin/_manager.py
def register(self, plugin: Plugin) -> None:
    """Register or replace `plugin`.

    The cached hook order is cleared so future delegations include the new
    plugin and any changed ordering constraints.
    """
    self.registry[plugin.name] = plugin
    self._sort_plugins_cache.clear()

get_impl_info

get_impl_info(func: Callable | None) -> ImplInfo | None

Return hook metadata previously attached with impl.

Source code in src/liblaf/cherries/core/plugin/_impl.py
def get_impl_info(func: Callable | None) -> ImplInfo | None:
    """Return hook metadata previously attached with [`impl`][liblaf.cherries.core.impl]."""
    if func is None:
        return None
    return getattr(func, "__cherries_impl__", None)

impl

impl[F: Callable[..., Any]](
    func: F,
    /,
    *,
    after: Iterable[PluginName] = (),
    before: Iterable[PluginName] = (),
) -> F
impl[F: Callable[..., Any]](
    *,
    after: Iterable[PluginName] = (),
    before: Iterable[PluginName] = (),
) -> Callable[[F], F]

Mark a method as a plugin hook implementation.

Parameters:

  • func (Callable[..., Any] | None, default: None ) –

    Method being decorated.

  • **kwargs (Any, default: {} ) –

    Ordering metadata accepted by [ImplInfo][liblaf.cherries.core.ImplInfo].

Examples:

>>> @impl(before=("Comet",))
... def log_metric():
...     return None
>>> get_impl_info(log_metric).before
('Comet',)
Source code in src/liblaf/cherries/core/plugin/_impl.py
def impl(func: Callable[..., Any] | None = None, /, **kwargs: Any) -> Any:
    """Mark a method as a plugin hook implementation.

    Args:
        func: Method being decorated.
        **kwargs: Ordering metadata accepted by [`ImplInfo`][liblaf.cherries.core.ImplInfo].

    Examples:
        >>> @impl(before=("Comet",))
        ... def log_metric():
        ...     return None
        >>> get_impl_info(log_metric).before
        ('Comet',)
    """
    if func is None:
        return functools.partial(impl, **kwargs)
    info = ImplInfo(**kwargs)
    func.__cherries_impl__ = info  # ty:ignore[unresolved-attribute]
    return func