Coverage for src / lexigram / contracts / admin / action_hooks.py: 100%
16 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:58 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:58 +0800
1"""Action lifecycle hook contracts — before/after/failure hooks for admin actions.
3Defines the ``ActionHookProtocol`` that action-level and resource-level
4hooks must satisfy, plus the ``HasActionHooks`` protocol marking objects
5that expose hook collections.
6"""
8from __future__ import annotations
10from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
12if TYPE_CHECKING:
13 from lexigram.contracts.admin.errors import AdminError
14 from lexigram.contracts.core.result import Result
17@runtime_checkable
18class ActionHookProtocol(Protocol):
19 """Lifecycle hooks for admin actions.
21 Hooks are executed around the action body by ``ActionExecutor``:
23 - ``before()`` runs first and may modify the input data or abort the
24 action by returning ``Err``.
25 - ``after()`` runs after successful execution.
26 - ``on_failure()`` runs when ``before()`` returned ``Err`` or the
27 action body raised an exception.
28 """
30 async def before(
31 self, record: Any, data: dict[str, Any]
32 ) -> Result[dict[str, Any], AdminError]:
33 """Run before the action body.
35 Args:
36 record: The target record (may be ``None`` for global actions).
37 data: Action payload; may be amended by the hook.
39 Returns:
40 ``Ok(data)`` with possibly-amended data, or ``Err`` to abort.
41 """
42 ...
44 async def after(self, record: Any, result: Any) -> None:
45 """Run after a successful action execution.
47 Args:
48 record: The target record (may be ``None`` for global actions).
49 result: The action result payload.
50 """
51 ...
53 async def on_failure(self, record: Any, error: Exception) -> None:
54 """Run when the action fails.
56 Args:
57 record: The target record (may be ``None`` for global actions).
58 error: The exception or error that caused the failure.
59 """
60 ...
63@runtime_checkable
64class HasActionHooks(Protocol):
65 """Protocol for objects exposing action lifecycle hooks.
67 Implemented by action handlers and resources so the executor can
68 discover ``before`` / ``after`` / ``failure`` hooks declaratively.
69 """
71 @property
72 def before_hooks(self) -> list[ActionHookProtocol]:
73 """Hooks run before the action body."""
74 ...
76 @property
77 def after_hooks(self) -> list[ActionHookProtocol]:
78 """Hooks run after successful execution."""
79 ...
81 @property
82 def failure_hooks(self) -> list[ActionHookProtocol]:
83 """Hooks run on action failure."""
84 ...
87__all__ = ["ActionHookProtocol", "HasActionHooks"]