python-cq 0.22.0__tar.gz → 0.23.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 (35) hide show
  1. {python_cq-0.22.0 → python_cq-0.23.0}/PKG-INFO +7 -2
  2. {python_cq-0.22.0 → python_cq-0.23.0}/cq/__init__.py +27 -24
  3. {python_cq-0.22.0 → python_cq-0.23.0}/cq/_core/message.py +1 -1
  4. {python_cq-0.22.0 → python_cq-0.23.0}/cq/_core/middleware.py +2 -2
  5. {python_cq-0.22.0/cq/_core → python_cq-0.23.0/cq/_core/queuing}/pump.py +1 -1
  6. {python_cq-0.22.0/cq/_core → python_cq-0.23.0/cq/_core/queuing}/queues/abc.py +3 -2
  7. {python_cq-0.22.0/cq/_core → python_cq-0.23.0/cq/_core/queuing}/queues/memory.py +6 -6
  8. python_cq-0.22.0/cq/_core/pipetools.py → python_cq-0.23.0/cq/_core/routing/command_pipeline.py +5 -5
  9. {python_cq-0.22.0/cq/_core → python_cq-0.23.0/cq/_core/routing}/dispatchers/bus.py +5 -5
  10. {python_cq-0.22.0/cq/_core → python_cq-0.23.0/cq/_core/routing}/dispatchers/lazy.py +2 -2
  11. {python_cq-0.22.0/cq/_core → python_cq-0.23.0/cq/_core/routing}/dispatchers/pipe.py +1 -1
  12. {python_cq-0.22.0/cq/_core → python_cq-0.23.0/cq/_core/routing}/handler.py +2 -2
  13. python_cq-0.22.0/cq/_core/cq.py → python_cq-0.23.0/cq/_core/routing/router.py +7 -7
  14. {python_cq-0.22.0 → python_cq-0.23.0}/cq/ext/injection.py +3 -3
  15. python_cq-0.23.0/cq/middlewares/__init__.py +0 -0
  16. {python_cq-0.22.0 → python_cq-0.23.0}/cq/middlewares/contextlib.py +4 -3
  17. python_cq-0.23.0/cq/py.typed +0 -0
  18. {python_cq-0.22.0 → python_cq-0.23.0}/docs/index.md +4 -0
  19. {python_cq-0.22.0 → python_cq-0.23.0}/pyproject.toml +2 -1
  20. {python_cq-0.22.0 → python_cq-0.23.0}/.gitignore +0 -0
  21. {python_cq-0.22.0 → python_cq-0.23.0}/LICENSE +0 -0
  22. {python_cq-0.22.0 → python_cq-0.23.0}/cq/_core/__init__.py +0 -0
  23. {python_cq-0.22.0 → python_cq-0.23.0}/cq/_core/common/__init__.py +0 -0
  24. {python_cq-0.22.0 → python_cq-0.23.0}/cq/_core/common/typing.py +0 -0
  25. {python_cq-0.22.0/cq/_core/dispatchers → python_cq-0.23.0/cq/_core/queuing}/__init__.py +0 -0
  26. {python_cq-0.22.0/cq/_core → python_cq-0.23.0/cq/_core/queuing}/queues/__init__.py +0 -0
  27. {python_cq-0.22.0 → python_cq-0.23.0}/cq/_core/related_events.py +0 -0
  28. {python_cq-0.22.0/cq/ext → python_cq-0.23.0/cq/_core/routing}/__init__.py +0 -0
  29. {python_cq-0.22.0/cq/_core → python_cq-0.23.0/cq/_core/routing}/di.py +0 -0
  30. {python_cq-0.22.0/cq/middlewares → python_cq-0.23.0/cq/_core/routing/dispatchers}/__init__.py +0 -0
  31. {python_cq-0.22.0/cq/_core → python_cq-0.23.0/cq/_core/routing}/dispatchers/abc.py +0 -0
  32. {python_cq-0.22.0 → python_cq-0.23.0}/cq/exceptions.py +0 -0
  33. /python_cq-0.22.0/cq/py.typed → /python_cq-0.23.0/cq/ext/__init__.py +0 -0
  34. {python_cq-0.22.0 → python_cq-0.23.0}/cq/middlewares/exc.py +0 -0
  35. {python_cq-0.22.0 → python_cq-0.23.0}/cq/middlewares/retry.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: python-cq
3
- Version: 0.22.0
3
+ Version: 0.23.0
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
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
18
18
  Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Programming Language :: Python :: 3.13
20
20
  Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Programming Language :: Python :: 3.15
21
22
  Classifier: Topic :: Software Development :: Libraries
22
23
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
23
24
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
@@ -80,23 +81,27 @@ from cq import CommandBus, command_handler
80
81
  from dataclasses import dataclass
81
82
  from injection import inject
82
83
 
84
+
83
85
  @dataclass
84
86
  class CreateUserCommand:
85
87
  name: str
86
88
  email: str
87
89
 
90
+
88
91
  @command_handler
89
92
  class CreateUserHandler:
90
93
  async def handle(self, command: CreateUserCommand) -> int:
91
94
  # ... persist the user, return its id
92
95
  return 42
93
96
 
97
+
94
98
  @inject
95
99
  async def main(bus: CommandBus[int]) -> None:
96
100
  command = CreateUserCommand(name="Ada", email="ada@example.com")
97
101
  user_id = await bus.dispatch(command)
98
102
  print(f"Created user {user_id}")
99
103
 
104
+
100
105
  asyncio.run(main())
101
106
  ```
102
107
 
@@ -1,9 +1,3 @@
1
- from ._core.cq import CQ
2
- from ._core.di import DIAdapter
3
- from ._core.di import NoDI as _NoDI
4
- from ._core.dispatchers.abc import Dispatcher
5
- from ._core.dispatchers.bus import Bus
6
- from ._core.dispatchers.pipe import ContextPipeline, Pipe
7
1
  from ._core.message import (
8
2
  AnyCommandBus,
9
3
  Command,
@@ -14,25 +8,30 @@ from ._core.message import (
14
8
  QueryBus,
15
9
  )
16
10
  from ._core.middleware import Middleware, MiddlewareResult, resolve_handler_source
17
- from ._core.pipetools import ContextCommandPipeline as _ContextCommandPipeline
18
- from ._core.pump import Pump
19
- from ._core.queues.abc import Consumer, Delivery, Producer, Queue
20
- from ._core.queues.memory import MemoryQueue
11
+ from ._core.queuing.pump import Pump
12
+ from ._core.queuing.queues.abc import Consumer, Delivery, Producer, Queue
13
+ from ._core.queuing.queues.memory import MemoryQueue
21
14
  from ._core.related_events import AnyIORelatedEvents, RelatedEvents
15
+ from ._core.routing.command_pipeline import (
16
+ ContextCommandPipeline as _ContextCommandPipeline,
17
+ )
18
+ from ._core.routing.di import DIAdapter
19
+ from ._core.routing.dispatchers.abc import Dispatcher
20
+ from ._core.routing.dispatchers.bus import Bus
21
+ from ._core.routing.dispatchers.pipe import ContextPipeline, Pipe
22
+ from ._core.routing.router import Router
22
23
 
23
24
  __all__ = (
24
- "__cq__",
25
25
  "AnyCommandBus",
26
26
  "AnyIORelatedEvents",
27
27
  "Bus",
28
- "CQ",
29
28
  "Command",
30
29
  "CommandBus",
31
30
  "Consumer",
32
31
  "ContextCommandPipeline",
33
32
  "ContextPipeline",
34
- "Delivery",
35
33
  "DIAdapter",
34
+ "Delivery",
36
35
  "Dispatcher",
37
36
  "Event",
38
37
  "EventBus",
@@ -46,6 +45,8 @@ __all__ = (
46
45
  "QueryBus",
47
46
  "Queue",
48
47
  "RelatedEvents",
48
+ "Router",
49
+ "__router__",
49
50
  "command_handler",
50
51
  "event_handler",
51
52
  "new_command_bus",
@@ -55,28 +56,30 @@ __all__ = (
55
56
  "resolve_handler_source",
56
57
  )
57
58
 
59
+
58
60
  try:
59
- from .ext.injection import InjectionAdapter as _InjectionAdapter
61
+ from .ext.injection import InjectionAdapter
60
62
 
61
63
  except ImportError: # pragma: no cover
62
- __cq__ = CQ(_NoDI())
64
+ __router__ = Router()
63
65
 
64
66
  else:
65
- __cq__ = CQ(_InjectionAdapter())
67
+ __router__ = Router(InjectionAdapter())
68
+ del InjectionAdapter
66
69
 
67
- __cq__.register_defaults()
70
+ __router__.register_defaults()
68
71
 
69
- command_handler = __cq__.command_handler
70
- event_handler = __cq__.event_handler
71
- query_handler = __cq__.query_handler
72
+ command_handler = __router__.command_handler
73
+ event_handler = __router__.event_handler
74
+ query_handler = __router__.query_handler
72
75
 
73
- new_command_bus = __cq__.new_command_bus
74
- new_event_bus = __cq__.new_event_bus
75
- new_query_bus = __cq__.new_query_bus
76
+ new_command_bus = __router__.new_command_bus
77
+ new_event_bus = __router__.new_event_bus
78
+ new_query_bus = __router__.new_query_bus
76
79
 
77
80
 
78
81
  class ContextCommandPipeline[C: Command](_ContextCommandPipeline[C]):
79
82
  __slots__ = ()
80
83
 
81
- def __init__(self, di: DIAdapter = __cq__.di) -> None:
84
+ def __init__(self, di: DIAdapter = __router__.di) -> None:
82
85
  super().__init__(di)
@@ -1,6 +1,6 @@
1
1
  from typing import Any
2
2
 
3
- from cq._core.dispatchers.abc import Dispatcher
3
+ from cq._core.routing.dispatchers.abc import Dispatcher
4
4
 
5
5
  Command = object
6
6
  Event = object
@@ -3,7 +3,7 @@ from dataclasses import dataclass, field
3
3
  from inspect import isasyncgenfunction
4
4
  from typing import Any, Concatenate, Self, TypeGuard
5
5
 
6
- from cq._core.handler import HandleFunction, HandlerType
6
+ from cq._core.routing.handler import HandleFunction, HandlerType
7
7
  from cq.exceptions import MiddlewareError
8
8
 
9
9
  type MiddlewareResult[T] = AsyncGenerator[None, T]
@@ -98,7 +98,7 @@ class _GeneratorMiddleware[**P, T]:
98
98
  while True:
99
99
  try:
100
100
  value = await call_next(*args, **kwargs)
101
- except BaseException as exc:
101
+ except BaseException as exc: # noqa: BLE001
102
102
  await generator.athrow(exc)
103
103
  else:
104
104
  await generator.asend(value)
@@ -6,7 +6,7 @@ from typing import Any, Self
6
6
  import anyio
7
7
 
8
8
  from cq._core.middleware import Middleware, MiddlewareGroup
9
- from cq._core.queues.abc import Consumer
9
+ from cq._core.queuing.queues.abc import Consumer
10
10
 
11
11
 
12
12
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
@@ -1,6 +1,7 @@
1
1
  from abc import abstractmethod
2
2
  from collections.abc import AsyncIterable
3
- from typing import AsyncContextManager, Protocol, runtime_checkable
3
+ from contextlib import AbstractAsyncContextManager
4
+ from typing import Protocol, runtime_checkable
4
5
 
5
6
 
6
7
  @runtime_checkable
@@ -16,7 +17,7 @@ class Producer[T](Protocol):
16
17
 
17
18
 
18
19
  @runtime_checkable
19
- class Delivery[T](AsyncContextManager[T], Protocol):
20
+ class Delivery[T](AbstractAsyncContextManager[T], Protocol):
20
21
  __slots__ = ()
21
22
 
22
23
 
@@ -7,8 +7,8 @@ import anyio
7
7
  from anyio.abc import ObjectReceiveStream, ObjectSendStream
8
8
 
9
9
  from cq._core.middleware import Middleware
10
- from cq._core.pump import Pump
11
- from cq._core.queues.abc import Delivery, Queue
10
+ from cq._core.queuing.pump import Pump
11
+ from cq._core.queuing.queues.abc import Delivery, Queue
12
12
 
13
13
 
14
14
  class MemoryQueue[T](Queue[T]):
@@ -17,7 +17,7 @@ class MemoryQueue[T](Queue[T]):
17
17
  __consumer: ObjectReceiveStream[T]
18
18
  __producer: ObjectSendStream[T]
19
19
 
20
- def __init__(self, maxsize: int = 0) -> None:
20
+ def __init__(self, maxsize: float = 0) -> None:
21
21
  self.__producer, self.__consumer = anyio.create_memory_object_stream(maxsize)
22
22
 
23
23
  async def __aenter__(self) -> Self:
@@ -51,10 +51,10 @@ class MemoryQueue[T](Queue[T]):
51
51
  async with (
52
52
  Pump(self, dispatcher, fail_silently)
53
53
  .add_middlewares(*middlewares)
54
- .draining(concurrency=concurrency, graceful=True)
54
+ .draining(concurrency=concurrency, graceful=True),
55
+ self,
55
56
  ):
56
- async with self:
57
- yield self
57
+ yield self
58
58
 
59
59
  async def send(self, message: T, /) -> None:
60
60
  await self.__producer.send(message)
@@ -1,16 +1,16 @@
1
1
  from typing import TYPE_CHECKING, Any, Self, overload
2
2
 
3
- from cq import Dispatcher
4
3
  from cq._core.common.typing import Decorator
5
- from cq._core.di import DIAdapter
6
- from cq._core.dispatchers.lazy import LazyDispatcher
7
- from cq._core.dispatchers.pipe import (
4
+ from cq._core.message import Command, CommandBus, Query, QueryBus
5
+ from cq._core.routing.di import DIAdapter
6
+ from cq._core.routing.dispatchers.abc import Dispatcher
7
+ from cq._core.routing.dispatchers.lazy import LazyDispatcher
8
+ from cq._core.routing.dispatchers.pipe import (
8
9
  ContextPipeline,
9
10
  ConvertMethod,
10
11
  ConvertMethodAsync,
11
12
  ConvertMethodSync,
12
13
  )
13
- from cq._core.message import Command, CommandBus, Query, QueryBus
14
14
 
15
15
 
16
16
  class ContextCommandPipeline[C: Command](ContextPipeline[C]):
@@ -5,15 +5,15 @@ from typing import Any, Protocol, Self, runtime_checkable
5
5
  import anyio
6
6
  from anyio.abc import TaskGroup
7
7
 
8
- from cq._core.dispatchers.abc import BaseDispatcher, Dispatcher
9
- from cq._core.handler import (
8
+ from cq._core.middleware import Middleware
9
+ from cq._core.routing.dispatchers.abc import BaseDispatcher, Dispatcher
10
+ from cq._core.routing.handler import (
10
11
  HandleFunction,
11
12
  HandlerFactory,
12
13
  HandlerRegistry,
13
14
  MultipleHandlerRegistry,
14
15
  SingleHandlerRegistry,
15
16
  )
16
- from cq._core.middleware import Middleware
17
17
 
18
18
  type Listener[T] = Callable[[T], Awaitable[Any]]
19
19
 
@@ -82,7 +82,7 @@ class SimpleBus[I, O](BaseBus[I, O]):
82
82
  async with anyio.create_task_group() as task_group:
83
83
  self._trigger_listeners(message, task_group)
84
84
 
85
- for handler in self._handlers_from(type(message)):
85
+ for handler in self._handlers_from(message.__class__):
86
86
  return await self._invoke(handler, message, handler.fail_silently)
87
87
 
88
88
  return NotImplemented
@@ -98,7 +98,7 @@ class TaskBus[I](BaseBus[I, None]):
98
98
  async with anyio.create_task_group() as task_group:
99
99
  self._trigger_listeners(message, task_group)
100
100
 
101
- for handler in self._handlers_from(type(message)):
101
+ for handler in self._handlers_from(message.__class__):
102
102
  task_group.start_soon(
103
103
  self._invoke,
104
104
  handler,
@@ -2,8 +2,8 @@ from collections.abc import Awaitable, Callable
2
2
  from types import GenericAlias
3
3
  from typing import TypeAliasType
4
4
 
5
- from cq._core.di import DIAdapter
6
- from cq._core.dispatchers.abc import Dispatcher
5
+ from cq._core.routing.di import DIAdapter
6
+ from cq._core.routing.dispatchers.abc import Dispatcher
7
7
 
8
8
 
9
9
  class LazyDispatcher[I, O](Dispatcher[I, O]):
@@ -14,8 +14,8 @@ from typing import (
14
14
  )
15
15
 
16
16
  from cq._core.common.typing import Decorator, Method
17
- from cq._core.dispatchers.abc import BaseDispatcher, Dispatcher
18
17
  from cq._core.middleware import Middleware, MiddlewareGroup
18
+ from cq._core.routing.dispatchers.abc import BaseDispatcher, Dispatcher
19
19
 
20
20
  type ConvertAsync[**P, I, O] = Callable[Concatenate[O, P], Awaitable[I]]
21
21
  type ConvertSync[**P, I, O] = Callable[Concatenate[O, P], I]
@@ -10,7 +10,7 @@ from typing import TYPE_CHECKING, Any, Protocol, Self, overload, runtime_checkab
10
10
  from type_analyzer import MatchingTypesConfig, iter_matching_types, matching_types
11
11
 
12
12
  from cq._core.common.typing import Decorator
13
- from cq._core.di import DIAdapter, NoDI
13
+ from cq._core.routing.di import DIAdapter, NoDI
14
14
 
15
15
  type HandlerType[**P, T] = type[Handler[P, T]]
16
16
  type HandlerFactory[**P, T] = Callable[..., Awaitable[Handler[P, T]]]
@@ -183,7 +183,7 @@ class HandlerDecorator[I, O]:
183
183
  and issubclass(message_or_handler_type, Handler)
184
184
  ):
185
185
  return self.__decorator(
186
- message_or_handler_type,
186
+ message_or_handler_type, # type: ignore[arg-type]
187
187
  fail_silently=fail_silently,
188
188
  )
189
189
 
@@ -1,18 +1,18 @@
1
1
  from collections.abc import KeysView
2
2
  from typing import Any, Self
3
3
 
4
- from cq._core.di import DIAdapter
5
- from cq._core.dispatchers.bus import Bus, SimpleBus, TaskBus
6
- from cq._core.handler import (
4
+ from cq._core.message import Command, Event, Query
5
+ from cq._core.routing.di import DIAdapter, NoDI
6
+ from cq._core.routing.dispatchers.bus import Bus, SimpleBus, TaskBus
7
+ from cq._core.routing.handler import (
7
8
  HandlerDecorator,
8
9
  HandlerRegistry,
9
10
  MultipleHandlerRegistry,
10
11
  SingleHandlerRegistry,
11
12
  )
12
- from cq._core.message import Command, Event, Query
13
13
 
14
14
 
15
- class CQ:
15
+ class Router:
16
16
  __slots__ = ("__command_registry", "__di", "__event_registry", "__query_registry")
17
17
 
18
18
  __command_registry: HandlerRegistry[Command, Any]
@@ -20,8 +20,8 @@ class CQ:
20
20
  __event_registry: HandlerRegistry[Event, Any]
21
21
  __query_registry: HandlerRegistry[Query, Any]
22
22
 
23
- def __init__(self, di: DIAdapter, /) -> None:
24
- self.__di = di
23
+ def __init__(self, di: DIAdapter | None = None, /) -> None:
24
+ self.__di = di or NoDI()
25
25
  self.__command_registry = SingleHandlerRegistry()
26
26
  self.__event_registry = MultipleHandlerRegistry()
27
27
  self.__query_registry = SingleHandlerRegistry()
@@ -1,16 +1,16 @@
1
- from collections.abc import AsyncIterator
1
+ from collections.abc import AsyncIterator, Awaitable, Callable
2
2
  from contextlib import AsyncExitStack
3
3
  from dataclasses import dataclass, field
4
4
  from enum import StrEnum
5
- from typing import Any, Awaitable, Callable
5
+ from typing import Any
6
6
 
7
7
  from injection import Module, adefine_scope, mod
8
8
  from injection.exceptions import ScopeAlreadyDefinedError
9
9
 
10
- from cq._core.di import DIAdapter
11
10
  from cq._core.message import Command, CommandBus, EventBus, QueryBus
12
11
  from cq._core.middleware import Middleware, MiddlewareResult
13
12
  from cq._core.related_events import AnyIORelatedEvents, RelatedEvents
13
+ from cq._core.routing.di import DIAdapter
14
14
 
15
15
  __all__ = ("CQScope", "InjectionAdapter", "InjectionScopeMiddleware")
16
16
 
File without changes
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from contextlib import AbstractAsyncContextManager, AbstractContextManager
3
4
  from dataclasses import dataclass
4
- from typing import TYPE_CHECKING, Any, AsyncContextManager, ContextManager
5
+ from typing import TYPE_CHECKING, Any
5
6
 
6
7
  if TYPE_CHECKING: # pragma: no cover
7
8
  from cq import MiddlewareResult
@@ -11,7 +12,7 @@ __all__ = ("AsyncContextManagerMiddleware", "ContextManagerMiddleware")
11
12
 
12
13
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
13
14
  class AsyncContextManagerMiddleware:
14
- context: AsyncContextManager[Any]
15
+ context: AbstractAsyncContextManager[Any]
15
16
 
16
17
  async def __call__(self, /, *args: Any, **kwargs: Any) -> MiddlewareResult[Any]:
17
18
  async with self.context:
@@ -20,7 +21,7 @@ class AsyncContextManagerMiddleware:
20
21
 
21
22
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
22
23
  class ContextManagerMiddleware:
23
- context: ContextManager[Any]
24
+ context: AbstractContextManager[Any]
24
25
 
25
26
  async def __call__(self, /, *args: Any, **kwargs: Any) -> MiddlewareResult[Any]:
26
27
  with self.context:
File without changes
@@ -49,23 +49,27 @@ from cq import CommandBus, command_handler
49
49
  from dataclasses import dataclass
50
50
  from injection import inject
51
51
 
52
+
52
53
  @dataclass
53
54
  class CreateUserCommand:
54
55
  name: str
55
56
  email: str
56
57
 
58
+
57
59
  @command_handler
58
60
  class CreateUserHandler:
59
61
  async def handle(self, command: CreateUserCommand) -> int:
60
62
  # ... persist the user, return its id
61
63
  return 42
62
64
 
65
+
63
66
  @inject
64
67
  async def main(bus: CommandBus[int]) -> None:
65
68
  command = CreateUserCommand(name="Ada", email="ada@example.com")
66
69
  user_id = await bus.dispatch(command)
67
70
  print(f"Created user {user_id}")
68
71
 
72
+
69
73
  asyncio.run(main())
70
74
  ```
71
75
 
@@ -20,7 +20,7 @@ test = [
20
20
 
21
21
  [project]
22
22
  name = "python-cq"
23
- version = "0.22.0"
23
+ version = "0.23.0"
24
24
  description = "CQRS library for async Python projects."
25
25
  license = "MIT"
26
26
  license-files = ["LICENSE"]
@@ -39,6 +39,7 @@ classifiers = [
39
39
  "Programming Language :: Python :: 3.12",
40
40
  "Programming Language :: Python :: 3.13",
41
41
  "Programming Language :: Python :: 3.14",
42
+ "Programming Language :: Python :: 3.15",
42
43
  "Operating System :: OS Independent",
43
44
  "Intended Audience :: Developers",
44
45
  "Natural Language :: English",
File without changes
File without changes
File without changes