Skip to content

type_bridge.crud.hooks

hooks

Lifecycle hook system for CRUD operations.

Hooks are duck-typed classes that implement only the methods they care about. Register them on a manager instance via manager.add_hook(hook).

Pre-hooks run in registration order and may raise HookCancelled to abort. Post-hooks run in reverse registration order; errors are logged, not propagated.

CrudEvent

Bases: Enum

CRUD lifecycle events.

HookCancelled

HookCancelled(reason='', *, event=None, hook=None)

Bases: Exception

Raise in a pre-hook to abort the operation.

Attributes:

Name Type Description
reason

Human-readable explanation.

event

The event that was cancelled (set by HookRunner).

hook

The hook instance that raised the cancellation (set by HookRunner).

Source code in type_bridge/crud/hooks.py
def __init__(
    self,
    reason: str = "",
    *,
    event: CrudEvent | None = None,
    hook: Any = None,
):
    self.reason = reason
    self.event = event
    self.hook = hook
    super().__init__(reason)

CrudHook

Bases: Protocol

Protocol for CRUD lifecycle hooks.

Implement only the methods you need. All methods are optional — HookRunner uses hasattr / getattr to discover them.

HookRunner

HookRunner()

Manages hook registration and execution.

Pre-hooks run in registration order. Post-hooks run in reverse registration order (middleware unwinding).

Source code in type_bridge/crud/hooks.py
def __init__(self) -> None:
    self._hooks: list[Any] = []

has_hooks property

has_hooks

Fast guard — skip all hook logic when the list is empty.

add

add(hook)

Register a hook.

Source code in type_bridge/crud/hooks.py
def add(self, hook: Any) -> None:
    """Register a hook."""
    self._hooks.append(hook)

remove

remove(hook)

Unregister a hook.

Raises ValueError if the hook is not registered.

Source code in type_bridge/crud/hooks.py
def remove(self, hook: Any) -> None:
    """Unregister a hook.

    Raises ``ValueError`` if the hook is not registered.
    """
    self._hooks.remove(hook)

run_pre

run_pre(event, sender, instance)

Run pre-hooks in registration order.

Raises HookCancelled if any hook cancels the operation.

Source code in type_bridge/crud/hooks.py
def run_pre(self, event: CrudEvent, sender: type, instance: Any) -> None:
    """Run pre-hooks in registration order.

    Raises ``HookCancelled`` if any hook cancels the operation.
    """
    method_name = event.value  # e.g. "pre_insert"
    for hook in self._hooks:
        if not self._should_run(hook, event, sender):
            continue
        method = getattr(hook, method_name, None)
        if method is not None:
            try:
                method(sender, instance)
            except HookCancelled as exc:
                # Enrich with context if not already set
                if exc.event is None:
                    exc.event = event
                if exc.hook is None:
                    exc.hook = hook
                raise

run_post

run_post(event, sender, instance)

Run post-hooks in reverse registration order.

Errors are logged but do not propagate.

Source code in type_bridge/crud/hooks.py
def run_post(self, event: CrudEvent, sender: type, instance: Any) -> None:
    """Run post-hooks in reverse registration order.

    Errors are logged but do **not** propagate.
    """
    method_name = event.value  # e.g. "post_insert"
    for hook in reversed(self._hooks):
        if not self._should_run(hook, event, sender):
            continue
        method = getattr(hook, method_name, None)
        if method is not None:
            try:
                method(sender, instance)
            except Exception:
                logger.exception(
                    "Post-hook %r failed for %s on %s",
                    hook,
                    event.value,
                    sender.__name__,
                )