python-cq 0.14.0__tar.gz → 0.14.1__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.14.0 → python_cq-0.14.1}/PKG-INFO +1 -1
- python_cq-0.14.1/cq/_core/common/typing.py +21 -0
- python_cq-0.14.1/cq/_core/dispatcher/pipe.py +312 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/_core/handler.py +4 -6
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/_core/message.py +1 -1
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/_core/middleware.py +1 -1
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/_core/pipetools.py +17 -8
- python_cq-0.14.1/cq/py.typed +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/pyproject.toml +1 -1
- python_cq-0.14.0/cq/_core/dispatcher/pipe.py +0 -210
- {python_cq-0.14.0 → python_cq-0.14.1}/.gitignore +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/LICENSE +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/__init__.py +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/_core/__init__.py +0 -0
- {python_cq-0.14.0/cq/_core/dispatcher → python_cq-0.14.1/cq/_core/common}/__init__.py +0 -0
- {python_cq-0.14.0/cq/ext → python_cq-0.14.1/cq/_core/dispatcher}/__init__.py +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/_core/dispatcher/base.py +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/_core/dispatcher/bus.py +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/_core/dispatcher/lazy.py +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/_core/related_events.py +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/_core/scope.py +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/exceptions.py +0 -0
- {python_cq-0.14.0/cq/middlewares → python_cq-0.14.1/cq/ext}/__init__.py +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/ext/fastapi.py +0 -0
- /python_cq-0.14.0/cq/py.typed → /python_cq-0.14.1/cq/middlewares/__init__.py +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/middlewares/retry.py +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/cq/middlewares/scope.py +0 -0
- {python_cq-0.14.0 → python_cq-0.14.1}/docs/index.md +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from typing import Any, Protocol, overload
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Decorator(Protocol):
|
|
6
|
+
def __call__[T](self, wrapped: T, /) -> T: ...
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Method[**P, T](Protocol):
|
|
10
|
+
@overload
|
|
11
|
+
def __call__(self, instance: Any, /, *args: P.args, **kwargs: P.kwargs) -> T: ...
|
|
12
|
+
|
|
13
|
+
@overload
|
|
14
|
+
def __call__(self, /, *args: Any, **kwargs: Any) -> T: ...
|
|
15
|
+
|
|
16
|
+
def __get__(
|
|
17
|
+
self,
|
|
18
|
+
instance: object,
|
|
19
|
+
owner: type | None = ...,
|
|
20
|
+
/,
|
|
21
|
+
) -> Callable[P, T]: ...
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from collections.abc import Awaitable, Callable
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from functools import partial
|
|
5
|
+
from inspect import iscoroutinefunction
|
|
6
|
+
from typing import (
|
|
7
|
+
TYPE_CHECKING,
|
|
8
|
+
Any,
|
|
9
|
+
Concatenate,
|
|
10
|
+
Protocol,
|
|
11
|
+
Self,
|
|
12
|
+
overload,
|
|
13
|
+
runtime_checkable,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from cq._core.common.typing import Decorator, Method
|
|
17
|
+
from cq._core.dispatcher.base import BaseDispatcher, Dispatcher
|
|
18
|
+
from cq._core.middleware import Middleware, MiddlewareGroup
|
|
19
|
+
|
|
20
|
+
type ConvertAsync[**P, I, O] = Callable[Concatenate[O, P], Awaitable[I]]
|
|
21
|
+
type ConvertSync[**P, I, O] = Callable[Concatenate[O, P], I]
|
|
22
|
+
type Convert[**P, I, O] = ConvertAsync[P, I, O] | ConvertSync[P, I, O]
|
|
23
|
+
|
|
24
|
+
type ConvertMethodAsync[I, O] = Method[[O], Awaitable[I]]
|
|
25
|
+
type ConvertMethodSync[I, O] = Method[[O], I]
|
|
26
|
+
type ConvertMethod[I, O] = ConvertMethodAsync[I, O] | ConvertMethodSync[I, O]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@runtime_checkable
|
|
30
|
+
class PipelineConverter[**P, I, O](Protocol):
|
|
31
|
+
__slots__ = ()
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
async def convert(self, output_value: O, /, *args: P.args, **kwargs: P.kwargs) -> I:
|
|
35
|
+
raise NotImplementedError
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
39
|
+
class PipelineStep[**P, I, O]:
|
|
40
|
+
converter: PipelineConverter[P, I, O]
|
|
41
|
+
dispatcher: Dispatcher[I, Any] | None = field(default=None)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
45
|
+
class PipelineSteps[**P, I, O]:
|
|
46
|
+
default_dispatcher: Dispatcher[Any, Any]
|
|
47
|
+
__steps: list[PipelineStep[P, Any, Any]] = field(default_factory=list, init=False)
|
|
48
|
+
|
|
49
|
+
def add[T](
|
|
50
|
+
self,
|
|
51
|
+
converter: PipelineConverter[P, T, Any],
|
|
52
|
+
dispatcher: Dispatcher[T, Any] | None,
|
|
53
|
+
) -> Self:
|
|
54
|
+
self.__steps.append(PipelineStep(converter, dispatcher))
|
|
55
|
+
return self
|
|
56
|
+
|
|
57
|
+
async def execute(self, input_value: I, /, *args: P.args, **kwargs: P.kwargs) -> O:
|
|
58
|
+
dispatcher = self.default_dispatcher
|
|
59
|
+
|
|
60
|
+
for step in self.__steps:
|
|
61
|
+
output_value = await dispatcher.dispatch(input_value)
|
|
62
|
+
input_value = await step.converter.convert(output_value, *args, **kwargs)
|
|
63
|
+
|
|
64
|
+
if input_value is None:
|
|
65
|
+
return NotImplemented
|
|
66
|
+
|
|
67
|
+
dispatcher = step.dispatcher or self.default_dispatcher
|
|
68
|
+
|
|
69
|
+
return await dispatcher.dispatch(input_value)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class Pipe[I, O](BaseDispatcher[I, O]):
|
|
73
|
+
__slots__ = ("__steps",)
|
|
74
|
+
|
|
75
|
+
__steps: PipelineSteps[[], I, O]
|
|
76
|
+
|
|
77
|
+
def __init__(self, dispatcher: Dispatcher[Any, Any]) -> None:
|
|
78
|
+
super().__init__()
|
|
79
|
+
self.__steps = PipelineSteps(dispatcher)
|
|
80
|
+
|
|
81
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
82
|
+
|
|
83
|
+
@overload
|
|
84
|
+
def step[T](
|
|
85
|
+
self,
|
|
86
|
+
wrapped: ConvertAsync[[], T, Any],
|
|
87
|
+
/,
|
|
88
|
+
*,
|
|
89
|
+
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
90
|
+
) -> ConvertAsync[[], T, Any]: ...
|
|
91
|
+
|
|
92
|
+
@overload
|
|
93
|
+
def step[T](
|
|
94
|
+
self,
|
|
95
|
+
wrapped: ConvertSync[[], T, Any],
|
|
96
|
+
/,
|
|
97
|
+
*,
|
|
98
|
+
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
99
|
+
) -> ConvertSync[[], T, Any]: ...
|
|
100
|
+
|
|
101
|
+
@overload
|
|
102
|
+
def step(
|
|
103
|
+
self,
|
|
104
|
+
wrapped: None = ...,
|
|
105
|
+
/,
|
|
106
|
+
*,
|
|
107
|
+
dispatcher: Dispatcher[Any, Any] | None = ...,
|
|
108
|
+
) -> Decorator: ...
|
|
109
|
+
|
|
110
|
+
def step[T](
|
|
111
|
+
self,
|
|
112
|
+
wrapped: Convert[[], T, Any] | None = None,
|
|
113
|
+
/,
|
|
114
|
+
*,
|
|
115
|
+
dispatcher: Dispatcher[T, Any] | None = None,
|
|
116
|
+
) -> Any:
|
|
117
|
+
def decorator(wp: Convert[[], T, Any]) -> Convert[[], T, Any]:
|
|
118
|
+
converter = (
|
|
119
|
+
_AsyncPipelineConverter(wp)
|
|
120
|
+
if iscoroutinefunction(wp)
|
|
121
|
+
else _SyncPipelineConverter(wp)
|
|
122
|
+
)
|
|
123
|
+
self.__steps.add(converter, dispatcher)
|
|
124
|
+
return wp
|
|
125
|
+
|
|
126
|
+
return decorator(wrapped) if wrapped else decorator
|
|
127
|
+
|
|
128
|
+
def add_static_step[T](
|
|
129
|
+
self,
|
|
130
|
+
input_value: T,
|
|
131
|
+
*,
|
|
132
|
+
dispatcher: Dispatcher[T, Any] | None = None,
|
|
133
|
+
) -> Self:
|
|
134
|
+
converter = _StaticPipelineConverter(input_value)
|
|
135
|
+
self.__steps.add(converter, dispatcher)
|
|
136
|
+
return self
|
|
137
|
+
|
|
138
|
+
async def dispatch(self, input_value: I, /) -> O:
|
|
139
|
+
return await self._invoke_with_middlewares(self.__steps.execute, input_value)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class ContextPipeline[I]:
|
|
143
|
+
__slots__ = ("__middleware_group", "__steps")
|
|
144
|
+
|
|
145
|
+
__middleware_group: MiddlewareGroup[[I], Any]
|
|
146
|
+
__steps: PipelineSteps[[object, type | None], I, Any]
|
|
147
|
+
|
|
148
|
+
def __init__(self, dispatcher: Dispatcher[Any, Any]) -> None:
|
|
149
|
+
self.__middleware_group = MiddlewareGroup()
|
|
150
|
+
self.__steps = PipelineSteps(dispatcher)
|
|
151
|
+
|
|
152
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
153
|
+
|
|
154
|
+
@overload
|
|
155
|
+
def __get__[O](self, instance: None, owner: type[O], /) -> Dispatcher[I, O]: ...
|
|
156
|
+
|
|
157
|
+
@overload
|
|
158
|
+
def __get__[O](
|
|
159
|
+
self,
|
|
160
|
+
instance: O,
|
|
161
|
+
owner: type[O] | None = ...,
|
|
162
|
+
/,
|
|
163
|
+
) -> Dispatcher[I, O]: ...
|
|
164
|
+
|
|
165
|
+
@overload
|
|
166
|
+
def __get__(self, instance: None = ..., owner: None = ..., /) -> Self: ...
|
|
167
|
+
|
|
168
|
+
def __get__[O](
|
|
169
|
+
self,
|
|
170
|
+
instance: O | None = None,
|
|
171
|
+
owner: type[O] | None = None,
|
|
172
|
+
/,
|
|
173
|
+
) -> Self | Dispatcher[I, O]:
|
|
174
|
+
if instance is None:
|
|
175
|
+
if owner is None:
|
|
176
|
+
return self
|
|
177
|
+
|
|
178
|
+
instance = owner()
|
|
179
|
+
|
|
180
|
+
dispatch_method = partial(self.__execute, context=instance, context_type=owner)
|
|
181
|
+
return BoundContextPipeline(dispatch_method)
|
|
182
|
+
|
|
183
|
+
def add_middlewares(self, *middlewares: Middleware[[I], Any]) -> Self:
|
|
184
|
+
self.__middleware_group.add(*middlewares)
|
|
185
|
+
return self
|
|
186
|
+
|
|
187
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
188
|
+
|
|
189
|
+
@overload
|
|
190
|
+
def step[T](
|
|
191
|
+
self,
|
|
192
|
+
wrapped: ConvertMethodAsync[T, Any],
|
|
193
|
+
/,
|
|
194
|
+
*,
|
|
195
|
+
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
196
|
+
) -> ConvertMethodAsync[T, Any]: ...
|
|
197
|
+
|
|
198
|
+
@overload
|
|
199
|
+
def step[T](
|
|
200
|
+
self,
|
|
201
|
+
wrapped: ConvertMethodSync[T, Any],
|
|
202
|
+
/,
|
|
203
|
+
*,
|
|
204
|
+
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
205
|
+
) -> ConvertMethodSync[T, Any]: ...
|
|
206
|
+
|
|
207
|
+
@overload
|
|
208
|
+
def step(
|
|
209
|
+
self,
|
|
210
|
+
wrapped: None = ...,
|
|
211
|
+
/,
|
|
212
|
+
*,
|
|
213
|
+
dispatcher: Dispatcher[Any, Any] | None = ...,
|
|
214
|
+
) -> Decorator: ...
|
|
215
|
+
|
|
216
|
+
def step[T](
|
|
217
|
+
self,
|
|
218
|
+
wrapped: ConvertMethod[T, Any] | None = None,
|
|
219
|
+
/,
|
|
220
|
+
*,
|
|
221
|
+
dispatcher: Dispatcher[T, Any] | None = None,
|
|
222
|
+
) -> Any:
|
|
223
|
+
def decorator(wp: ConvertMethod[T, Any]) -> ConvertMethod[T, Any]:
|
|
224
|
+
converter = (
|
|
225
|
+
_AsyncContextPipelineConverter(wp)
|
|
226
|
+
if iscoroutinefunction(wp)
|
|
227
|
+
else _SyncContextPipelineConverter(wp)
|
|
228
|
+
)
|
|
229
|
+
self.__steps.add(converter, dispatcher)
|
|
230
|
+
return wp
|
|
231
|
+
|
|
232
|
+
return decorator(wrapped) if wrapped else decorator
|
|
233
|
+
|
|
234
|
+
async def __execute[O](
|
|
235
|
+
self,
|
|
236
|
+
input_value: I,
|
|
237
|
+
/,
|
|
238
|
+
*,
|
|
239
|
+
context: O,
|
|
240
|
+
context_type: type[O] | None,
|
|
241
|
+
) -> O:
|
|
242
|
+
await self.__middleware_group.invoke(
|
|
243
|
+
lambda i: self.__steps.execute(i, context, context_type),
|
|
244
|
+
input_value,
|
|
245
|
+
)
|
|
246
|
+
return context
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
250
|
+
class BoundContextPipeline[I, O](Dispatcher[I, O]):
|
|
251
|
+
dispatch_method: Callable[[I], Awaitable[O]]
|
|
252
|
+
|
|
253
|
+
async def dispatch(self, input_value: I, /) -> O:
|
|
254
|
+
return await self.dispatch_method(input_value)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
258
|
+
class _AsyncPipelineConverter[**P, I, O](PipelineConverter[P, I, O]):
|
|
259
|
+
converter: ConvertAsync[P, I, O]
|
|
260
|
+
|
|
261
|
+
async def convert(self, output_value: O, /, *args: P.args, **kwargs: P.kwargs) -> I:
|
|
262
|
+
return await self.converter(output_value, *args, **kwargs)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
266
|
+
class _SyncPipelineConverter[**P, I, O](PipelineConverter[P, I, O]):
|
|
267
|
+
converter: ConvertSync[P, I, O]
|
|
268
|
+
|
|
269
|
+
async def convert(self, output_value: O, /, *args: P.args, **kwargs: P.kwargs) -> I:
|
|
270
|
+
return self.converter(output_value, *args, **kwargs)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
274
|
+
class _StaticPipelineConverter[I](PipelineConverter[..., I, Any]):
|
|
275
|
+
input_value: I
|
|
276
|
+
|
|
277
|
+
async def convert(self, output_value: Any, /, *args: Any, **kwargs: Any) -> I:
|
|
278
|
+
return self.input_value
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
282
|
+
class _AsyncContextPipelineConverter[I, O](
|
|
283
|
+
PipelineConverter[[object, type | None], I, O],
|
|
284
|
+
):
|
|
285
|
+
converter: ConvertMethodAsync[I, O]
|
|
286
|
+
|
|
287
|
+
async def convert(
|
|
288
|
+
self,
|
|
289
|
+
output_value: O,
|
|
290
|
+
/,
|
|
291
|
+
context: object,
|
|
292
|
+
context_type: type | None,
|
|
293
|
+
) -> I:
|
|
294
|
+
method = self.converter.__get__(context, context_type)
|
|
295
|
+
return await method(output_value)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
299
|
+
class _SyncContextPipelineConverter[I, O](
|
|
300
|
+
PipelineConverter[[object, type | None], I, O],
|
|
301
|
+
):
|
|
302
|
+
converter: ConvertMethodSync[I, O]
|
|
303
|
+
|
|
304
|
+
async def convert(
|
|
305
|
+
self,
|
|
306
|
+
output_value: O,
|
|
307
|
+
/,
|
|
308
|
+
context: object,
|
|
309
|
+
context_type: type | None,
|
|
310
|
+
) -> I:
|
|
311
|
+
method = self.converter.__get__(context, context_type)
|
|
312
|
+
return method(output_value)
|
|
@@ -10,6 +10,8 @@ from typing import TYPE_CHECKING, Any, Protocol, Self, overload, runtime_checkab
|
|
|
10
10
|
import injection
|
|
11
11
|
from type_analyzer import MatchingTypesConfig, iter_matching_types, matching_types
|
|
12
12
|
|
|
13
|
+
from cq._core.common.typing import Decorator
|
|
14
|
+
|
|
13
15
|
type HandlerType[**P, T] = type[Handler[P, T]]
|
|
14
16
|
type HandlerFactory[**P, T] = Callable[..., Awaitable[Handler[P, T]]]
|
|
15
17
|
|
|
@@ -90,10 +92,6 @@ class SingleHandlerRegistry[I, O](HandlerRegistry[I, O]):
|
|
|
90
92
|
return self
|
|
91
93
|
|
|
92
94
|
|
|
93
|
-
class _Decorator(Protocol):
|
|
94
|
-
def __call__[T](self, wrapped: T, /) -> T: ...
|
|
95
|
-
|
|
96
|
-
|
|
97
95
|
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
98
96
|
class HandlerDecorator[I, O]:
|
|
99
97
|
registry: HandlerRegistry[I, O]
|
|
@@ -108,7 +106,7 @@ class HandlerDecorator[I, O]:
|
|
|
108
106
|
/,
|
|
109
107
|
*,
|
|
110
108
|
threadsafe: bool | None = ...,
|
|
111
|
-
) ->
|
|
109
|
+
) -> Decorator: ...
|
|
112
110
|
|
|
113
111
|
@overload
|
|
114
112
|
def __call__[T](
|
|
@@ -126,7 +124,7 @@ class HandlerDecorator[I, O]:
|
|
|
126
124
|
/,
|
|
127
125
|
*,
|
|
128
126
|
threadsafe: bool | None = ...,
|
|
129
|
-
) ->
|
|
127
|
+
) -> Decorator: ...
|
|
130
128
|
|
|
131
129
|
def __call__[T](
|
|
132
130
|
self,
|
|
@@ -26,7 +26,7 @@ AnyCommandBus = CommandBus[Any]
|
|
|
26
26
|
command_handler: Final[HandlerDecorator[Command, Any]] = HandlerDecorator(
|
|
27
27
|
SingleHandlerRegistry(),
|
|
28
28
|
)
|
|
29
|
-
event_handler: Final[HandlerDecorator[Event,
|
|
29
|
+
event_handler: Final[HandlerDecorator[Event, Any]] = HandlerDecorator(
|
|
30
30
|
MultipleHandlerRegistry(),
|
|
31
31
|
)
|
|
32
32
|
query_handler: Final[HandlerDecorator[Query, Any]] = HandlerDecorator(
|
|
@@ -59,7 +59,7 @@ class _BoundMiddleware[**P, T]:
|
|
|
59
59
|
call_next: Callable[P, Awaitable[T]]
|
|
60
60
|
middleware: ClassicMiddleware[P, T]
|
|
61
61
|
|
|
62
|
-
async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
62
|
+
async def __call__(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
|
|
63
63
|
return await self.middleware(self.call_next, *args, **kwargs)
|
|
64
64
|
|
|
65
65
|
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, Any,
|
|
1
|
+
from typing import TYPE_CHECKING, Any, overload
|
|
2
2
|
|
|
3
3
|
import injection
|
|
4
4
|
|
|
5
5
|
from cq import Dispatcher
|
|
6
|
+
from cq._core.common.typing import Decorator
|
|
6
7
|
from cq._core.dispatcher.lazy import LazyDispatcher
|
|
7
|
-
from cq._core.dispatcher.pipe import
|
|
8
|
+
from cq._core.dispatcher.pipe import (
|
|
9
|
+
ContextPipeline,
|
|
10
|
+
ConvertMethod,
|
|
11
|
+
ConvertMethodAsync,
|
|
12
|
+
ConvertMethodSync,
|
|
13
|
+
)
|
|
8
14
|
from cq._core.message import AnyCommandBus, Command, Query, QueryBus
|
|
9
15
|
from cq._core.scope import CQScope
|
|
10
16
|
from cq.middlewares.scope import InjectionScopeMiddleware
|
|
@@ -47,20 +53,23 @@ class ContextCommandPipeline[I: Command](ContextPipeline[I]):
|
|
|
47
53
|
@overload
|
|
48
54
|
def query_step[T: Query](
|
|
49
55
|
self,
|
|
50
|
-
wrapped:
|
|
56
|
+
wrapped: ConvertMethodAsync[T, Any],
|
|
51
57
|
/,
|
|
52
|
-
) ->
|
|
58
|
+
) -> ConvertMethodAsync[T, Any]: ...
|
|
53
59
|
|
|
54
60
|
@overload
|
|
55
61
|
def query_step[T: Query](
|
|
56
62
|
self,
|
|
57
|
-
wrapped:
|
|
63
|
+
wrapped: ConvertMethodSync[T, Any],
|
|
58
64
|
/,
|
|
59
|
-
) ->
|
|
65
|
+
) -> ConvertMethodSync[T, Any]: ...
|
|
60
66
|
|
|
61
|
-
|
|
67
|
+
@overload
|
|
68
|
+
def query_step(self, wrapped: None = ..., /) -> Decorator: ...
|
|
69
|
+
|
|
70
|
+
def query_step[T: Query]( # type: ignore[misc]
|
|
62
71
|
self,
|
|
63
|
-
wrapped:
|
|
72
|
+
wrapped: ConvertMethod[T, Any] | None = None,
|
|
64
73
|
/,
|
|
65
74
|
) -> Any:
|
|
66
75
|
return self.step(wrapped, dispatcher=self.__query_dispatcher)
|
|
File without changes
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
from collections import deque
|
|
2
|
-
from collections.abc import Awaitable, Callable
|
|
3
|
-
from dataclasses import dataclass, field
|
|
4
|
-
from typing import TYPE_CHECKING, 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
|
-
if TYPE_CHECKING: # pragma: no cover
|
|
38
|
-
|
|
39
|
-
@overload
|
|
40
|
-
def step[T](
|
|
41
|
-
self,
|
|
42
|
-
wrapped: PipeConverter[T, Any],
|
|
43
|
-
/,
|
|
44
|
-
*,
|
|
45
|
-
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
46
|
-
) -> PipeConverter[T, Any]: ...
|
|
47
|
-
|
|
48
|
-
@overload
|
|
49
|
-
def step[T](
|
|
50
|
-
self,
|
|
51
|
-
wrapped: None = ...,
|
|
52
|
-
/,
|
|
53
|
-
*,
|
|
54
|
-
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
55
|
-
) -> Callable[[PipeConverter[T, Any]], PipeConverter[T, Any]]: ...
|
|
56
|
-
|
|
57
|
-
def step[T](
|
|
58
|
-
self,
|
|
59
|
-
wrapped: PipeConverter[T, Any] | None = None,
|
|
60
|
-
/,
|
|
61
|
-
*,
|
|
62
|
-
dispatcher: Dispatcher[T, Any] | None = None,
|
|
63
|
-
) -> Any:
|
|
64
|
-
def decorator(wp: PipeConverter[T, Any]) -> PipeConverter[T, Any]:
|
|
65
|
-
step = PipeStep(wp, dispatcher)
|
|
66
|
-
self.__steps.append(step)
|
|
67
|
-
return wp
|
|
68
|
-
|
|
69
|
-
return decorator(wrapped) if wrapped else decorator
|
|
70
|
-
|
|
71
|
-
def add_static_step[T](
|
|
72
|
-
self,
|
|
73
|
-
input_value: T,
|
|
74
|
-
*,
|
|
75
|
-
dispatcher: Dispatcher[T, Any] | None = None,
|
|
76
|
-
) -> Self:
|
|
77
|
-
@self.step(dispatcher=dispatcher)
|
|
78
|
-
async def converter(_: Any) -> T:
|
|
79
|
-
return input_value
|
|
80
|
-
|
|
81
|
-
return self
|
|
82
|
-
|
|
83
|
-
async def dispatch(self, input_value: I, /) -> O:
|
|
84
|
-
return await self._invoke_with_middlewares(self.__execute, input_value)
|
|
85
|
-
|
|
86
|
-
async def __execute(self, input_value: I) -> O:
|
|
87
|
-
dispatcher = self.__dispatcher
|
|
88
|
-
|
|
89
|
-
for step in self.__steps:
|
|
90
|
-
output_value = await dispatcher.dispatch(input_value)
|
|
91
|
-
input_value = await step.converter(output_value)
|
|
92
|
-
|
|
93
|
-
if input_value is None:
|
|
94
|
-
return NotImplemented
|
|
95
|
-
|
|
96
|
-
dispatcher = step.dispatcher or self.__dispatcher
|
|
97
|
-
|
|
98
|
-
return await dispatcher.dispatch(input_value)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
102
|
-
class ContextPipelineStep[I, O]:
|
|
103
|
-
converter: PipeConverterMethod[I, O]
|
|
104
|
-
dispatcher: Dispatcher[I, Any] | None = field(default=None)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
class ContextPipeline[I]:
|
|
108
|
-
__slots__ = ("__dispatcher", "__middlewares", "__steps")
|
|
109
|
-
|
|
110
|
-
__dispatcher: Dispatcher[Any, Any]
|
|
111
|
-
__middlewares: deque[Middleware[Any, Any]]
|
|
112
|
-
__steps: list[ContextPipelineStep[Any, Any]]
|
|
113
|
-
|
|
114
|
-
def __init__(self, dispatcher: Dispatcher[Any, Any]) -> None:
|
|
115
|
-
self.__dispatcher = dispatcher
|
|
116
|
-
self.__middlewares = deque()
|
|
117
|
-
self.__steps = []
|
|
118
|
-
|
|
119
|
-
if TYPE_CHECKING: # pragma: no cover
|
|
120
|
-
|
|
121
|
-
@overload
|
|
122
|
-
def __get__[O](self, instance: None, owner: type[O], /) -> Dispatcher[I, O]: ...
|
|
123
|
-
|
|
124
|
-
@overload
|
|
125
|
-
def __get__[O](
|
|
126
|
-
self,
|
|
127
|
-
instance: O,
|
|
128
|
-
owner: type[O] | None = ...,
|
|
129
|
-
/,
|
|
130
|
-
) -> Dispatcher[I, O]: ...
|
|
131
|
-
|
|
132
|
-
@overload
|
|
133
|
-
def __get__(self, instance: None = ..., owner: None = ..., /) -> Self: ...
|
|
134
|
-
|
|
135
|
-
def __get__[O](
|
|
136
|
-
self,
|
|
137
|
-
instance: O | None = None,
|
|
138
|
-
owner: type[O] | None = None,
|
|
139
|
-
/,
|
|
140
|
-
) -> Self | Dispatcher[I, O]:
|
|
141
|
-
if instance is None:
|
|
142
|
-
if owner is None:
|
|
143
|
-
return self
|
|
144
|
-
|
|
145
|
-
instance = owner()
|
|
146
|
-
|
|
147
|
-
pipeline = self.__new_pipeline(instance, owner)
|
|
148
|
-
return BoundContextPipeline(instance, pipeline)
|
|
149
|
-
|
|
150
|
-
def add_middlewares(self, *middlewares: Middleware[[I], Any]) -> Self:
|
|
151
|
-
self.__middlewares.extendleft(reversed(middlewares))
|
|
152
|
-
return self
|
|
153
|
-
|
|
154
|
-
if TYPE_CHECKING: # pragma: no cover
|
|
155
|
-
|
|
156
|
-
@overload
|
|
157
|
-
def step[T](
|
|
158
|
-
self,
|
|
159
|
-
wrapped: PipeConverterMethod[T, Any],
|
|
160
|
-
/,
|
|
161
|
-
*,
|
|
162
|
-
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
163
|
-
) -> PipeConverterMethod[T, Any]: ...
|
|
164
|
-
|
|
165
|
-
@overload
|
|
166
|
-
def step[T](
|
|
167
|
-
self,
|
|
168
|
-
wrapped: None = ...,
|
|
169
|
-
/,
|
|
170
|
-
*,
|
|
171
|
-
dispatcher: Dispatcher[T, Any] | None = ...,
|
|
172
|
-
) -> Callable[[PipeConverterMethod[T, Any]], PipeConverterMethod[T, Any]]: ...
|
|
173
|
-
|
|
174
|
-
def step[T](
|
|
175
|
-
self,
|
|
176
|
-
wrapped: PipeConverterMethod[T, Any] | None = None,
|
|
177
|
-
/,
|
|
178
|
-
*,
|
|
179
|
-
dispatcher: Dispatcher[T, Any] | None = None,
|
|
180
|
-
) -> Any:
|
|
181
|
-
def decorator(wp: PipeConverterMethod[T, Any]) -> PipeConverterMethod[T, Any]:
|
|
182
|
-
step = ContextPipelineStep(wp, dispatcher)
|
|
183
|
-
self.__steps.append(step)
|
|
184
|
-
return wp
|
|
185
|
-
|
|
186
|
-
return decorator(wrapped) if wrapped else decorator
|
|
187
|
-
|
|
188
|
-
def __new_pipeline[T](
|
|
189
|
-
self,
|
|
190
|
-
context: T,
|
|
191
|
-
context_type: type[T] | None,
|
|
192
|
-
) -> Pipe[I, Any]:
|
|
193
|
-
pipeline: Pipe[I, Any] = Pipe(self.__dispatcher)
|
|
194
|
-
pipeline.add_middlewares(*self.__middlewares)
|
|
195
|
-
|
|
196
|
-
for step in self.__steps:
|
|
197
|
-
converter = step.converter.__get__(context, context_type)
|
|
198
|
-
pipeline.step(converter, dispatcher=step.dispatcher)
|
|
199
|
-
|
|
200
|
-
return pipeline
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
@dataclass(repr=False, eq=False, frozen=True, slots=True)
|
|
204
|
-
class BoundContextPipeline[I, O](Dispatcher[I, O]):
|
|
205
|
-
context: O
|
|
206
|
-
pipeline: Pipe[I, Any]
|
|
207
|
-
|
|
208
|
-
async def dispatch(self, input_value: I, /) -> O:
|
|
209
|
-
await self.pipeline.dispatch(input_value)
|
|
210
|
-
return self.context
|
|
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
|
|
File without changes
|
|
File without changes
|