python-cqrs 0.1.4__tar.gz → 0.2.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 (48) hide show
  1. {python_cqrs-0.1.4/src/python_cqrs.egg-info → python_cqrs-0.2.0}/PKG-INFO +1 -1
  2. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/pyproject.toml +1 -1
  3. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/__init__.py +8 -5
  4. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/adapters/kafka.py +22 -4
  5. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/dispatcher/dispatcher.py +2 -1
  6. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/events/event_emitter.py +2 -1
  7. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/events/map.py +7 -4
  8. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/message_brokers/protocol.py +1 -1
  9. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/middlewares/base.py +2 -1
  10. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/outbox/repository.py +7 -1
  11. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/outbox/sqlalchemy.py +22 -5
  12. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/requests/map.py +6 -1
  13. {python_cqrs-0.1.4 → python_cqrs-0.2.0/src/python_cqrs.egg-info}/PKG-INFO +1 -1
  14. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/LICENSE +0 -0
  15. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/README.md +0 -0
  16. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/setup.cfg +0 -0
  17. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/adapters/__init__.py +0 -0
  18. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/adapters/amqp.py +0 -0
  19. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/compressors/__init__.py +0 -0
  20. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/compressors/protocol.py +0 -0
  21. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/compressors/zlib.py +0 -0
  22. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/container/__init__.py +0 -0
  23. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/container/di.py +0 -0
  24. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/container/protocol.py +0 -0
  25. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/dispatcher/__init__.py +0 -0
  26. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/events/__init__.py +0 -0
  27. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/events/bootstrap.py +0 -0
  28. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/events/event.py +0 -0
  29. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/events/event_handler.py +0 -0
  30. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/mediator.py +0 -0
  31. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/message_brokers/__init__.py +0 -0
  32. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/message_brokers/amqp.py +0 -0
  33. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/message_brokers/devnull.py +0 -0
  34. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/message_brokers/kafka.py +0 -0
  35. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/middlewares/__init__.py +0 -0
  36. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/middlewares/logging.py +0 -0
  37. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/outbox/__init__.py +0 -0
  38. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/outbox/producer.py +0 -0
  39. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/outbox/protocol.py +0 -0
  40. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/requests/__init__.py +0 -0
  41. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/requests/bootstrap.py +0 -0
  42. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/requests/request.py +0 -0
  43. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/requests/request_handler.py +0 -0
  44. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/cqrs/response.py +0 -0
  45. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/python_cqrs.egg-info/SOURCES.txt +0 -0
  46. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/python_cqrs.egg-info/dependency_links.txt +0 -0
  47. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/python_cqrs.egg-info/requires.txt +0 -0
  48. {python_cqrs-0.1.4 → python_cqrs-0.2.0}/src/python_cqrs.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-cqrs
3
- Version: 0.1.4
3
+ Version: 0.2.0
4
4
  Summary: Python CQRS pattern implementation
5
5
  Author-email: Vadim Kozyrevskiy <vadikko2@mail.ru>
6
6
  Maintainer-email: Vadim Kozyrevskiy <vadikko2@mail.ru>
@@ -27,7 +27,7 @@ maintainers = [{name = "Vadim Kozyrevskiy", email = "vadikko2@mail.ru"}]
27
27
  name = "python-cqrs"
28
28
  readme = "README.md"
29
29
  requires-python = ">=3.10"
30
- version = "0.1.4"
30
+ version = "0.2.0"
31
31
 
32
32
  [project.optional-dependencies]
33
33
  dev = [
@@ -1,17 +1,17 @@
1
1
  from cqrs.compressors.protocol import Compressor
2
2
  from cqrs.compressors.zlib import ZlibCompressor
3
- from cqrs.events.event import DomainEvent, ECSTEvent, NotificationEvent
3
+ from cqrs.container.di import DIContainer
4
+ from cqrs.container.protocol import Container
5
+ from cqrs.events.event import DomainEvent, ECSTEvent, Event, NotificationEvent
4
6
  from cqrs.events.event_emitter import EventEmitter
5
- from cqrs.events.event_handler import EventHandler
7
+ from cqrs.events.event_handler import EventHandler, SyncEventHandler
6
8
  from cqrs.mediator import EventMediator, RequestMediator
7
9
  from cqrs.outbox.producer import EventProducer
8
10
  from cqrs.outbox.repository import OutboxedEventRepository
9
11
  from cqrs.outbox.sqlalchemy import SqlAlchemyOutboxedEventRepository
10
12
  from cqrs.requests.request import Request
11
- from cqrs.requests.request_handler import RequestHandler
13
+ from cqrs.requests.request_handler import RequestHandler, SyncRequestHandler
12
14
  from cqrs.response import Response
13
- from cqrs.container.protocol import Container
14
- from cqrs.container.di import DIContainer
15
15
 
16
16
  __all__ = (
17
17
  "RequestMediator",
@@ -19,10 +19,13 @@ __all__ = (
19
19
  "DomainEvent",
20
20
  "NotificationEvent",
21
21
  "ECSTEvent",
22
+ "Event",
22
23
  "EventEmitter",
23
24
  "EventHandler",
25
+ "SyncEventHandler",
24
26
  "Request",
25
27
  "RequestHandler",
28
+ "SyncRequestHandler",
26
29
  "Response",
27
30
  "OutboxedEventRepository",
28
31
  "SqlAlchemyOutboxedEventRepository",
@@ -25,8 +25,19 @@ _retry = functools.partial(
25
25
  is_async=True,
26
26
  )
27
27
 
28
- SecurityProtocol: typing.TypeAlias = typing.Literal["PLAINTEXT", "SSL", "SASL_PLAINTEXT", "SASL_SSL"]
29
- SaslMechanism: typing.TypeAlias = typing.Literal["PLAIN", "GSSAPI", "SCRAM-SHA-256", "SCRAM-SHA-512", "OAUTHBEARER"]
28
+ SecurityProtocol: typing.TypeAlias = typing.Literal[
29
+ "PLAINTEXT",
30
+ "SSL",
31
+ "SASL_PLAINTEXT",
32
+ "SASL_SSL",
33
+ ]
34
+ SaslMechanism: typing.TypeAlias = typing.Literal[
35
+ "PLAIN",
36
+ "GSSAPI",
37
+ "SCRAM-SHA-256",
38
+ "SCRAM-SHA-512",
39
+ "OAUTHBEARER",
40
+ ]
30
41
 
31
42
  logger = logging.getLogger("cqrs")
32
43
  logger.setLevel(logging.DEBUG)
@@ -74,7 +85,9 @@ class KafkaProducer(metaclass=_Singleton):
74
85
  Produces event to kafka broker.
75
86
  Tries to reconnect if connect has been lost or has not been opened.
76
87
  """
77
- await _retry(tries=self._retry_count, delay=self._retry_delay)(self._produce)(message)
88
+ await _retry(tries=self._retry_count, delay=self._retry_delay)(self._produce)(
89
+ message,
90
+ )
78
91
 
79
92
 
80
93
  def kafka_producer_factory(
@@ -99,4 +112,9 @@ def kafka_producer_factory(
99
112
  sasl_plain_password=password,
100
113
  loop=loop,
101
114
  )
102
- return KafkaProducer(producer=producer, topics=topics, retry_count=retry_count, retry_delay=retry_delay)
115
+ return KafkaProducer(
116
+ producer=producer,
117
+ topics=topics,
118
+ retry_count=retry_count,
119
+ retry_delay=retry_delay,
120
+ )
@@ -27,7 +27,8 @@ _EventHandler: typing.TypeAlias = (
27
27
  )
28
28
 
29
29
 
30
- class RequestHandlerDoesNotExist(Exception): ...
30
+ class RequestHandlerDoesNotExist(Exception):
31
+ pass
31
32
 
32
33
 
33
34
  class RequestDispatchResult(pydantic.BaseModel, typing.Generic[_Resp]):
@@ -30,7 +30,8 @@ class EventEmitter:
30
30
  self._message_broker = message_broker
31
31
 
32
32
  @functools.singledispatchmethod
33
- async def emit(self, event: event_model.Event) -> None: ...
33
+ async def emit(self, event: event_model.Event) -> None:
34
+ pass
34
35
 
35
36
  @emit.register
36
37
  async def _(self, event: event_model.DomainEvent) -> None:
@@ -2,17 +2,20 @@ import typing
2
2
 
3
3
  from cqrs.events import event, event_handler
4
4
 
5
+ TEventHandler = typing.TypeVar(
6
+ "TEventHandler",
7
+ bound=typing.Type[event_handler.EventHandler | event_handler.SyncEventHandler],
8
+ )
9
+
5
10
  _KT = typing.TypeVar("_KT", bound=typing.Type[event.Event])
6
- _VT: typing.TypeAlias = typing.List[
7
- typing.Type[event_handler.EventHandler[event.Event]]
8
- ]
11
+ _VT: typing.TypeAlias = typing.List[TEventHandler]
9
12
 
10
13
 
11
14
  class EventMap(typing.Dict[_KT, _VT]):
12
15
  def bind(
13
16
  self,
14
17
  event_type: _KT,
15
- handler_type: typing.Type[event_handler.EventHandler],
18
+ handler_type: TEventHandler,
16
19
  ) -> None:
17
20
  if event_type not in self:
18
21
  self[event_type] = [handler_type]
@@ -19,4 +19,4 @@ class MessageBroker(typing.Protocol):
19
19
  """
20
20
 
21
21
  async def send_message(self, message: Message) -> None:
22
- ...
22
+ raise NotImplementedError
@@ -9,7 +9,8 @@ HandleType = typing.Callable[[_Req], typing.Awaitable[_Res] | _Res]
9
9
 
10
10
 
11
11
  class Middleware(typing.Protocol[_Req, _Res]):
12
- async def __call__(self, request: _Req, handle: HandleType) -> _Res: ...
12
+ async def __call__(self, request: _Req, handle: HandleType) -> _Res:
13
+ raise NotImplementedError
13
14
 
14
15
 
15
16
  class MiddlewareChain:
@@ -34,7 +34,12 @@ class OutboxedEventRepository(abc.ABC, typing.Generic[Session]):
34
34
  """end transaction"""
35
35
 
36
36
  @abc.abstractmethod
37
- def add(self, session: Session, event: Event) -> None:
37
+ def add(
38
+ self,
39
+ session: Session,
40
+ event: Event,
41
+ topic: typing.Text | None = None,
42
+ ) -> None:
38
43
  """Add an event to the repository."""
39
44
 
40
45
  @abc.abstractmethod
@@ -46,6 +51,7 @@ class OutboxedEventRepository(abc.ABC, typing.Generic[Session]):
46
51
  self,
47
52
  session: Session,
48
53
  batch_size: int = 100,
54
+ topic: typing.Text | None = None,
49
55
  ) -> typing.List[Event]:
50
56
  """Get many events from the repository."""
51
57
 
@@ -78,7 +78,12 @@ class OutboxModel(Base):
78
78
  nullable=False,
79
79
  comment="Event name",
80
80
  )
81
-
81
+ topic = sqlalchemy.Column(
82
+ sqlalchemy.String(255),
83
+ nullable=False,
84
+ comment="Event topic",
85
+ default="",
86
+ )
82
87
  created_at = sqlalchemy.Column(
83
88
  sqlalchemy.DateTime,
84
89
  nullable=False,
@@ -98,7 +103,11 @@ class OutboxModel(Base):
98
103
  }
99
104
 
100
105
  @classmethod
101
- def get_batch_query(cls, size: int) -> sqlalchemy.Select:
106
+ def get_batch_query(
107
+ cls,
108
+ size: int,
109
+ topic: typing.Text | None = None,
110
+ ) -> sqlalchemy.Select:
102
111
  return (
103
112
  sqlalchemy.select(cls)
104
113
  .select_from(cls)
@@ -111,6 +120,7 @@ class OutboxModel(Base):
111
120
  ],
112
121
  ),
113
122
  cls.flush_counter < MAX_FLUSH_COUNTER_VALUE,
123
+ (cls.topic == topic) if topic is not None else sqlalchemy.true(),
114
124
  ),
115
125
  )
116
126
  .order_by(cls.status_sorting_case().asc())
@@ -184,7 +194,12 @@ class SqlAlchemyOutboxedEventRepository(
184
194
  EventType.ECST_EVENT: ev.ECSTEvent,
185
195
  }
186
196
 
187
- def add(self, session: sql_session.AsyncSession, event: repository.Event) -> None:
197
+ def add(
198
+ self,
199
+ session: sql_session.AsyncSession,
200
+ event,
201
+ topic: typing.Text | None = None,
202
+ ) -> None:
188
203
  bytes_payload = orjson.dumps(event.payload)
189
204
  if self._compressor:
190
205
  bytes_payload = self._compressor.compress(bytes_payload)
@@ -196,6 +211,7 @@ class SqlAlchemyOutboxedEventRepository(
196
211
  event_name=event.event_name,
197
212
  created_at=event.event_timestamp,
198
213
  payload=bytes_payload,
214
+ topic=topic or "",
199
215
  ),
200
216
  )
201
217
 
@@ -215,9 +231,10 @@ class SqlAlchemyOutboxedEventRepository(
215
231
  self,
216
232
  session: sql_session.AsyncSession,
217
233
  batch_size: int = 100,
234
+ topic: typing.Text | None = None,
218
235
  ) -> typing.List[repository.Event]:
219
236
  events: typing.Sequence[OutboxModel] = (
220
- (await session.execute(OutboxModel.get_batch_query(batch_size)))
237
+ (await session.execute(OutboxModel.get_batch_query(batch_size, topic)))
221
238
  .scalars()
222
239
  .all()
223
240
  )
@@ -234,7 +251,7 @@ class SqlAlchemyOutboxedEventRepository(
234
251
  async def get_one(
235
252
  self,
236
253
  session: sql_session.AsyncSession,
237
- event_id: uuid.UUID,
254
+ event_id,
238
255
  ) -> repository.Event | None:
239
256
  event: OutboxModel | None = (
240
257
  await session.execute(OutboxModel.get_event_query(event_id))
@@ -3,7 +3,12 @@ import typing
3
3
  from cqrs.requests import request, request_handler
4
4
 
5
5
  _KT = typing.TypeVar("_KT", bound=typing.Type[request.Request])
6
- _VT = typing.TypeVar("_VT", bound=typing.Type[request_handler.RequestHandler])
6
+ _VT = typing.TypeVar(
7
+ "_VT",
8
+ bound=typing.Type[
9
+ request_handler.RequestHandler | request_handler.SyncRequestHandler
10
+ ],
11
+ )
7
12
 
8
13
 
9
14
  class RequestMap(typing.Dict[_KT, _VT]):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-cqrs
3
- Version: 0.1.4
3
+ Version: 0.2.0
4
4
  Summary: Python CQRS pattern implementation
5
5
  Author-email: Vadim Kozyrevskiy <vadikko2@mail.ru>
6
6
  Maintainer-email: Vadim Kozyrevskiy <vadikko2@mail.ru>
File without changes
File without changes
File without changes