Coverage for src / kemi / operations / _ops_hooks.py: 91%
23 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-06-05 15:47 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-06-05 15:47 +0000
1"""Event hook operations: add_event_hook, remove_event_hook, _run_hooks."""
3from __future__ import annotations
5import logging
6from typing import TYPE_CHECKING, Any, Callable
8if TYPE_CHECKING:
9 from kemi._memory_impl import Memory
11logger = logging.getLogger(__name__)
14def add(memory: "Memory", phase: str, callback: Callable[..., Any]) -> None:
15 """Register an event hook callback.
17 Args:
18 phase: "pre" or "post" — called before or after the operation.
19 callback: Callable that receives (operation, **kwargs).
20 """
21 if phase not in ("pre", "post"):
22 raise ValueError("phase must be 'pre' or 'post'")
23 memory._event_hooks[phase].append(callback)
26def remove(
27 memory: "Memory", phase: str, callback: Callable[..., Any]
28) -> bool:
29 """Remove a previously registered event hook callback.
31 Returns True if removed, False if not found.
32 """
33 if phase in memory._event_hooks and callback in memory._event_hooks[phase]:
34 memory._event_hooks[phase].remove(callback)
35 return True
36 return False
39def run(
40 memory: "Memory",
41 phase: str,
42 operation: str,
43 *,
44 raise_on_error: bool | None = None,
45 **kwargs: Any,
46) -> None:
47 """Run all hooks registered for a phase/operation.
49 Args:
50 phase: "pre" or "post".
51 operation: Name of the operation triggering the hook.
52 raise_on_error: If True, exceptions from hooks are re-raised so
53 a failing pre-hook can abort the operation. If None (default),
54 the value is taken from ``memory._config.hooks_raise_on_error``.
55 **kwargs: Passed through to each callback.
56 """
57 if raise_on_error is None:
58 raise_on_error = memory._config.hooks_raise_on_error
59 for hook in memory._event_hooks.get(phase, []):
60 try:
61 hook(operation, **kwargs)
62 except Exception:
63 # Broad catch: hooks are user code, so any error is their fault.
64 # Honour the configured raise_on_error flag.
65 if raise_on_error:
66 raise
67 logger.warning(
68 f"Event hook failed for {phase}:{operation}", exc_info=True
69 )