python-cq 0.7.2__tar.gz → 0.7.4__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.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-cq
3
- Version: 0.7.2
4
- Summary: Lightweight CQRS library.
3
+ Version: 0.7.4
4
+ Summary: Lightweight CQRS library for async Python projects.
5
5
  Project-URL: Repository, https://github.com/100nm/python-cq
6
6
  Author: remimd
7
7
  License-Expression: MIT
@@ -94,6 +94,8 @@ class HandlerDecorator[I, O]:
94
94
  self,
95
95
  input_or_handler_type: type[I],
96
96
  /,
97
+ *,
98
+ threadsafe: bool | None = ...,
97
99
  ) -> Callable[[HandlerType[[I], O]], HandlerType[[I], O]]: ...
98
100
 
99
101
  @overload
@@ -101,6 +103,8 @@ class HandlerDecorator[I, O]:
101
103
  self,
102
104
  input_or_handler_type: HandlerType[[I], O],
103
105
  /,
106
+ *,
107
+ threadsafe: bool | None = ...,
104
108
  ) -> HandlerType[[I], O]: ...
105
109
 
106
110
  @overload
@@ -108,31 +112,39 @@ class HandlerDecorator[I, O]:
108
112
  self,
109
113
  input_or_handler_type: None = ...,
110
114
  /,
115
+ *,
116
+ threadsafe: bool | None = ...,
111
117
  ) -> Callable[[HandlerType[[I], O]], HandlerType[[I], O]]: ...
112
118
 
113
119
  def __call__(
114
120
  self,
115
121
  input_or_handler_type: type[I] | HandlerType[[I], O] | None = None,
116
122
  /,
123
+ *,
124
+ threadsafe: bool | None = None,
117
125
  ) -> Any:
118
- if input_or_handler_type is None:
119
- return self.__decorator
120
-
121
- elif isclass(input_or_handler_type) and issubclass(
122
- input_or_handler_type,
123
- Handler,
126
+ if (
127
+ input_or_handler_type is not None
128
+ and isclass(input_or_handler_type)
129
+ and issubclass(input_or_handler_type, Handler)
124
130
  ):
125
- return self.__decorator(input_or_handler_type)
131
+ return self.__decorator(input_or_handler_type, threadsafe=threadsafe)
126
132
 
127
- return partial(self.__decorator, input_type=input_or_handler_type) # type: ignore[arg-type]
133
+ return partial(
134
+ self.__decorator,
135
+ input_type=input_or_handler_type, # type: ignore[arg-type]
136
+ threadsafe=threadsafe,
137
+ )
128
138
 
129
139
  def __decorator(
130
140
  self,
131
141
  wrapped: HandlerType[[I], O],
142
+ /,
132
143
  *,
133
144
  input_type: type[I] | None = None,
145
+ threadsafe: bool | None = None,
134
146
  ) -> HandlerType[[I], O]:
135
- factory = self.injection_module.make_async_factory(wrapped)
147
+ factory = self.injection_module.make_async_factory(wrapped, threadsafe)
136
148
  input_type = input_type or _resolve_input_type(wrapped)
137
149
  self.manager.subscribe(input_type, factory)
138
150
  return wrapped
@@ -1,4 +1,4 @@
1
- from typing import Any
1
+ from typing import Any, Final
2
2
 
3
3
  import injection
4
4
 
@@ -22,23 +22,24 @@ type QueryBus[T] = Bus[Query, T]
22
22
  AnyCommandBus = CommandBus[Any]
23
23
 
24
24
 
25
- command_handler: HandlerDecorator[Command, Any] = HandlerDecorator(
25
+ command_handler: Final[HandlerDecorator[Command, Any]] = HandlerDecorator(
26
26
  SingleHandlerManager(),
27
27
  )
28
- event_handler: HandlerDecorator[Event, None] = HandlerDecorator(
28
+ event_handler: Final[HandlerDecorator[Event, None]] = HandlerDecorator(
29
29
  MultipleHandlerManager(),
30
30
  )
31
- query_handler: HandlerDecorator[Query, Any] = HandlerDecorator(
31
+ query_handler: Final[HandlerDecorator[Query, Any]] = HandlerDecorator(
32
32
  SingleHandlerManager(),
33
33
  )
34
34
 
35
35
 
36
36
  @injection.injectable(inject=False, mode="fallback")
37
- def new_command_bus() -> CommandBus: # type: ignore[type-arg]
37
+ def new_command_bus(*, threadsafe: bool | None = None) -> CommandBus: # type: ignore[type-arg]
38
38
  bus = SimpleBus(command_handler.manager)
39
39
  transaction_scope_middleware = InjectionScopeMiddleware(
40
40
  CQScope.TRANSACTION,
41
41
  exist_ok=True,
42
+ threadsafe=threadsafe,
42
43
  )
43
44
  bus.add_middlewares(transaction_scope_middleware)
44
45
  return bus
@@ -17,16 +17,17 @@ __all__ = ("InjectionScopeMiddleware",)
17
17
  class InjectionScopeMiddleware:
18
18
  scope_name: str
19
19
  exist_ok: bool = field(default=False, kw_only=True)
20
+ threadsafe: bool | None = field(default=None, kw_only=True)
20
21
 
21
22
  async def __call__(self, *args: Any, **kwargs: Any) -> MiddlewareResult[Any]:
22
23
  async with AsyncExitStack() as stack:
24
+ cm = adefine_scope(self.scope_name, threadsafe=self.threadsafe)
23
25
  try:
24
- await stack.enter_async_context(
25
- adefine_scope(self.scope_name),
26
- )
26
+ await stack.enter_async_context(cm)
27
27
 
28
28
  except ScopeAlreadyDefinedError:
29
29
  if not self.exist_ok:
30
30
  raise
31
31
 
32
+ del cm
32
33
  yield
@@ -21,8 +21,8 @@ test = [
21
21
 
22
22
  [project]
23
23
  name = "python-cq"
24
- version = "0.7.2"
25
- description = "Lightweight CQRS library."
24
+ version = "0.7.4"
25
+ description = "Lightweight CQRS library for async Python projects."
26
26
  license = "MIT"
27
27
  license-files = ["LICENSE"]
28
28
  readme = "README.md"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes