hooks - Hook manager¶
Overview¶
This module provides a general framework for callbacks and “hookable” events.
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¶
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:
SingletonHook manager.
- add_hook(event: Any, source: Any, callback: Callable) None[source]¶
Add new hook.
- Parameters:
- Return type:
None
Important
The signature of
callbackmust conform to requirements for particular hookable event.- Raises:
TypeError – When
subjectis not registered as hookable.ValueError – When
eventis not supported by specifiedsubject.
- Parameters:
- Return type:
None
- get_callbacks(event: Any, source: Any) List[source]¶
Returns list of all callbacks installed for specified event and hookable subject.
- register_class(cls: Type, events: Type[Enum] | Set = None) None[source]¶
Register hookable class.
- Parameters:
- Return type:
None
Events could be specified using an
Enumtype or set of event identificators. When Enum is used (recommended), all enum values are registered as hookable events.
- remove_hook(event: Any, source: Any, callback: Callable) None[source]¶
Remove hook callback installed by
add_hook().- Parameters:
- Return type:
None
Important
For successful removal, the argument values must be exactly the same as used in
add_hook()call.The method does nothing if described hook is not installed.
Globals¶
- firebird.base.hooks.hook_manager: HookManager¶
Hook manager