Skip to content

Commit a2063ae

Browse files
refactor(caller): Protocol HookCaller, split callers, CompletionHook multicall
Complete design step 05: - HookCaller is now a @runtime_checkable Protocol; concrete callers are NormalHookCaller (split list[NormalImpl] / list[WrapperImpl] storage), HistoricHookCaller (memorize/replay, rejects wrappers) and SubsetHookCaller (read-only filtered proxy). _HookCaller and _SubsetHookCaller remain as compat aliases. - _multicall takes dual sequences and orchestrates phases only: wrapper setup collects CompletionHooks, normals run, completion hooks run LIFO and may replace (result, exception) - no wrapper flag branching. - add_hookspecs hands a NormalHookCaller over to a HistoricHookCaller when a historic spec arrives after impl registration. - PluginManager._hookexec and tracing use the dual-sequence signature; monitoring callbacks keep receiving one combined impl list. - HookSpec.verify_all_args_are_provided replaces the caller-side helper; set_specification accepts a config object or legacy mapping (shim). - New tests: protocol isinstance for all concretes, historic handover, historic direct-call/call_extra rejection. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 53041e2 commit a2063ae

12 files changed

Lines changed: 692 additions & 250 deletions

changelog/707.feature.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
:class:`pluggy.HookCaller` is now a runtime-checkable
2+
:class:`~typing.Protocol` implemented by the new concrete callers
3+
:class:`pluggy.NormalHookCaller` (split normal/wrapper implementation
4+
lists), :class:`pluggy.HistoricHookCaller` (call memorization and replay,
5+
no wrappers) and :class:`pluggy.SubsetHookCaller`.
6+
``isinstance(caller, HookCaller)`` keeps working for all of them.
7+
8+
The hook execution engine now runs a dual-sequence multicall: wrappers own
9+
their setup/teardown through ``CompletionHook`` callbacks which run LIFO
10+
after the normal implementations, removing all wrapper flag branching from
11+
the hot loop. Hook call monitoring callbacks still receive a single
12+
combined implementation list.

docs/api_reference.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ API Reference
2929
:members:
3030
:special-members: __call__
3131

32+
.. autoclass:: pluggy.NormalHookCaller()
33+
:members:
34+
:special-members: __call__
35+
36+
.. autoclass:: pluggy.HistoricHookCaller()
37+
:members:
38+
:special-members: __call__
39+
40+
.. autoclass:: pluggy.SubsetHookCaller()
41+
:members:
42+
3243
.. autoclass:: pluggy.HookCallError()
3344
:show-inheritance:
3445
:members:

src/pluggy/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
"PluginManager",
44
"PluginValidationError",
55
"HookCaller",
6+
"NormalHookCaller",
7+
"HistoricHookCaller",
8+
"SubsetHookCaller",
69
"HookCallError",
710
"HookspecConfiguration",
811
"HookimplConfiguration",
@@ -20,12 +23,15 @@
2023
]
2124
from ._config import HookimplConfiguration
2225
from ._config import HookspecConfiguration
26+
from ._hooks import HistoricHookCaller
2327
from ._hooks import HookCaller
2428
from ._hooks import HookImpl
2529
from ._hooks import HookimplMarker
2630
from ._hooks import HookRelay
2731
from ._hooks import HookspecMarker
32+
from ._hooks import NormalHookCaller
2833
from ._hooks import NormalImpl
34+
from ._hooks import SubsetHookCaller
2935
from ._hooks import WrapperImpl
3036
from ._manager import PluginManager
3137
from ._manager import PluginValidationError

0 commit comments

Comments
 (0)