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 source
provides 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.Event
is 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 provider
is a class or function that implements the event for event source, and asks thehook_manager
for list of event consumers (callbacks) registered for particular event and source.Event consumer
is a function or class method that implements the callback for particular event. The callback must be registered inhook_manager
before 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
ANY
event from particular source, or particular event fromANY
source, or even toANY
event fromANY
source.
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:
Singleton
Hook manager.
- add_hook(event: Any, source: Any, callback: Callable) None [source]¶
Add new hook.
- Parameters:
- Return type:
None
Important
The signature of
callback
must conform to requirements for particular hookable event.- Raises:
TypeError – When
subject
is not registered as hookable.ValueError – When
event
is 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) None [source]¶
Register hookable class.
- Parameters:
- Return type:
None
Events could be specified using an
Enum
type 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