python-cq 0.2.1__tar.gz → 0.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-cq
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: Lightweight CQRS library.
5
5
  Home-page: https://github.com/100nm/python-cq
6
6
  License: MIT
@@ -1,16 +1,23 @@
1
- from ._core.command import (
1
+ from ._core.dispatcher.bus import Bus
2
+ from ._core.dispatcher.pipe import Pipe
3
+ from ._core.dto import DTO
4
+ from ._core.message import (
2
5
  AnyCommandBus,
3
6
  Command,
4
7
  CommandBus,
8
+ Event,
9
+ EventBus,
10
+ Message,
11
+ Query,
12
+ QueryBus,
5
13
  command_handler,
6
- find_command_bus,
14
+ event_handler,
15
+ get_command_bus,
16
+ get_event_bus,
17
+ get_query_bus,
18
+ query_handler,
7
19
  )
8
- from ._core.dispatcher.bus import Bus
9
- from ._core.dispatcher.pipe import Pipe
10
- from ._core.dto import DTO
11
- from ._core.event import Event, EventBus, event_handler, find_event_bus
12
20
  from ._core.middleware import Middleware, MiddlewareResult
13
- from ._core.query import Query, QueryBus, find_query_bus, query_handler
14
21
 
15
22
  __all__ = (
16
23
  "AnyCommandBus",
@@ -20,6 +27,7 @@ __all__ = (
20
27
  "DTO",
21
28
  "Event",
22
29
  "EventBus",
30
+ "Message",
23
31
  "Middleware",
24
32
  "MiddlewareResult",
25
33
  "Pipe",
@@ -27,8 +35,8 @@ __all__ = (
27
35
  "QueryBus",
28
36
  "command_handler",
29
37
  "event_handler",
30
- "find_command_bus",
31
- "find_event_bus",
32
- "find_query_bus",
38
+ "get_command_bus",
39
+ "get_event_bus",
40
+ "get_query_bus",
33
41
  "query_handler",
34
42
  )
@@ -1,7 +1,7 @@
1
1
  import asyncio
2
2
  from abc import ABC, abstractmethod
3
- from collections.abc import Awaitable
4
- from typing import Callable, Protocol, Self, runtime_checkable
3
+ from collections.abc import Awaitable, Callable
4
+ from typing import Protocol, Self, runtime_checkable
5
5
 
6
6
  from cq._core.middleware import Middleware, MiddlewareGroup
7
7
 
@@ -1,11 +1,11 @@
1
1
  import asyncio
2
- from abc import abstractmethod
2
+ from abc import ABC, abstractmethod
3
3
  from collections import defaultdict
4
- from collections.abc import Callable
4
+ from collections.abc import Awaitable, Callable
5
5
  from dataclasses import dataclass, field
6
6
  from inspect import isclass
7
7
  from types import GenericAlias
8
- from typing import Protocol, Self, TypeAliasType, runtime_checkable
8
+ from typing import Any, Protocol, Self, TypeAliasType, runtime_checkable
9
9
 
10
10
  import injection
11
11
 
@@ -14,6 +14,8 @@ from cq._core.dispatcher.base import BaseDispatcher, Dispatcher
14
14
  type HandlerType[**P, T] = type[Handler[P, T]]
15
15
  type HandlerFactory[**P, T] = Callable[..., Handler[P, T]]
16
16
 
17
+ type Listener[T] = Callable[[T], Awaitable[Any]]
18
+
17
19
  type BusType[I, O] = type[Bus[I, O]]
18
20
 
19
21
 
@@ -34,6 +36,10 @@ class Bus[I, O](Dispatcher[I, O], Protocol):
34
36
  def subscribe(self, input_type: type[I], factory: HandlerFactory[[I], O]) -> Self:
35
37
  raise NotImplementedError
36
38
 
39
+ @abstractmethod
40
+ def add_listeners(self, *listeners: Listener[I]) -> Self:
41
+ raise NotImplementedError
42
+
37
43
 
38
44
  @dataclass(eq=False, frozen=True, slots=True)
39
45
  class SubscriberDecorator[I, O]:
@@ -59,7 +65,24 @@ class SubscriberDecorator[I, O]:
59
65
  return self.injection_module.find_instance(self.bus_type)
60
66
 
61
67
 
62
- class SimpleBus[I, O](BaseDispatcher[I, O], Bus[I, O]):
68
+ class BaseBus[I, O](BaseDispatcher[I, O], Bus[I, O], ABC):
69
+ __slots__ = ("__listeners",)
70
+
71
+ __listeners: list[Listener[I]]
72
+
73
+ def __init__(self) -> None:
74
+ super().__init__()
75
+ self.__listeners = []
76
+
77
+ def add_listeners(self, *listeners: Listener[I]) -> Self:
78
+ self.__listeners.extend(listeners)
79
+ return self
80
+
81
+ async def _trigger_listeners(self, input_value: I, /) -> None:
82
+ await asyncio.gather(*(listener(input_value) for listener in self.__listeners))
83
+
84
+
85
+ class SimpleBus[I, O](BaseBus[I, O]):
63
86
  __slots__ = ("__handlers",)
64
87
 
65
88
  __handlers: dict[type[I], HandlerFactory[[I], O]]
@@ -69,6 +92,7 @@ class SimpleBus[I, O](BaseDispatcher[I, O], Bus[I, O]):
69
92
  self.__handlers = {}
70
93
 
71
94
  async def dispatch(self, input_value: I, /) -> O:
95
+ await self._trigger_listeners(input_value)
72
96
  input_type = type(input_value)
73
97
 
74
98
  try:
@@ -91,7 +115,7 @@ class SimpleBus[I, O](BaseDispatcher[I, O], Bus[I, O]):
91
115
  return self
92
116
 
93
117
 
94
- class TaskBus[I](BaseDispatcher[I, None], Bus[I, None]):
118
+ class TaskBus[I](BaseBus[I, None]):
95
119
  __slots__ = ("__handlers",)
96
120
 
97
121
  __handlers: dict[type[I], list[HandlerFactory[[I], None]]]
@@ -101,6 +125,7 @@ class TaskBus[I](BaseDispatcher[I, None], Bus[I, None]):
101
125
  self.__handlers = defaultdict(list)
102
126
 
103
127
  async def dispatch(self, input_value: I, /) -> None:
128
+ await self._trigger_listeners(input_value)
104
129
  handler_factories = self.__handlers.get(type(input_value))
105
130
 
106
131
  if not handler_factories:
@@ -1,6 +1,6 @@
1
- from collections.abc import Callable
1
+ from collections.abc import Awaitable, Callable
2
2
  from dataclasses import dataclass, field
3
- from typing import Any, Awaitable, Self
3
+ from typing import Any, Self
4
4
 
5
5
  from cq._core.dispatcher.base import BaseDispatcher, Dispatcher
6
6
 
@@ -0,0 +1,54 @@
1
+ from abc import ABC
2
+ from typing import Any
3
+
4
+ import injection
5
+
6
+ from cq._core.dispatcher.bus import Bus, SimpleBus, SubscriberDecorator, TaskBus
7
+ from cq._core.dto import DTO
8
+
9
+
10
+ class Message(DTO, ABC):
11
+ __slots__ = ()
12
+
13
+
14
+ class Command(Message, ABC):
15
+ __slots__ = ()
16
+
17
+
18
+ class Event(Message, ABC):
19
+ __slots__ = ()
20
+
21
+
22
+ class Query(Message, ABC):
23
+ __slots__ = ()
24
+
25
+
26
+ type CommandBus[T] = Bus[Command, T]
27
+ type EventBus = Bus[Event, None]
28
+ type QueryBus[T] = Bus[Query, T]
29
+
30
+ AnyCommandBus = CommandBus[Any]
31
+
32
+
33
+ command_handler: SubscriberDecorator[Command, Any] = SubscriberDecorator(CommandBus)
34
+ event_handler: SubscriberDecorator[Event, None] = SubscriberDecorator(EventBus)
35
+ query_handler: SubscriberDecorator[Query, Any] = SubscriberDecorator(QueryBus)
36
+
37
+ injection.set_constant(SimpleBus(), CommandBus, alias=True)
38
+ injection.set_constant(TaskBus(), EventBus, alias=True)
39
+ injection.set_constant(SimpleBus(), QueryBus, alias=True)
40
+
41
+
42
+ @injection.inject
43
+ def get_command_bus[T](bus: CommandBus[T] = NotImplemented, /) -> CommandBus[T]:
44
+ return bus
45
+
46
+
47
+ @injection.inject
48
+ def get_event_bus(bus: EventBus = NotImplemented, /) -> EventBus:
49
+ return bus
50
+
51
+
52
+ @injection.inject
53
+ def get_query_bus[T](bus: QueryBus[T] = NotImplemented, /) -> QueryBus[T]:
54
+ return bus
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-cq"
3
- version = "0.2.1"
3
+ version = "0.3.0"
4
4
  description = "Lightweight CQRS library."
5
5
  license = "MIT"
6
6
  authors = ["remimd"]
@@ -1,22 +0,0 @@
1
- from abc import ABC
2
- from typing import Any
3
-
4
- import injection
5
-
6
- from cq._core.dispatcher.bus import Bus, SimpleBus, SubscriberDecorator
7
- from cq._core.dto import DTO
8
-
9
-
10
- class Command(DTO, ABC):
11
- __slots__ = ()
12
-
13
-
14
- type CommandBus[T] = Bus[Command, T]
15
- AnyCommandBus = CommandBus[Any]
16
- command_handler: SubscriberDecorator[Command, Any] = SubscriberDecorator(CommandBus)
17
-
18
- injection.set_constant(SimpleBus(), CommandBus, alias=True)
19
-
20
-
21
- def find_command_bus[T]() -> CommandBus[T]:
22
- return injection.find_instance(CommandBus)
@@ -1,20 +0,0 @@
1
- from abc import ABC
2
-
3
- import injection
4
-
5
- from cq._core.dispatcher.bus import Bus, SubscriberDecorator, TaskBus
6
- from cq._core.dto import DTO
7
-
8
-
9
- class Event(DTO, ABC):
10
- __slots__ = ()
11
-
12
-
13
- type EventBus = Bus[Event, None]
14
- event_handler: SubscriberDecorator[Event, None] = SubscriberDecorator(EventBus)
15
-
16
- injection.set_constant(TaskBus(), EventBus, alias=True)
17
-
18
-
19
- def find_event_bus() -> EventBus:
20
- return injection.find_instance(EventBus)
@@ -1,21 +0,0 @@
1
- from abc import ABC
2
- from typing import Any
3
-
4
- import injection
5
-
6
- from cq._core.dispatcher.bus import Bus, SimpleBus, SubscriberDecorator
7
- from cq._core.dto import DTO
8
-
9
-
10
- class Query(DTO, ABC):
11
- __slots__ = ()
12
-
13
-
14
- type QueryBus[T] = Bus[Query, T]
15
- query_handler: SubscriberDecorator[Query, Any] = SubscriberDecorator(QueryBus)
16
-
17
- injection.set_constant(SimpleBus(), QueryBus, alias=True)
18
-
19
-
20
- def find_query_bus[T]() -> QueryBus[T]:
21
- return injection.find_instance(QueryBus)
File without changes
File without changes
File without changes
File without changes