python-cq 0.10.0__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.10.0 → python_cq-0.11.0}/PKG-INFO +1 -1
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/__init__.py +6 -1
- 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.10.0 → python_cq-0.11.0}/pyproject.toml +1 -1
- python_cq-0.10.0/cq/_core/dispatcher/pipe.py +0 -82
- {python_cq-0.10.0 → python_cq-0.11.0}/.gitignore +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/LICENSE +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/README.md +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/_core/__init__.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/_core/dispatcher/__init__.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/_core/dispatcher/base.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/_core/dispatcher/bus.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/_core/handler.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/_core/message.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/_core/middleware.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/_core/related_events.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/_core/scope.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/exceptions.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/ext/__init__.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/ext/fastapi.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/middlewares/__init__.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/middlewares/retry.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/middlewares/scope.py +0 -0
- {python_cq-0.10.0 → python_cq-0.11.0}/cq/py.typed +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
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,10 +28,13 @@ __all__ = (
|
|
|
26
28
|
"CQScope",
|
|
27
29
|
"Command",
|
|
28
30
|
"CommandBus",
|
|
31
|
+
"ContextCommandPipeline",
|
|
32
|
+
"ContextPipeline",
|
|
29
33
|
"DeferredDispatcher",
|
|
30
34
|
"Dispatcher",
|
|
31
35
|
"Event",
|
|
32
36
|
"EventBus",
|
|
37
|
+
"LazyDispatcher",
|
|
33
38
|
"Middleware",
|
|
34
39
|
"MiddlewareResult",
|
|
35
40
|
"Pipe",
|
|
@@ -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)
|
|
@@ -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)
|
|
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
|
|
File without changes
|