hooks - Hook manager¶
Overview¶
This module provides a general framework for callbacks and “hookable” events, implementing a variation of the publish-subscribe pattern. It allows different parts of an application to register interest in events triggered by specific objects or classes and execute custom code (callbacks) when those events occur.
Architecture¶
The callback extension mechanism is based on the following:
The
Event sourceprovides one or more “hookable events” that work like connection points. The event source represents “origin of event” and is always identified by class, class instance or name. Event sources that are identified by classes (or their instances) must be registered along with events they provide.Eventis typically linked to particular event source, but it’s not mandatory and it’s possible to define global events. Event is represented as value of any type, that must be unique in used context (particular event source or global).Each event should be properly documented along with required signature for callback function.
Event provideris a class or function that implements the event for event source, and asks thehook_managerfor list of event consumers (callbacks) registered for particular event and source.Event consumeris a function or class method that implements the callback for particular event. The callback must be registered inhook_managerbefore it could be called by event providers.
The architecture supports multiple usage strategies:
If event provider uses class instance to identify the event source, it’s possible to register callbacks to all instances (by registering to class), or particular instance(s).
It’s possible to register callback to particular instance by name, if instance is associated with name by
register_name()function.It’s possible to register callback to
ANYevent from particular source, or particular event fromANYsource, or even toANYevent fromANYsource.
Example¶
from __future__ import annotations
from enum import Enum, auto
from firebird.base.types import *
from firebird.base.hooks import hook_manager
class MyEvents(Enum):
"Sample definition of events"
CREATE = auto()
ACTION = auto()
class MyHookable:
"Example of hookable class, i.e. a class that calls hooks registered for events."
def __init__(self, name: str):
self.name: str = name
for hook in hook_manager.get_callbacks(MyEvents.CREATE, self):
try:
hook(self, MyEvents.CREATE)
except Exception as e:
print(f"{self.name}.CREATE hook call outcome: ERROR ({e.args[0]})")
else:
print(f"{self.name}.CREATE hook call outcome: OK")
def action(self):
print(f"{self.name}.ACTION!")
for hook in hook_manager.get_callbacks(MyEvents.ACTION, self):
try:
hook(self, MyEvents.ACTION)
except Exception as e:
print(f"{self.name}.ACTION hook call outcome: ERROR ({e.args[0]})")
else:
print(f"{self.name}.ACTION hook call outcome: OK")
class MyHook:
"Example of hook implementation"
def __init__(self, name: str):
self.name: str = name
def callback(self, subject: MyHookable, event: MyEvents):
print(f"Hook {self.name} event {event.name} called by {subject.name}")
def err_callback(self, subject: MyHookable, event: MyEvents):
self.callback(subject, event)
raise Exception("Error in hook")
# Example code that installs and uses hooks
hook_manager.register_class(MyHookable, MyEvents)
hook_A: MyHook = MyHook('Hook-A')
hook_B: MyHook = MyHook('Hook-B')
hook_C: MyHook = MyHook('Hook-C')
print("Install hooks")
hook_manager.add_hook(MyEvents.CREATE, MyHookable, hook_A.callback)
hook_manager.add_hook(MyEvents.CREATE, MyHookable, hook_B.err_callback)
hook_manager.add_hook(MyEvents.ACTION, MyHookable, hook_C.callback)
print("Create event sources, emits CREATE")
src_A: MyHookable = MyHookable('Source-A')
src_B: MyHookable = MyHookable('Source-B')
print("Install instance hooks")
hook_manager.add_hook(MyEvents.ACTION, src_A, hook_A.callback)
hook_manager.add_hook(MyEvents.ACTION, src_B, hook_B.callback)
print("And action!")
src_A.action()
src_B.action()
Output from sample code:
Install hooks
Create event sources, emits CREATE
Hook Hook-A event CREATE called by Source-A
Source-A.CREATE hook call outcome: OK
Hook Hook-B event CREATE called by Source-A
Source-A.CREATE hook call outcome: ERROR (Error in hook)
Hook Hook-A event CREATE called by Source-B
Source-B.CREATE hook call outcome: OK
Hook Hook-B event CREATE called by Source-B
Source-B.CREATE hook call outcome: ERROR (Error in hook)
Install instance hooks
And action!
Source-A.ACTION!
Hook Hook-A event ACTION called by Source-A
Source-A.ACTION hook call outcome: OK
Hook Hook-C event ACTION called by Source-A
Source-A.ACTION hook call outcome: OK
Source-B.ACTION!
Hook Hook-B event ACTION called by Source-B
Source-B.ACTION hook call outcome: OK
Hook Hook-C event ACTION called by Source-B
Source-B.ACTION hook call outcome: OK
Functions¶
- firebird.base.hooks.register_class(cls: type, events: type[Enum] | set | None = None) None¶
Shortcut for
hook_manager.register_class()
- firebird.base.hooks.register_name(instance: Any, name: str) None¶
shortcut for
hook_manager.register_name()
Classes¶
- class firebird.base.hooks.HookManager(*args, **kwargs)[source]¶
Bases:
SingletonManages the registration and retrieval of hooks (callbacks).
This singleton class acts as the central registry for hookable classes, named instances, and the hooks themselves. It provides methods to add, remove, and retrieve callbacks based on event and source specifications.
- Return type:
- add_hook(event: Any, source: Any, callback: Callable) None[source]¶
Register a callback function (hook) for a specific event and source.
- Parameters:
event (Any) – The event identifier the callback subscribes to. Can be
ANYto subscribe to all events from the specified source.source (Any) –
The source of the event. Can be:
A hookable class (registered via
register_class): The callback will trigger for this event from any instance of this class.An instance of a hookable class: The callback will trigger only for this event from this specific instance.
A string name (registered via
register_name): The callback will trigger only for this event from the instance associated with this name.ANY: The callback will trigger for this event from any source.
callback (Callable) – The function or method to be called when the event occurs.
- Return type:
None
Important
The signature of the
callbackfunction must match the signature expected by the code that triggers the event (the event provider). This framework does not enforce signature matching; it’s the responsibility of the event provider and consumer documentation.- Raises:
TypeError – If
sourceis a class/instance type not registered as hookable, or ifsourceis not a class, instance, name, orANY.ValueError – If
eventis notANYand is not declared as a supported event by the specifiedsourceclass (duringregister_class).
- Parameters:
- Return type:
None
- get_callbacks(event: Any, source: Any) list[source]¶
Return a list of all callbacks applicable to the specified event and source.
The method searches for matching hook registrations based on the provided
eventandsource, considering class hierarchy, registered names, and theANYsentinel for broader matches.- Parameters:
event (Any) – The specific event identifier being triggered.
source (Any) –
The source triggering the event. Can be:
An instance of a hookable class.
A hookable class itself (e.g., for class-level events).
A registered string name.
Note
Using
ANYas thesourcehere is generally not meaningful, as event triggers typically originate from a specific source.
- Returns:
A list of
Callableobjects. The order reflects the lookup process but is not guaranteed between different calls or manager states.- Return type:
- Lookup Logic:
The returned list aggregates callbacks from registrations matching:
Specific Instance: Hooks registered for (
event,ANY,sourceinstance).Specific Name: Hooks for (
event,ANY,sourcename) ifsourceinstance has a registered name.Specific Class: Hooks for (
event,cls,ANY) for every classclsin thesourceinstance’s Method Resolution Order (MRO) that is registered.ANY Event on Instance: Hooks for (
ANY,ANY,sourceinstance).ANY Event on Name: Hooks for (
ANY,ANY,sourcename`) if applicable.ANY Event on Class: Hooks for (
ANY,cls,ANY) for applicable classesclsin the MRO.
Note
If
sourceis a class or name directly, only relevant parts of the above logic apply.
- register_class(cls: type, events: type[Enum] | set | None = None) None[source]¶
Register a class as being capable of generating hookable events.
Registration is necessary for validation when adding hooks and potentially for optimizing callback lookups.
- Parameters:
cls (type) – The class that acts as an event source.
events (type[Enum] | set | None) – The set of events this class (and its instances) can trigger. Can be specified using an
Enumtype (recommended), asetof event identifiers, orNoneif events are not statically known or validated at registration time.
- Raises:
TypeError – If
eventsis provided but is not an Enum type or a set.- Return type:
None
- register_name(instance: Any, name: str) None[source]¶
Associate a unique string name with an instance of a hookable class.
This allows registering hooks specifically for this named instance using the name string as the
source.- Parameters:
instance (Any) – An instance of a class previously registered via
register_class.name (str) – A unique string name to assign to the instance.
- Raises:
TypeError – If
instanceis not an instance of any registered hookable class.- Return type:
None
- remove_hook(event: Any, source: Any, callback: Callable) None[source]¶
Remove a previously registered hook callback.
- Parameters:
- Return type:
None
Important
To successfully remove a hook, all arguments (
event,source,callback) must exactly match the values used in the originaladd_hook()call. Comparing function objects requires using the same function object.This method does nothing if no matching hook registration is found.
Globals¶
- firebird.base.hooks.hook_manager: HookManager¶
Hook manager singleton instance.
Dataclasses¶
- class firebird.base.hooks.Hook(event: ~typing.Any, cls: type = ANY, instance: ~typing.Any = ANY, callbacks: list[~collections.abc.Callable] = <factory>)[source]¶
Bases:
DistinctRepresents a registered hook subscription.
Instances of this class store the details of a callback registered for a specific event and source combination within the
HookManager.- Parameters:
event (Any) – The specific event this hook subscribes to (can be
ANY).cls (type) – The specific class this hook targets.
ANYif targeting an instance/name directly or globally.instance (Any) – The specific instance or instance name this hook targets.
ANYif targeting a class or globally.callbacks (list[Callable]) – A list of callable functions to be executed when the specified event occurs for the specified source.
- cls¶
The specific class this hook targets.
ANYif targeting an instance/name directly or globally.alias of
ANY
- instance¶
The specific instance or instance name this hook targets.
ANYif targeting a class or globally.alias of
ANY
- get_key() Any[source]¶
Returns the unique key for this hook registration used by the Registry.
The key is a tuple of (event, class, instance/name).
- Return type: