Skip to content

Commit 9c2075f

Browse files
author
remimd
committed
docstring
1 parent 69c6688 commit 9c2075f

2 files changed

Lines changed: 71 additions & 10 deletions

File tree

cq/_core/di.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,61 @@
66
from typing import TYPE_CHECKING, Any, AsyncContextManager, Protocol, runtime_checkable
77

88
if TYPE_CHECKING:
9-
from cq._core.message import CommandBus, EventBus, QueryBus
9+
from cq import CommandBus, EventBus, QueryBus
1010

1111

1212
@runtime_checkable
1313
class 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

uv.lock

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)