python-cq 0.9.1__tar.gz → 0.11.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_cq-0.9.1 → python_cq-0.11.0}/PKG-INFO +1 -1
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/__init__.py +9 -3
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/dispatcher/base.py +6 -1
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/dispatcher/bus.py +7 -2
- python_cq-0.11.0/cq/_core/dispatcher/lazy.py +28 -0
- python_cq-0.11.0/cq/_core/dispatcher/pipe.py +196 -0
- python_cq-0.11.0/cq/_core/pipetools.py +31 -0
- python_cq-0.11.0/cq/ext/fastapi.py +73 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/pyproject.toml +1 -1
- python_cq-0.9.1/cq/_core/defer.py +0 -10
- python_cq-0.9.1/cq/_core/dispatcher/pipe.py +0 -82
- python_cq-0.9.1/cq/ext/fastapi.py +0 -44
- python_cq-0.9.1/cq/ext/fastapi.pyi +0 -5
- {python_cq-0.9.1 → python_cq-0.11.0}/.gitignore +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/LICENSE +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/README.md +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/__init__.py +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/dispatcher/__init__.py +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/handler.py +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/message.py +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/middleware.py +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/related_events.py +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/_core/scope.py +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/exceptions.py +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/ext/__init__.py +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/middlewares/__init__.py +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/middlewares/retry.py +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/middlewares/scope.py +0 -0
- {python_cq-0.9.1 → python_cq-0.11.0}/cq/py.typed +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
from ._core.
|
|
1
|
+
from ._core.dispatcher.base import DeferredDispatcher, Dispatcher
|
|
2
2
|
from ._core.dispatcher.bus import Bus
|
|
3
|
-
from ._core.dispatcher.
|
|
3
|
+
from ._core.dispatcher.lazy import LazyDispatcher
|
|
4
|
+
from ._core.dispatcher.pipe import ContextPipeline, Pipe
|
|
4
5
|
from ._core.message import (
|
|
5
6
|
AnyCommandBus,
|
|
6
7
|
Command,
|
|
@@ -17,6 +18,7 @@ from ._core.message import (
|
|
|
17
18
|
query_handler,
|
|
18
19
|
)
|
|
19
20
|
from ._core.middleware import Middleware, MiddlewareResult
|
|
21
|
+
from ._core.pipetools import ContextCommandPipeline
|
|
20
22
|
from ._core.related_events import RelatedEvents
|
|
21
23
|
from ._core.scope import CQScope
|
|
22
24
|
|
|
@@ -26,9 +28,13 @@ __all__ = (
|
|
|
26
28
|
"CQScope",
|
|
27
29
|
"Command",
|
|
28
30
|
"CommandBus",
|
|
29
|
-
"
|
|
31
|
+
"ContextCommandPipeline",
|
|
32
|
+
"ContextPipeline",
|
|
33
|
+
"DeferredDispatcher",
|
|
34
|
+
"Dispatcher",
|
|
30
35
|
"Event",
|
|
31
36
|
"EventBus",
|
|
37
|
+
"LazyDispatcher",
|
|
32
38
|
"Middleware",
|
|
33
39
|
"MiddlewareResult",
|
|
34
40
|
"Pipe",
|
|
@@ -13,8 +13,13 @@ class Dispatcher[I, O](Protocol):
|
|
|
13
13
|
async def dispatch(self, input_value: I, /) -> O:
|
|
14
14
|
raise NotImplementedError
|
|
15
15
|
|
|
16
|
+
|
|
17
|
+
@runtime_checkable
|
|
18
|
+
class DeferredDispatcher[I](Protocol):
|
|
19
|
+
__slots__ = ()
|
|
20
|
+
|
|
16
21
|
@abstractmethod
|
|
17
|
-
def
|
|
22
|
+
async def defer(self, input_value: I, /) -> None:
|
|
18
23
|
raise NotImplementedError
|
|
19
24
|
|
|
20
25
|
|
|
@@ -12,6 +12,7 @@ from cq._core.handler import (
|
|
|
12
12
|
MultipleHandlerManager,
|
|
13
13
|
SingleHandlerManager,
|
|
14
14
|
)
|
|
15
|
+
from cq._core.middleware import Middleware
|
|
15
16
|
|
|
16
17
|
type Listener[T] = Callable[[T], Awaitable[Any]]
|
|
17
18
|
|
|
@@ -21,11 +22,15 @@ class Bus[I, O](Dispatcher[I, O], Protocol):
|
|
|
21
22
|
__slots__ = ()
|
|
22
23
|
|
|
23
24
|
@abstractmethod
|
|
24
|
-
def
|
|
25
|
+
def add_listeners(self, *listeners: Listener[I]) -> Self:
|
|
25
26
|
raise NotImplementedError
|
|
26
27
|
|
|
27
28
|
@abstractmethod
|
|
28
|
-
def
|
|
29
|
+
def add_middlewares(self, *middlewares: Middleware[[I], O]) -> Self:
|
|
30
|
+
raise NotImplementedError
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def subscribe(self, input_type: type[I], factory: HandlerFactory[[I], O]) -> Self:
|
|
29
34
|
raise NotImplementedError
|
|
30
35
|
|
|
31
36
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from collections.abc import Awaitable
|
|
2
|
+
from types import GenericAlias
|
|
3
|
+
from typing import TypeAliasType
|
|
4
|
+
|
|
5
|
+
import injection
|
|
6
|
+
|
|
7
|
+
from cq._core.dispatcher.base import Dispatcher
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class LazyDispatcher[I, O](Dispatcher[I, O]):
|
|
11
|
+
__slots__ = ("__value",)
|
|
12
|
+
|
|
13
|
+
__value: Awaitable[Dispatcher[I, O]]
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
dispatcher_type: type[Dispatcher[I, O]] | TypeAliasType | GenericAlias,
|
|
18
|
+
/,
|
|
19
|
+
*,
|
|
20
|
+
injection_module: injection.Module | None = None,
|
|
21
|
+
threadsafe: bool | None = None,
|
|
22
|
+
) -> None:
|
|
23
|
+
module = injection_module or injection.mod()
|
|
24
|
+
self.__value = module.aget_lazy_instance(dispatcher_type, threadsafe=threadsafe)
|
|
25
|
+
|
|
26
|
+
async def dispatch(self, input_value: I, /) -> O:
|
|
27
|
+
dispatcher = await self.__value
|
|
28
|
+
return await dispatcher.dispatch(input_value)
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
from collections import deque
|
|
2
|
+
from collections.abc import Awaitable, Callable
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any, Protocol, Self, overload
|
|
5
|
+
|
|
6
|
+
from cq._core.dispatcher.base import BaseDispatcher, Dispatcher
|
|
7
|
+
from cq._core.middleware import Middleware
|
|
8
|
+
|
|
9
|
+
type PipeConverter[I, O] = Callable[[O], Awaitable[I]]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PipeConverterMethod[I, O](Protocol):
|
|
13
|
+
def __get__(
|
|
14
|
+
self,
|
|
15
|
+
instance: object,
|
|
16
|
+
owner: type | None = ...,
|
|
17
|
+
) -> PipeConverter[I, O]: ...
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
21
|
+
class PipeStep[I, O]:
|
|
22
|
+
converter: PipeConverter[I, O]
|
|
23
|
+
dispatcher: Dispatcher[I, Any] | None = field(default=None)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Pipe[I, O](BaseDispatcher[I, O]):
|
|
27
|
+
__slots__ = ("__dispatcher", "__steps")
|
|
28
|
+
|
|
29
|
+
__dispatcher: Dispatcher[Any, Any]
|
|
30
|
+
__steps: list[PipeStep[Any, Any]]
|
|
31
|
+
|
|
32
|
+
def __init__(self, dispatcher: Dispatcher[Any, Any]) -> None:
|
|
33
|
+
super().__init__()
|
|
34
|
+
self.__dispatcher = dispatcher
|
|
35
|
+
self.__steps = []
|
|
36
|
+
|
|
37
|
+
@overload
|
|
38
|
+
def step[T](
|
|
39
|
+
self,
|
|
40
|
+
wrapped: PipeConverter[T, Any],
|
|
41
|
+
/,
|
|
42
|
+
*,
|
|
43
|
+
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
44
|
+
) -> PipeConverter[T, Any]: ...
|
|
45
|
+
|
|
46
|
+
@overload
|
|
47
|
+
def step[T](
|
|
48
|
+
self,
|
|
49
|
+
wrapped: None = ...,
|
|
50
|
+
/,
|
|
51
|
+
*,
|
|
52
|
+
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
53
|
+
) -> Callable[[PipeConverter[T, Any]], PipeConverter[T, Any]]: ...
|
|
54
|
+
|
|
55
|
+
def step[T](
|
|
56
|
+
self,
|
|
57
|
+
wrapped: PipeConverter[T, Any] | None = None,
|
|
58
|
+
/,
|
|
59
|
+
*,
|
|
60
|
+
dispatcher: Dispatcher[T, Any] | None = None,
|
|
61
|
+
) -> Any:
|
|
62
|
+
def decorator(wp: PipeConverter[T, Any]) -> PipeConverter[T, Any]:
|
|
63
|
+
step = PipeStep(wp, dispatcher)
|
|
64
|
+
self.__steps.append(step)
|
|
65
|
+
return wp
|
|
66
|
+
|
|
67
|
+
return decorator(wrapped) if wrapped else decorator
|
|
68
|
+
|
|
69
|
+
def add_static_step[T](
|
|
70
|
+
self,
|
|
71
|
+
input_value: T,
|
|
72
|
+
*,
|
|
73
|
+
dispatcher: Dispatcher[T, Any] | None = None,
|
|
74
|
+
) -> Self:
|
|
75
|
+
@self.step(dispatcher=dispatcher)
|
|
76
|
+
async def converter(_: Any) -> T:
|
|
77
|
+
return input_value
|
|
78
|
+
|
|
79
|
+
return self
|
|
80
|
+
|
|
81
|
+
async def dispatch(self, input_value: I, /) -> O:
|
|
82
|
+
return await self._invoke_with_middlewares(self.__execute, input_value)
|
|
83
|
+
|
|
84
|
+
async def __execute(self, input_value: I) -> O:
|
|
85
|
+
dispatcher = self.__dispatcher
|
|
86
|
+
|
|
87
|
+
for step in self.__steps:
|
|
88
|
+
output_value = await dispatcher.dispatch(input_value)
|
|
89
|
+
input_value = await step.converter(output_value)
|
|
90
|
+
|
|
91
|
+
if input_value is None:
|
|
92
|
+
return NotImplemented
|
|
93
|
+
|
|
94
|
+
dispatcher = step.dispatcher or self.__dispatcher
|
|
95
|
+
|
|
96
|
+
return await dispatcher.dispatch(input_value)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
100
|
+
class ContextPipelineStep[I, O]:
|
|
101
|
+
converter: PipeConverterMethod[I, O]
|
|
102
|
+
dispatcher: Dispatcher[I, Any] | None = field(default=None)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class ContextPipeline[I]:
|
|
106
|
+
__slots__ = ("__dispatcher", "__middlewares", "__steps")
|
|
107
|
+
|
|
108
|
+
__dispatcher: Dispatcher[Any, Any]
|
|
109
|
+
__middlewares: deque[Middleware[Any, Any]]
|
|
110
|
+
__steps: list[ContextPipelineStep[Any, Any]]
|
|
111
|
+
|
|
112
|
+
def __init__(self, dispatcher: Dispatcher[Any, Any]) -> None:
|
|
113
|
+
self.__dispatcher = dispatcher
|
|
114
|
+
self.__middlewares = deque()
|
|
115
|
+
self.__steps = []
|
|
116
|
+
|
|
117
|
+
@overload
|
|
118
|
+
def __get__[O](
|
|
119
|
+
self,
|
|
120
|
+
instance: O,
|
|
121
|
+
owner: type[O] | None = ...,
|
|
122
|
+
) -> Dispatcher[I, O]: ...
|
|
123
|
+
|
|
124
|
+
@overload
|
|
125
|
+
def __get__(self, instance: None = ..., owner: type | None = ...) -> Self: ...
|
|
126
|
+
|
|
127
|
+
def __get__[O](
|
|
128
|
+
self,
|
|
129
|
+
instance: O | None = None,
|
|
130
|
+
owner: type[O] | None = None,
|
|
131
|
+
) -> Self | Dispatcher[I, O]:
|
|
132
|
+
if instance is None:
|
|
133
|
+
return self
|
|
134
|
+
|
|
135
|
+
pipeline = self.__new_pipeline(instance, owner)
|
|
136
|
+
return BoundContextPipeline(instance, pipeline)
|
|
137
|
+
|
|
138
|
+
def add_middlewares(self, *middlewares: Middleware[[I], Any]) -> Self:
|
|
139
|
+
self.__middlewares.extendleft(reversed(middlewares))
|
|
140
|
+
return self
|
|
141
|
+
|
|
142
|
+
@overload
|
|
143
|
+
def step[T](
|
|
144
|
+
self,
|
|
145
|
+
wrapped: PipeConverterMethod[T, Any],
|
|
146
|
+
/,
|
|
147
|
+
*,
|
|
148
|
+
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
149
|
+
) -> PipeConverterMethod[T, Any]: ...
|
|
150
|
+
|
|
151
|
+
@overload
|
|
152
|
+
def step[T](
|
|
153
|
+
self,
|
|
154
|
+
wrapped: None = ...,
|
|
155
|
+
/,
|
|
156
|
+
*,
|
|
157
|
+
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
158
|
+
) -> Callable[[PipeConverterMethod[T, Any]], PipeConverterMethod[T, Any]]: ...
|
|
159
|
+
|
|
160
|
+
def step[T](
|
|
161
|
+
self,
|
|
162
|
+
wrapped: PipeConverterMethod[T, Any] | None = None,
|
|
163
|
+
/,
|
|
164
|
+
*,
|
|
165
|
+
dispatcher: Dispatcher[T, Any] | None = None,
|
|
166
|
+
) -> Any:
|
|
167
|
+
def decorator(wp: PipeConverterMethod[T, Any]) -> PipeConverterMethod[T, Any]:
|
|
168
|
+
step = ContextPipelineStep(wp, dispatcher)
|
|
169
|
+
self.__steps.append(step)
|
|
170
|
+
return wp
|
|
171
|
+
|
|
172
|
+
return decorator(wrapped) if wrapped else decorator
|
|
173
|
+
|
|
174
|
+
def __new_pipeline[T](
|
|
175
|
+
self,
|
|
176
|
+
context: T,
|
|
177
|
+
context_type: type[T] | None,
|
|
178
|
+
) -> Pipe[I, Any]:
|
|
179
|
+
pipeline: Pipe[I, Any] = Pipe(self.__dispatcher)
|
|
180
|
+
pipeline.add_middlewares(*self.__middlewares)
|
|
181
|
+
|
|
182
|
+
for step in self.__steps:
|
|
183
|
+
converter = step.converter.__get__(context, context_type)
|
|
184
|
+
pipeline.step(converter, dispatcher=step.dispatcher)
|
|
185
|
+
|
|
186
|
+
return pipeline
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
190
|
+
class BoundContextPipeline[I, O](Dispatcher[I, O]):
|
|
191
|
+
context: O
|
|
192
|
+
pipeline: Pipe[I, Any]
|
|
193
|
+
|
|
194
|
+
async def dispatch(self, input_value: I, /) -> O:
|
|
195
|
+
await self.pipeline.dispatch(input_value)
|
|
196
|
+
return self.context
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import injection
|
|
2
|
+
|
|
3
|
+
from cq._core.dispatcher.lazy import LazyDispatcher
|
|
4
|
+
from cq._core.dispatcher.pipe import ContextPipeline
|
|
5
|
+
from cq._core.message import AnyCommandBus, Command
|
|
6
|
+
from cq._core.scope import CQScope
|
|
7
|
+
from cq.middlewares.scope import InjectionScopeMiddleware
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ContextCommandPipeline[I: Command](ContextPipeline[I]):
|
|
11
|
+
__slots__ = ()
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
/,
|
|
16
|
+
*,
|
|
17
|
+
injection_module: injection.Module | None = None,
|
|
18
|
+
threadsafe: bool | None = None,
|
|
19
|
+
) -> None:
|
|
20
|
+
dispatcher = LazyDispatcher(
|
|
21
|
+
AnyCommandBus,
|
|
22
|
+
injection_module=injection_module,
|
|
23
|
+
threadsafe=threadsafe,
|
|
24
|
+
)
|
|
25
|
+
super().__init__(dispatcher)
|
|
26
|
+
transaction_scope_middleware = InjectionScopeMiddleware(
|
|
27
|
+
CQScope.TRANSACTION,
|
|
28
|
+
exist_ok=True,
|
|
29
|
+
threadsafe=threadsafe,
|
|
30
|
+
)
|
|
31
|
+
self.add_middlewares(transaction_scope_middleware)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import TYPE_CHECKING, Annotated, Any
|
|
3
|
+
|
|
4
|
+
from fastapi import BackgroundTasks, Depends
|
|
5
|
+
from injection.ext.fastapi import Inject
|
|
6
|
+
|
|
7
|
+
from cq import (
|
|
8
|
+
Command,
|
|
9
|
+
CommandBus,
|
|
10
|
+
DeferredDispatcher,
|
|
11
|
+
Dispatcher,
|
|
12
|
+
Event,
|
|
13
|
+
EventBus,
|
|
14
|
+
Query,
|
|
15
|
+
QueryBus,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = (
|
|
19
|
+
"DeferredCommandBus",
|
|
20
|
+
"DeferredEventBus",
|
|
21
|
+
"DeferredQueryBus",
|
|
22
|
+
"FastAPIDeferredDispatcher",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
27
|
+
class FastAPIDeferredDispatcher[I](DeferredDispatcher[I]):
|
|
28
|
+
background_tasks: BackgroundTasks
|
|
29
|
+
dispatcher: Dispatcher[I, Any]
|
|
30
|
+
|
|
31
|
+
async def defer(self, input_value: I, /) -> None:
|
|
32
|
+
self.background_tasks.add_task(self.dispatcher.dispatch, input_value)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def new_deferred_command_bus[T](
|
|
36
|
+
background_tasks: BackgroundTasks,
|
|
37
|
+
command_bus: Inject[CommandBus[T]],
|
|
38
|
+
) -> DeferredDispatcher[Command]:
|
|
39
|
+
return FastAPIDeferredDispatcher(background_tasks, command_bus)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def new_deferred_event_bus(
|
|
43
|
+
background_tasks: BackgroundTasks,
|
|
44
|
+
event_bus: Inject[EventBus],
|
|
45
|
+
) -> DeferredDispatcher[Event]:
|
|
46
|
+
return FastAPIDeferredDispatcher(background_tasks, event_bus)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def new_deferred_query_bus[T](
|
|
50
|
+
background_tasks: BackgroundTasks,
|
|
51
|
+
query_bus: Inject[QueryBus[T]],
|
|
52
|
+
) -> DeferredDispatcher[Query]:
|
|
53
|
+
return FastAPIDeferredDispatcher(background_tasks, query_bus)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if TYPE_CHECKING:
|
|
57
|
+
type DeferredCommandBus = DeferredDispatcher[Command]
|
|
58
|
+
type DeferredEventBus = DeferredDispatcher[Event]
|
|
59
|
+
type DeferredQueryBus = DeferredDispatcher[Query]
|
|
60
|
+
|
|
61
|
+
else:
|
|
62
|
+
DeferredCommandBus = Annotated[
|
|
63
|
+
DeferredDispatcher[Command],
|
|
64
|
+
Depends(new_deferred_command_bus, use_cache=False),
|
|
65
|
+
]
|
|
66
|
+
DeferredEventBus = Annotated[
|
|
67
|
+
DeferredDispatcher[Event],
|
|
68
|
+
Depends(new_deferred_event_bus, use_cache=False),
|
|
69
|
+
]
|
|
70
|
+
DeferredQueryBus = Annotated[
|
|
71
|
+
DeferredDispatcher[Query],
|
|
72
|
+
Depends(new_deferred_query_bus, use_cache=False),
|
|
73
|
+
]
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
from collections.abc import Awaitable, Callable
|
|
2
|
-
from dataclasses import dataclass, field
|
|
3
|
-
from typing import Any, Self, overload
|
|
4
|
-
|
|
5
|
-
from cq._core.dispatcher.base import BaseDispatcher, Dispatcher
|
|
6
|
-
|
|
7
|
-
type PipeConverter[I, O] = Callable[[O], Awaitable[I]]
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
11
|
-
class PipeStep[I, O]:
|
|
12
|
-
converter: PipeConverter[I, O]
|
|
13
|
-
dispatcher: Dispatcher[I, Any] | None = field(default=None)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class Pipe[I, O](BaseDispatcher[I, O]):
|
|
17
|
-
__slots__ = ("__dispatcher", "__steps")
|
|
18
|
-
|
|
19
|
-
__dispatcher: Dispatcher[Any, Any]
|
|
20
|
-
__steps: list[PipeStep[Any, Any]]
|
|
21
|
-
|
|
22
|
-
def __init__(self, dispatcher: Dispatcher[Any, Any]) -> None:
|
|
23
|
-
super().__init__()
|
|
24
|
-
self.__dispatcher = dispatcher
|
|
25
|
-
self.__steps = []
|
|
26
|
-
|
|
27
|
-
@overload
|
|
28
|
-
def step[T](
|
|
29
|
-
self,
|
|
30
|
-
wrapped: PipeConverter[T, Any],
|
|
31
|
-
/,
|
|
32
|
-
*,
|
|
33
|
-
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
34
|
-
) -> PipeConverter[T, Any]: ...
|
|
35
|
-
|
|
36
|
-
@overload
|
|
37
|
-
def step[T](
|
|
38
|
-
self,
|
|
39
|
-
wrapped: None = ...,
|
|
40
|
-
/,
|
|
41
|
-
*,
|
|
42
|
-
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
43
|
-
) -> Callable[[PipeConverter[T, Any]], PipeConverter[T, Any]]: ...
|
|
44
|
-
|
|
45
|
-
def step[T](
|
|
46
|
-
self,
|
|
47
|
-
wrapped: PipeConverter[T, Any] | None = None,
|
|
48
|
-
/,
|
|
49
|
-
*,
|
|
50
|
-
dispatcher: Dispatcher[T, Any] | None = None,
|
|
51
|
-
) -> Any:
|
|
52
|
-
def decorator(wp: PipeConverter[T, Any]) -> PipeConverter[T, Any]:
|
|
53
|
-
step = PipeStep(wp, dispatcher)
|
|
54
|
-
self.__steps.append(step)
|
|
55
|
-
return wp
|
|
56
|
-
|
|
57
|
-
return decorator(wrapped) if wrapped else decorator
|
|
58
|
-
|
|
59
|
-
def add_static_step[T](
|
|
60
|
-
self,
|
|
61
|
-
input_value: T,
|
|
62
|
-
*,
|
|
63
|
-
dispatcher: Dispatcher[T, Any] | None = None,
|
|
64
|
-
) -> Self:
|
|
65
|
-
@self.step(dispatcher=dispatcher)
|
|
66
|
-
async def converter(_: Any) -> T:
|
|
67
|
-
return input_value
|
|
68
|
-
|
|
69
|
-
return self
|
|
70
|
-
|
|
71
|
-
async def dispatch(self, input_value: I, /) -> O:
|
|
72
|
-
return await self._invoke_with_middlewares(self.__execute, input_value)
|
|
73
|
-
|
|
74
|
-
async def __execute(self, input_value: I) -> O:
|
|
75
|
-
dispatcher = self.__dispatcher
|
|
76
|
-
|
|
77
|
-
for step in self.__steps:
|
|
78
|
-
output_value = await dispatcher.dispatch(input_value)
|
|
79
|
-
input_value = await step.converter(output_value)
|
|
80
|
-
dispatcher = step.dispatcher or self.__dispatcher
|
|
81
|
-
|
|
82
|
-
return await dispatcher.dispatch(input_value)
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from typing import Annotated, Any
|
|
3
|
-
|
|
4
|
-
from fastapi import BackgroundTasks, Depends
|
|
5
|
-
from injection.ext.fastapi import Inject
|
|
6
|
-
|
|
7
|
-
from cq import Bus, Command, CommandBus, DeferredBus, Event, EventBus, Query, QueryBus
|
|
8
|
-
|
|
9
|
-
__all__ = ("DeferredCommandBus", "DeferredEventBus", "DeferredQueryBus")
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
13
|
-
class FastAPIDeferredBus[I](DeferredBus[I]):
|
|
14
|
-
background_tasks: BackgroundTasks
|
|
15
|
-
bus: Bus[I, Any]
|
|
16
|
-
|
|
17
|
-
async def defer(self, input_value: I, /) -> None:
|
|
18
|
-
self.background_tasks.add_task(self.bus.dispatch, input_value)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
async def new_deferred_command_bus[T](
|
|
22
|
-
background_tasks: BackgroundTasks,
|
|
23
|
-
command_bus: Inject[CommandBus[T]],
|
|
24
|
-
) -> DeferredBus[Command]:
|
|
25
|
-
return FastAPIDeferredBus(background_tasks, command_bus)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
async def new_deferred_event_bus(
|
|
29
|
-
background_tasks: BackgroundTasks,
|
|
30
|
-
event_bus: Inject[EventBus],
|
|
31
|
-
) -> DeferredBus[Event]:
|
|
32
|
-
return FastAPIDeferredBus(background_tasks, event_bus)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
async def new_deferred_query_bus[T](
|
|
36
|
-
background_tasks: BackgroundTasks,
|
|
37
|
-
query_bus: Inject[QueryBus[T]],
|
|
38
|
-
) -> DeferredBus[Query]:
|
|
39
|
-
return FastAPIDeferredBus(background_tasks, query_bus)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
DeferredCommandBus = Annotated[DeferredBus[Command], Depends(new_deferred_command_bus)]
|
|
43
|
-
DeferredEventBus = Annotated[DeferredBus[Event], Depends(new_deferred_event_bus)]
|
|
44
|
-
DeferredQueryBus = Annotated[DeferredBus[Query], Depends(new_deferred_query_bus)]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|