python-cq 0.12.2__tar.gz → 0.13.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.
Files changed (28) hide show
  1. python_cq-0.13.0/PKG-INFO +70 -0
  2. {python_cq-0.12.2 → python_cq-0.13.0}/cq/_core/dispatcher/bus.py +13 -13
  3. {python_cq-0.12.2 → python_cq-0.13.0}/cq/_core/handler.py +16 -12
  4. {python_cq-0.12.2 → python_cq-0.13.0}/cq/_core/message.py +8 -8
  5. python_cq-0.13.0/cq/_core/pipetools.py +66 -0
  6. {python_cq-0.12.2 → python_cq-0.13.0}/cq/middlewares/scope.py +3 -3
  7. python_cq-0.13.0/docs/index.md +40 -0
  8. {python_cq-0.12.2 → python_cq-0.13.0}/pyproject.toml +8 -8
  9. python_cq-0.12.2/PKG-INFO +0 -55
  10. python_cq-0.12.2/README.md +0 -26
  11. python_cq-0.12.2/cq/_core/pipetools.py +0 -31
  12. {python_cq-0.12.2 → python_cq-0.13.0}/.gitignore +0 -0
  13. {python_cq-0.12.2 → python_cq-0.13.0}/LICENSE +0 -0
  14. {python_cq-0.12.2 → python_cq-0.13.0}/cq/__init__.py +0 -0
  15. {python_cq-0.12.2 → python_cq-0.13.0}/cq/_core/__init__.py +0 -0
  16. {python_cq-0.12.2 → python_cq-0.13.0}/cq/_core/dispatcher/__init__.py +0 -0
  17. {python_cq-0.12.2 → python_cq-0.13.0}/cq/_core/dispatcher/base.py +0 -0
  18. {python_cq-0.12.2 → python_cq-0.13.0}/cq/_core/dispatcher/lazy.py +0 -0
  19. {python_cq-0.12.2 → python_cq-0.13.0}/cq/_core/dispatcher/pipe.py +0 -0
  20. {python_cq-0.12.2 → python_cq-0.13.0}/cq/_core/middleware.py +0 -0
  21. {python_cq-0.12.2 → python_cq-0.13.0}/cq/_core/related_events.py +0 -0
  22. {python_cq-0.12.2 → python_cq-0.13.0}/cq/_core/scope.py +0 -0
  23. {python_cq-0.12.2 → python_cq-0.13.0}/cq/exceptions.py +0 -0
  24. {python_cq-0.12.2 → python_cq-0.13.0}/cq/ext/__init__.py +0 -0
  25. {python_cq-0.12.2 → python_cq-0.13.0}/cq/ext/fastapi.py +0 -0
  26. {python_cq-0.12.2 → python_cq-0.13.0}/cq/middlewares/__init__.py +0 -0
  27. {python_cq-0.12.2 → python_cq-0.13.0}/cq/middlewares/retry.py +0 -0
  28. {python_cq-0.12.2 → python_cq-0.13.0}/cq/py.typed +0 -0
@@ -0,0 +1,70 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-cq
3
+ Version: 0.13.0
4
+ Summary: CQRS library for async Python projects.
5
+ Project-URL: Documentation, https://python-cq.remimd.dev
6
+ Project-URL: Repository, https://github.com/100nm/python-cq
7
+ Author: remimd
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: cqrs
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Natural Language :: English
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Software Development :: Libraries
22
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: <3.15,>=3.12
26
+ Requires-Dist: anyio
27
+ Requires-Dist: python-injection
28
+ Requires-Dist: type-analyzer
29
+ Description-Content-Type: text/markdown
30
+
31
+ # python-cq
32
+
33
+ [![PyPI - Version](https://img.shields.io/pypi/v/python-cq.svg?color=4051b5&style=for-the-badge)](https://pypi.org/project/python-cq)
34
+ [![PyPI - Downloads](https://img.shields.io/pypi/dm/python-cq.svg?color=4051b5&style=for-the-badge)](https://pypistats.org/packages/python-cq)
35
+
36
+ **python-cq** is a Python package designed to organize your code following CQRS principles. It builds on top of [python-injection](https://github.com/100nm/python-injection) for dependency injection.
37
+
38
+ ## What is CQRS?
39
+
40
+ CQRS (Command Query Responsibility Segregation) is an architectural pattern that separates read operations from write operations. This separation helps to:
41
+
42
+ - **Clarify intent**: each operation has a single, well-defined responsibility
43
+ - **Improve maintainability**: smaller, focused handlers are easier to understand and modify
44
+ - **Simplify testing**: isolated handlers are straightforward to unit test
45
+
46
+ CQRS is often associated with distributed systems and Event Sourcing, but its benefits extend beyond that. Even in a local or monolithic application, adopting this pattern helps structure your code and makes the boundaries between reading and writing explicit.
47
+
48
+ ## Prerequisites
49
+
50
+ To get the most out of **python-cq**, familiarity with the following concepts is recommended:
51
+
52
+ - **CQRS** and the distinction between Commands, Queries and Events
53
+ - **Domain Driven Design (DDD)**, particularly aggregates and bounded contexts
54
+
55
+ This knowledge will help you design coherent handlers and organize your code effectively.
56
+
57
+ ## Message types
58
+
59
+ **python-cq** provides three types of messages to model your application's operations:
60
+
61
+ - **Command**: represents an intent to change the system's state. A command is handled by exactly one handler and may return a value for convenience.
62
+ - **Query**: represents a request for information. A query is handled by exactly one handler and returns data without side effects.
63
+ - **Event**: represents something that has happened in the system. An event can be handled by zero, one, or many handlers, enabling loose coupling between components.
64
+
65
+ ## Installation
66
+
67
+ Requires Python 3.12 or higher.
68
+ ```bash
69
+ pip install python-cq
70
+ ```
@@ -8,9 +8,9 @@ from anyio.abc import TaskGroup
8
8
  from cq._core.dispatcher.base import BaseDispatcher, Dispatcher
9
9
  from cq._core.handler import (
10
10
  HandlerFactory,
11
- HandlerManager,
12
- MultipleHandlerManager,
13
- SingleHandlerManager,
11
+ HandlerRegistry,
12
+ MultipleHandlerRegistry,
13
+ SingleHandlerRegistry,
14
14
  )
15
15
  from cq._core.middleware import Middleware
16
16
 
@@ -35,29 +35,29 @@ class Bus[I, O](Dispatcher[I, O], Protocol):
35
35
 
36
36
 
37
37
  class BaseBus[I, O](BaseDispatcher[I, O], Bus[I, O], ABC):
38
- __slots__ = ("__listeners", "__manager")
38
+ __slots__ = ("__listeners", "__registry")
39
39
 
40
40
  __listeners: list[Listener[I]]
41
- __manager: HandlerManager[I, O]
41
+ __registry: HandlerRegistry[I, O]
42
42
 
43
- def __init__(self, manager: HandlerManager[I, O]) -> None:
43
+ def __init__(self, registry: HandlerRegistry[I, O], /) -> None:
44
44
  super().__init__()
45
45
  self.__listeners = []
46
- self.__manager = manager
46
+ self.__registry = registry
47
47
 
48
48
  def add_listeners(self, *listeners: Listener[I]) -> Self:
49
49
  self.__listeners.extend(listeners)
50
50
  return self
51
51
 
52
52
  def subscribe(self, input_type: type[I], factory: HandlerFactory[[I], O]) -> Self:
53
- self.__manager.subscribe(input_type, factory)
53
+ self.__registry.subscribe(input_type, factory)
54
54
  return self
55
55
 
56
56
  def _handlers_from(
57
57
  self,
58
58
  input_type: type[I],
59
59
  ) -> Iterator[Callable[[I], Awaitable[O]]]:
60
- return self.__manager.handlers_from(input_type)
60
+ return self.__registry.handlers_from(input_type)
61
61
 
62
62
  def _trigger_listeners(self, input_value: I, /, task_group: TaskGroup) -> None:
63
63
  for listener in self.__listeners:
@@ -67,8 +67,8 @@ class BaseBus[I, O](BaseDispatcher[I, O], Bus[I, O], ABC):
67
67
  class SimpleBus[I, O](BaseBus[I, O]):
68
68
  __slots__ = ()
69
69
 
70
- def __init__(self, manager: HandlerManager[I, O] | None = None) -> None:
71
- super().__init__(manager or SingleHandlerManager())
70
+ def __init__(self, registry: HandlerRegistry[I, O] | None = None, /) -> None:
71
+ super().__init__(registry or SingleHandlerRegistry())
72
72
 
73
73
  async def dispatch(self, input_value: I, /) -> O:
74
74
  async with anyio.create_task_group() as task_group:
@@ -83,8 +83,8 @@ class SimpleBus[I, O](BaseBus[I, O]):
83
83
  class TaskBus[I](BaseBus[I, None]):
84
84
  __slots__ = ()
85
85
 
86
- def __init__(self, manager: HandlerManager[I, None] | None = None) -> None:
87
- super().__init__(manager or MultipleHandlerManager())
86
+ def __init__(self, registry: HandlerRegistry[I, None] | None = None, /) -> None:
87
+ super().__init__(registry or MultipleHandlerRegistry())
88
88
 
89
89
  async def dispatch(self, input_value: I, /) -> None:
90
90
  async with anyio.create_task_group() as task_group:
@@ -24,7 +24,7 @@ class Handler[**P, T](Protocol):
24
24
 
25
25
 
26
26
  @runtime_checkable
27
- class HandlerManager[I, O](Protocol):
27
+ class HandlerRegistry[I, O](Protocol):
28
28
  __slots__ = ()
29
29
 
30
30
  @abstractmethod
@@ -40,7 +40,7 @@ class HandlerManager[I, O](Protocol):
40
40
 
41
41
 
42
42
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
43
- class MultipleHandlerManager[I, O](HandlerManager[I, O]):
43
+ class MultipleHandlerRegistry[I, O](HandlerRegistry[I, O]):
44
44
  __factories: dict[type[I], list[HandlerFactory[[I], O]]] = field(
45
45
  default_factory=partial(defaultdict, list),
46
46
  init=False,
@@ -62,7 +62,7 @@ class MultipleHandlerManager[I, O](HandlerManager[I, O]):
62
62
 
63
63
 
64
64
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
65
- class SingleHandlerManager[I, O](HandlerManager[I, O]):
65
+ class SingleHandlerRegistry[I, O](HandlerRegistry[I, O]):
66
66
  __factories: dict[type[I], HandlerFactory[[I], O]] = field(
67
67
  default_factory=dict,
68
68
  init=False,
@@ -90,9 +90,13 @@ class SingleHandlerManager[I, O](HandlerManager[I, O]):
90
90
  return self
91
91
 
92
92
 
93
+ class _Decorator(Protocol):
94
+ def __call__[T](self, wrapped: T, /) -> T: ...
95
+
96
+
93
97
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
94
98
  class HandlerDecorator[I, O]:
95
- manager: HandlerManager[I, O]
99
+ registry: HandlerRegistry[I, O]
96
100
  injection_module: injection.Module = field(default_factory=injection.mod)
97
101
 
98
102
  if TYPE_CHECKING: # pragma: no cover
@@ -104,16 +108,16 @@ class HandlerDecorator[I, O]:
104
108
  /,
105
109
  *,
106
110
  threadsafe: bool | None = ...,
107
- ) -> Callable[[HandlerType[[I], O]], HandlerType[[I], O]]: ...
111
+ ) -> _Decorator: ...
108
112
 
109
113
  @overload
110
- def __call__(
114
+ def __call__[T](
111
115
  self,
112
- input_or_handler_type: HandlerType[[I], O],
116
+ input_or_handler_type: T,
113
117
  /,
114
118
  *,
115
119
  threadsafe: bool | None = ...,
116
- ) -> HandlerType[[I], O]: ...
120
+ ) -> T: ...
117
121
 
118
122
  @overload
119
123
  def __call__(
@@ -122,11 +126,11 @@ class HandlerDecorator[I, O]:
122
126
  /,
123
127
  *,
124
128
  threadsafe: bool | None = ...,
125
- ) -> Callable[[HandlerType[[I], O]], HandlerType[[I], O]]: ...
129
+ ) -> _Decorator: ...
126
130
 
127
- def __call__(
131
+ def __call__[T](
128
132
  self,
129
- input_or_handler_type: type[I] | HandlerType[[I], O] | None = None,
133
+ input_or_handler_type: type[I] | T | None = None,
130
134
  /,
131
135
  *,
132
136
  threadsafe: bool | None = None,
@@ -154,7 +158,7 @@ class HandlerDecorator[I, O]:
154
158
  ) -> HandlerType[[I], O]:
155
159
  factory = self.injection_module.make_async_factory(wrapped, threadsafe)
156
160
  input_type = input_type or _resolve_input_type(wrapped)
157
- self.manager.subscribe(input_type, factory)
161
+ self.registry.subscribe(input_type, factory)
158
162
  return wrapped
159
163
 
160
164
 
@@ -6,8 +6,8 @@ from cq._core.dispatcher.base import Dispatcher
6
6
  from cq._core.dispatcher.bus import Bus, SimpleBus, TaskBus
7
7
  from cq._core.handler import (
8
8
  HandlerDecorator,
9
- MultipleHandlerManager,
10
- SingleHandlerManager,
9
+ MultipleHandlerRegistry,
10
+ SingleHandlerRegistry,
11
11
  )
12
12
  from cq._core.scope import CQScope
13
13
  from cq.middlewares.scope import InjectionScopeMiddleware
@@ -24,13 +24,13 @@ AnyCommandBus = CommandBus[Any]
24
24
 
25
25
 
26
26
  command_handler: Final[HandlerDecorator[Command, Any]] = HandlerDecorator(
27
- SingleHandlerManager(),
27
+ SingleHandlerRegistry(),
28
28
  )
29
29
  event_handler: Final[HandlerDecorator[Event, None]] = HandlerDecorator(
30
- MultipleHandlerManager(),
30
+ MultipleHandlerRegistry(),
31
31
  )
32
32
  query_handler: Final[HandlerDecorator[Query, Any]] = HandlerDecorator(
33
- SingleHandlerManager(),
33
+ SingleHandlerRegistry(),
34
34
  )
35
35
 
36
36
 
@@ -41,7 +41,7 @@ query_handler: Final[HandlerDecorator[Query, Any]] = HandlerDecorator(
41
41
  mode="fallback",
42
42
  )
43
43
  def new_command_bus(*, threadsafe: bool | None = None) -> Bus[Command, Any]:
44
- bus = SimpleBus(command_handler.manager)
44
+ bus = SimpleBus(command_handler.registry)
45
45
  transaction_scope_middleware = InjectionScopeMiddleware(
46
46
  CQScope.TRANSACTION,
47
47
  exist_ok=True,
@@ -58,7 +58,7 @@ def new_command_bus(*, threadsafe: bool | None = None) -> Bus[Command, Any]:
58
58
  mode="fallback",
59
59
  )
60
60
  def new_event_bus() -> Bus[Event, None]:
61
- return TaskBus(event_handler.manager)
61
+ return TaskBus(event_handler.registry)
62
62
 
63
63
 
64
64
  @injection.injectable(
@@ -68,4 +68,4 @@ def new_event_bus() -> Bus[Event, None]:
68
68
  mode="fallback",
69
69
  )
70
70
  def new_query_bus() -> Bus[Query, Any]:
71
- return SimpleBus(query_handler.manager)
71
+ return SimpleBus(query_handler.registry)
@@ -0,0 +1,66 @@
1
+ from typing import TYPE_CHECKING, Any, Callable, overload
2
+
3
+ import injection
4
+
5
+ from cq import Dispatcher
6
+ from cq._core.dispatcher.lazy import LazyDispatcher
7
+ from cq._core.dispatcher.pipe import ContextPipeline, PipeConverterMethod
8
+ from cq._core.message import AnyCommandBus, Command, Query, QueryBus
9
+ from cq._core.scope import CQScope
10
+ from cq.middlewares.scope import InjectionScopeMiddleware
11
+
12
+
13
+ class ContextCommandPipeline[I: Command](ContextPipeline[I]):
14
+ __slots__ = ("__query_dispatcher",)
15
+
16
+ __query_dispatcher: Dispatcher[Query, Any]
17
+
18
+ def __init__(
19
+ self,
20
+ /,
21
+ *,
22
+ injection_module: injection.Module | None = None,
23
+ threadsafe: bool | None = None,
24
+ ) -> None:
25
+ command_dispatcher = LazyDispatcher(
26
+ AnyCommandBus,
27
+ injection_module=injection_module,
28
+ threadsafe=threadsafe,
29
+ )
30
+ super().__init__(command_dispatcher)
31
+
32
+ self.__query_dispatcher = LazyDispatcher(
33
+ QueryBus,
34
+ injection_module=injection_module,
35
+ threadsafe=threadsafe,
36
+ )
37
+
38
+ transaction_scope_middleware = InjectionScopeMiddleware(
39
+ CQScope.TRANSACTION,
40
+ exist_ok=True,
41
+ threadsafe=threadsafe,
42
+ )
43
+ self.add_middlewares(transaction_scope_middleware)
44
+
45
+ if TYPE_CHECKING: # pragma: no cover
46
+
47
+ @overload
48
+ def query_step[T: Query](
49
+ self,
50
+ wrapped: PipeConverterMethod[T, Any],
51
+ /,
52
+ ) -> PipeConverterMethod[T, Any]: ...
53
+
54
+ @overload
55
+ def query_step[T: Query](
56
+ self,
57
+ wrapped: None = ...,
58
+ /,
59
+ ) -> Callable[[PipeConverterMethod[T, Any]], PipeConverterMethod[T, Any]]: ...
60
+
61
+ def query_step[T: Query](
62
+ self,
63
+ wrapped: PipeConverterMethod[T, Any] | None = None,
64
+ /,
65
+ ) -> Any:
66
+ return self.step(wrapped, dispatcher=self.__query_dispatcher)
@@ -21,13 +21,13 @@ class InjectionScopeMiddleware:
21
21
 
22
22
  async def __call__(self, *args: Any, **kwargs: Any) -> MiddlewareResult[Any]:
23
23
  async with AsyncExitStack() as stack:
24
- cm = adefine_scope(self.scope_name, threadsafe=self.threadsafe)
25
24
  try:
26
- await stack.enter_async_context(cm)
25
+ await stack.enter_async_context(
26
+ adefine_scope(self.scope_name, threadsafe=self.threadsafe)
27
+ )
27
28
 
28
29
  except ScopeAlreadyDefinedError:
29
30
  if not self.exist_ok:
30
31
  raise
31
32
 
32
- del cm
33
33
  yield
@@ -0,0 +1,40 @@
1
+ # python-cq
2
+
3
+ [![PyPI - Version](https://img.shields.io/pypi/v/python-cq.svg?color=4051b5&style=for-the-badge)](https://pypi.org/project/python-cq)
4
+ [![PyPI - Downloads](https://img.shields.io/pypi/dm/python-cq.svg?color=4051b5&style=for-the-badge)](https://pypistats.org/packages/python-cq)
5
+
6
+ **python-cq** is a Python package designed to organize your code following CQRS principles. It builds on top of [python-injection](https://github.com/100nm/python-injection) for dependency injection.
7
+
8
+ ## What is CQRS?
9
+
10
+ CQRS (Command Query Responsibility Segregation) is an architectural pattern that separates read operations from write operations. This separation helps to:
11
+
12
+ - **Clarify intent**: each operation has a single, well-defined responsibility
13
+ - **Improve maintainability**: smaller, focused handlers are easier to understand and modify
14
+ - **Simplify testing**: isolated handlers are straightforward to unit test
15
+
16
+ CQRS is often associated with distributed systems and Event Sourcing, but its benefits extend beyond that. Even in a local or monolithic application, adopting this pattern helps structure your code and makes the boundaries between reading and writing explicit.
17
+
18
+ ## Prerequisites
19
+
20
+ To get the most out of **python-cq**, familiarity with the following concepts is recommended:
21
+
22
+ - **CQRS** and the distinction between Commands, Queries and Events
23
+ - **Domain Driven Design (DDD)**, particularly aggregates and bounded contexts
24
+
25
+ This knowledge will help you design coherent handlers and organize your code effectively.
26
+
27
+ ## Message types
28
+
29
+ **python-cq** provides three types of messages to model your application's operations:
30
+
31
+ - **Command**: represents an intent to change the system's state. A command is handled by exactly one handler and may return a value for convenience.
32
+ - **Query**: represents a request for information. A query is handled by exactly one handler and returns data without side effects.
33
+ - **Event**: represents something that has happened in the system. An event can be handled by zero, one, or many handlers, enabling loose coupling between components.
34
+
35
+ ## Installation
36
+
37
+ Requires Python 3.12 or higher.
38
+ ```bash
39
+ pip install python-cq
40
+ ```
@@ -8,10 +8,9 @@ dev = [
8
8
  "mypy",
9
9
  "ruff",
10
10
  ]
11
- example = [
12
- "fastapi",
13
- "msgspec",
14
- "pydantic",
11
+ docs = [
12
+ "mkdocs",
13
+ "mkdocs-material",
15
14
  ]
16
15
  test = [
17
16
  "fastapi",
@@ -23,11 +22,11 @@ test = [
23
22
 
24
23
  [project]
25
24
  name = "python-cq"
26
- version = "0.12.2"
27
- description = "Lightweight CQRS library for async Python projects."
25
+ version = "0.13.0"
26
+ description = "CQRS library for async Python projects."
28
27
  license = "MIT"
29
28
  license-files = ["LICENSE"]
30
- readme = "README.md"
29
+ readme = "docs/index.md"
31
30
  requires-python = ">=3.12, <3.15"
32
31
  authors = [{ name = "remimd" }]
33
32
  keywords = ["cqrs"]
@@ -54,6 +53,7 @@ dependencies = [
54
53
  ]
55
54
 
56
55
  [project.urls]
56
+ Documentation = "https://python-cq.remimd.dev"
57
57
  Repository = "https://github.com/100nm/python-cq"
58
58
 
59
59
  [tool.coverage.report]
@@ -104,5 +104,5 @@ extend-select = ["F", "I", "N"]
104
104
  fixable = ["ALL"]
105
105
 
106
106
  [tool.uv]
107
- default-groups = ["dev", "example", "test"]
107
+ default-groups = ["dev", "docs", "test"]
108
108
  package = true
python_cq-0.12.2/PKG-INFO DELETED
@@ -1,55 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: python-cq
3
- Version: 0.12.2
4
- Summary: Lightweight CQRS library for async Python projects.
5
- Project-URL: Repository, https://github.com/100nm/python-cq
6
- Author: remimd
7
- License-Expression: MIT
8
- License-File: LICENSE
9
- Keywords: cqrs
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Intended Audience :: Developers
12
- Classifier: Natural Language :: English
13
- Classifier: Operating System :: OS Independent
14
- Classifier: Programming Language :: Python
15
- Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3 :: Only
17
- Classifier: Programming Language :: Python :: 3.12
18
- Classifier: Programming Language :: Python :: 3.13
19
- Classifier: Programming Language :: Python :: 3.14
20
- Classifier: Topic :: Software Development :: Libraries
21
- Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
22
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
- Classifier: Typing :: Typed
24
- Requires-Python: <3.15,>=3.12
25
- Requires-Dist: anyio
26
- Requires-Dist: python-injection
27
- Requires-Dist: type-analyzer
28
- Description-Content-Type: text/markdown
29
-
30
- # python-cq
31
-
32
- [![CI](https://github.com/100nm/python-cq/actions/workflows/ci.yml/badge.svg)](https://github.com/100nm/python-cq)
33
- [![PyPI - Version](https://img.shields.io/pypi/v/python-cq.svg?color=blue)](https://pypi.org/project/python-cq)
34
- [![PyPI - Downloads](https://img.shields.io/pypi/dm/python-cq.svg?color=blue)](https://pypistats.org/packages/python-cq)
35
- [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
36
-
37
- Lightweight library for separating Python code according to **Command and Query Responsibility Segregation** principles.
38
-
39
- Dependency injection is handled by [python-injection](https://github.com/100nm/python-injection).
40
-
41
- Easy to use with [FastAPI](https://github.com/fastapi/fastapi).
42
-
43
- ## Installation
44
-
45
- ⚠️ _Requires Python 3.12 or higher_
46
-
47
- ```bash
48
- pip install python-cq
49
- ```
50
-
51
- ## Resources
52
-
53
- * [**Writing Application Layer**](https://github.com/100nm/python-cq/tree/prod/documentation/writing-application-layer.md)
54
- * [**Pipeline**](https://github.com/100nm/python-cq/tree/prod/documentation/pipeline.md)
55
- * [**FastAPI Example**](https://github.com/100nm/python-cq/tree/prod/documentation/fastapi-example.md)
@@ -1,26 +0,0 @@
1
- # python-cq
2
-
3
- [![CI](https://github.com/100nm/python-cq/actions/workflows/ci.yml/badge.svg)](https://github.com/100nm/python-cq)
4
- [![PyPI - Version](https://img.shields.io/pypi/v/python-cq.svg?color=blue)](https://pypi.org/project/python-cq)
5
- [![PyPI - Downloads](https://img.shields.io/pypi/dm/python-cq.svg?color=blue)](https://pypistats.org/packages/python-cq)
6
- [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
7
-
8
- Lightweight library for separating Python code according to **Command and Query Responsibility Segregation** principles.
9
-
10
- Dependency injection is handled by [python-injection](https://github.com/100nm/python-injection).
11
-
12
- Easy to use with [FastAPI](https://github.com/fastapi/fastapi).
13
-
14
- ## Installation
15
-
16
- ⚠️ _Requires Python 3.12 or higher_
17
-
18
- ```bash
19
- pip install python-cq
20
- ```
21
-
22
- ## Resources
23
-
24
- * [**Writing Application Layer**](https://github.com/100nm/python-cq/tree/prod/documentation/writing-application-layer.md)
25
- * [**Pipeline**](https://github.com/100nm/python-cq/tree/prod/documentation/pipeline.md)
26
- * [**FastAPI Example**](https://github.com/100nm/python-cq/tree/prod/documentation/fastapi-example.md)
@@ -1,31 +0,0 @@
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)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes