python-cq 0.15.0__tar.gz → 0.15.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.
Files changed (27) hide show
  1. {python_cq-0.15.0 → python_cq-0.15.1}/PKG-INFO +1 -1
  2. {python_cq-0.15.0 → python_cq-0.15.1}/cq/__init__.py +2 -1
  3. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/dispatcher/bus.py +13 -3
  4. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/handler.py +16 -7
  5. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/middleware.py +15 -1
  6. {python_cq-0.15.0 → python_cq-0.15.1}/pyproject.toml +1 -1
  7. {python_cq-0.15.0 → python_cq-0.15.1}/.gitignore +0 -0
  8. {python_cq-0.15.0 → python_cq-0.15.1}/LICENSE +0 -0
  9. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/__init__.py +0 -0
  10. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/common/__init__.py +0 -0
  11. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/common/typing.py +0 -0
  12. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/dispatcher/__init__.py +0 -0
  13. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/dispatcher/base.py +0 -0
  14. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/dispatcher/lazy.py +0 -0
  15. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/dispatcher/pipe.py +0 -0
  16. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/message.py +0 -0
  17. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/pipetools.py +0 -0
  18. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/related_events.py +0 -0
  19. {python_cq-0.15.0 → python_cq-0.15.1}/cq/_core/scope.py +0 -0
  20. {python_cq-0.15.0 → python_cq-0.15.1}/cq/exceptions.py +0 -0
  21. {python_cq-0.15.0 → python_cq-0.15.1}/cq/ext/__init__.py +0 -0
  22. {python_cq-0.15.0 → python_cq-0.15.1}/cq/ext/fastapi.py +0 -0
  23. {python_cq-0.15.0 → python_cq-0.15.1}/cq/middlewares/__init__.py +0 -0
  24. {python_cq-0.15.0 → python_cq-0.15.1}/cq/middlewares/retry.py +0 -0
  25. {python_cq-0.15.0 → python_cq-0.15.1}/cq/middlewares/scope.py +0 -0
  26. {python_cq-0.15.0 → python_cq-0.15.1}/cq/py.typed +0 -0
  27. {python_cq-0.15.0 → python_cq-0.15.1}/docs/index.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-cq
3
- Version: 0.15.0
3
+ Version: 0.15.1
4
4
  Summary: CQRS library for async Python projects.
5
5
  Project-URL: Documentation, https://python-cq.remimd.dev
6
6
  Project-URL: Repository, https://github.com/100nm/python-cq
@@ -17,7 +17,7 @@ from ._core.message import (
17
17
  new_query_bus,
18
18
  query_handler,
19
19
  )
20
- from ._core.middleware import Middleware, MiddlewareResult
20
+ from ._core.middleware import Middleware, MiddlewareResult, resolve_handler_source
21
21
  from ._core.pipetools import ContextCommandPipeline
22
22
  from ._core.related_events import RelatedEvents
23
23
  from ._core.scope import CQScope
@@ -47,4 +47,5 @@ __all__ = (
47
47
  "new_event_bus",
48
48
  "new_query_bus",
49
49
  "query_handler",
50
+ "resolve_handler_source",
50
51
  )
@@ -31,7 +31,12 @@ class Bus[I, O](Dispatcher[I, O], Protocol):
31
31
  raise NotImplementedError
32
32
 
33
33
  @abstractmethod
34
- def subscribe(self, input_type: type[I], factory: HandlerFactory[[I], O]) -> Self:
34
+ def subscribe(
35
+ self,
36
+ input_type: type[I],
37
+ factory: HandlerFactory[[I], O],
38
+ fail_silently: bool = ...,
39
+ ) -> Self:
35
40
  raise NotImplementedError
36
41
 
37
42
 
@@ -50,8 +55,13 @@ class BaseBus[I, O](BaseDispatcher[I, O], Bus[I, O], ABC):
50
55
  self.__listeners.extend(listeners)
51
56
  return self
52
57
 
53
- def subscribe(self, input_type: type[I], factory: HandlerFactory[[I], O]) -> Self:
54
- self.__registry.subscribe(input_type, factory)
58
+ def subscribe(
59
+ self,
60
+ input_type: type[I],
61
+ factory: HandlerFactory[[I], O],
62
+ fail_silently: bool = False,
63
+ ) -> Self:
64
+ self.__registry.subscribe(input_type, factory, fail_silently=fail_silently)
55
65
  return self
56
66
 
57
67
  def _handlers_from(self, input_type: type[I]) -> Iterator[HandleFunction[[I], O]]:
@@ -3,7 +3,7 @@ from collections import defaultdict
3
3
  from collections.abc import Awaitable, Callable, Iterator
4
4
  from dataclasses import dataclass, field
5
5
  from functools import partial
6
- from inspect import Parameter, isclass
6
+ from inspect import Parameter, isclass, unwrap
7
7
  from inspect import signature as inspect_signature
8
8
  from typing import TYPE_CHECKING, Any, Protocol, Self, overload, runtime_checkable
9
9
 
@@ -27,14 +27,23 @@ class Handler[**P, T](Protocol):
27
27
 
28
28
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
29
29
  class HandleFunction[**P, T]:
30
- handler_factory: HandlerFactory[P, T]
31
- handler_type: HandlerType[P, T] | None = field(default=None)
32
- fail_silently: bool = field(default=False)
30
+ factory: HandlerFactory[P, T]
31
+ source: HandlerType[P, T] | Any
32
+ fail_silently: bool
33
33
 
34
34
  async def __call__(self, /, *args: P.args, **kwargs: P.kwargs) -> T:
35
- handler = await self.handler_factory()
35
+ handler = await self.factory()
36
36
  return await handler.handle(*args, **kwargs)
37
37
 
38
+ @classmethod
39
+ def create(
40
+ cls,
41
+ factory: HandlerFactory[P, T],
42
+ source: HandlerType[P, T] | None = None,
43
+ fail_silently: bool = False,
44
+ ) -> Self:
45
+ return cls(factory, source or unwrap(factory), fail_silently)
46
+
38
47
 
39
48
  @runtime_checkable
40
49
  class HandlerRegistry[I, O](Protocol):
@@ -73,7 +82,7 @@ class MultipleHandlerRegistry[I, O](HandlerRegistry[I, O]):
73
82
  handler_type: HandlerType[[I], O] | None = None,
74
83
  fail_silently: bool = False,
75
84
  ) -> Self:
76
- function = HandleFunction(handler_factory, handler_type, fail_silently)
85
+ function = HandleFunction.create(handler_factory, handler_type, fail_silently)
77
86
 
78
87
  for key_type in _build_key_types(input_type):
79
88
  self.__values[key_type].append(function)
@@ -101,7 +110,7 @@ class SingleHandlerRegistry[I, O](HandlerRegistry[I, O]):
101
110
  handler_type: HandlerType[[I], O] | None = None,
102
111
  fail_silently: bool = False,
103
112
  ) -> Self:
104
- function = HandleFunction(handler_factory, handler_type, fail_silently)
113
+ function = HandleFunction.create(handler_factory, handler_type, fail_silently)
105
114
  entries = {key_type: function for key_type in _build_key_types(input_type)}
106
115
 
107
116
  for key_type in entries:
@@ -1,8 +1,9 @@
1
1
  from collections.abc import AsyncGenerator, Awaitable, Callable
2
2
  from dataclasses import dataclass, field
3
3
  from inspect import isasyncgenfunction
4
- from typing import Concatenate, Self, TypeGuard
4
+ from typing import Any, Concatenate, Self, TypeGuard
5
5
 
6
+ from cq._core.handler import HandleFunction, HandlerType
6
7
  from cq.exceptions import MiddlewareError
7
8
 
8
9
  type MiddlewareResult[T] = AsyncGenerator[None, T]
@@ -63,6 +64,19 @@ class _BoundMiddleware[**P, T]:
63
64
  return await self.middleware(self.call_next, *args, **kwargs)
64
65
 
65
66
 
67
+ def resolve_handler_source[**P, T](
68
+ call_next: Callable[P, Awaitable[T]]
69
+ | _BoundMiddleware[P, T]
70
+ | HandleFunction[P, T],
71
+ /,
72
+ ) -> HandlerType[P, T] | Any:
73
+ while True:
74
+ try:
75
+ call_next = call_next.call_next # type: ignore[union-attr]
76
+ except AttributeError:
77
+ return call_next.source # type: ignore[union-attr]
78
+
79
+
66
80
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
67
81
  class _GeneratorMiddleware[**P, T]:
68
82
  middleware: GeneratorMiddleware[P, T]
@@ -22,7 +22,7 @@ test = [
22
22
 
23
23
  [project]
24
24
  name = "python-cq"
25
- version = "0.15.0"
25
+ version = "0.15.1"
26
26
  description = "CQRS library for async Python projects."
27
27
  license = "MIT"
28
28
  license-files = ["LICENSE"]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes