66from typing import TYPE_CHECKING , Any , AsyncContextManager , Protocol , runtime_checkable
77
88if TYPE_CHECKING :
9- from cq . _core . message import CommandBus , EventBus , QueryBus
9+ from cq import CommandBus , EventBus , QueryBus
1010
1111
1212@runtime_checkable
1313class DIAdapter (Protocol ):
14+ """
15+ Protocol for integrating a dependency injection container with python-cq.
16+
17+ Implement this protocol to connect your DI framework to the CQ buses.
18+ A concrete implementation (``InjectionAdapter``) is provided via the
19+ ``python-cq[injection]`` extra for projects that use *python-injection*.
20+ """
21+
1422 __slots__ = ()
1523
1624 @abstractmethod
1725 def command_scope (self ) -> AsyncContextManager [None ]:
26+ """
27+ Return an async context manager that delimits the lifetime of a
28+ command dispatch.
29+
30+ **Responsibilities**
31+
32+ The scope must at minimum manage the lifecycle of a ``RelatedEvents``
33+ instance and register it so that it is resolvable via injection for
34+ the duration of the scope.
35+
36+ **Nested calls**
37+
38+ ``command_scope`` is entered in two distinct situations:
39+
40+ 1. Around a standard command dispatch (via
41+ ``CommandDispatchScopeMiddleware``).
42+ 2. Around each step of a ``ContextCommandPipeline``, which itself
43+ wraps a command dispatch.
44+
45+ This means two nested calls can occur for a single logical command.
46+ Implementations must detect re-entrant activation (e.g. a scope
47+ already active on the current task) and silently ignore the inner
48+ call instead of opening a second, conflicting scope.
49+ """
50+
1851 raise NotImplementedError
1952
2053 @abstractmethod
2154 def lazy [T ](self , tp : type [T ]) -> Callable [[], Awaitable [T ]]:
55+ """
56+ Return a zero-argument async factory for ``tp``.
57+
58+ The returned callable must resolve an instance of ``tp`` from the
59+ DI container each time it is awaited. Resolution is deferred to
60+ call-time so that scoped dependencies are looked up inside the
61+ active scope rather than at registration time.
62+ """
63+
2264 raise NotImplementedError
2365
2466 def register_defaults (
@@ -27,10 +69,29 @@ def register_defaults(
2769 event_bus : Callable [..., EventBus ],
2870 query_bus : Callable [..., QueryBus [Any ]],
2971 ) -> None :
72+ """
73+ Register the CQ buses as default providers in the DI container.
74+
75+ Called once during setup so that handlers and middlewares can
76+ declare ``CommandBus``, ``EventBus``, or ``QueryBus`` as
77+ constructor dependencies and receive the configured instances.
78+
79+ The default implementation is a no-op for adapters that do not
80+ need automatic bus registration.
81+ """
82+
3083 return
3184
3285 @abstractmethod
3386 def wire [T ](self , tp : type [T ]) -> Callable [..., Awaitable [T ]]:
87+ """
88+ Return an async factory that instantiates ``tp`` with injected
89+ dependencies.
90+
91+ Used internally to build handler instances whose dependencies are
92+ resolved by the container.
93+ """
94+
3495 raise NotImplementedError
3596
3697
0 commit comments