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 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 the hook_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 in hook_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 from ANY source, or even to ANY event from ANY 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()

Parameters:
Return type:

None

firebird.base.hooks.register_name(instance: Any, name: str) None

shortcut for hook_manager.register_name()

Parameters:
  • instance (Any) –

  • name (str) –

Return type:

None

firebird.base.hooks.add_hook(event: Any, source: Any, callback: Callable) None

shortcut for hook_manager.add_hook()

Parameters:
Return type:

None

firebird.base.hooks.get_callbacks(event: Any, source: Any) list

shortcut for hook_manager.get_callbacks()

Parameters:
  • event (Any) –

  • source (Any) –

Return type:

list

Classes

class firebird.base.hooks.HookManager(*args, **kwargs)[source]

Bases: Singleton

Manages 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:

Singleton

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 ANY to 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 callback function 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 source is a class/instance type not registered as hookable, or if source is not a class, instance, name, or ANY.

  • ValueError – If event is not ANY and is not declared as a supported event by the specified source class (during register_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 event and source, considering class hierarchy, registered names, and the ANY sentinel 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 ANY as the source here is generally not meaningful, as event triggers typically originate from a specific source.

Returns:

A list of Callable objects. The order reflects the lookup process but is not guaranteed between different calls or manager states.

Return type:

list

Lookup Logic:

The returned list aggregates callbacks from registrations matching:

  1. Specific Instance: Hooks registered for (event, ANY, source instance).

  2. Specific Name: Hooks for (event, ANY, source name) if source instance has a registered name.

  3. Specific Class: Hooks for (event, cls, ANY) for every class cls in the source instance’s Method Resolution Order (MRO) that is registered.

  4. ANY Event on Instance: Hooks for (ANY, ANY, source instance).

  5. ANY Event on Name: Hooks for (ANY, ANY, source name`) if applicable.

  6. ANY Event on Class: Hooks for (ANY, cls, ANY) for applicable classes cls in the MRO.

Note

If source is 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 Enum type (recommended), a set of event identifiers, or None if events are not statically known or validated at registration time.

Raises:

TypeError – If events is 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 instance is not an instance of any registered hookable class.

Return type:

None

remove_all_hooks() None[source]

Removes all installed hooks.

Return type:

None

remove_hook(event: Any, source: Any, callback: Callable) None[source]

Remove a previously registered hook callback.

Parameters:
  • event (Any) – The event identifier used when registering the hook.

  • source (Any) – The hookable class, instance, name, or ANY used when registering.

  • callback (Callable) – The specific callback function instance that was registered.

Return type:

None

Important

To successfully remove a hook, all arguments (event, source, callback) must exactly match the values used in the original add_hook() call. Comparing function objects requires using the same function object.

This method does nothing if no matching hook registration is found.

reset() None[source]

Removes all installed hooks and unregisters all hookable classes and instances.

Return type:

None

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: Distinct

Represents 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. ANY if targeting an instance/name directly or globally.

  • instance (Any) – The specific instance or instance name this hook targets. ANY if 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. ANY if targeting an instance/name directly or globally.

alias of ANY

instance

The specific instance or instance name this hook targets. ANY if 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:

Any

callbacks: list[Callable]

A list of callable functions to be executed when the specified event occurs for the specified source.

event: Any

The specific event this hook subscribes to (can be ANY).