python-cqrs 3.0.1__tar.gz → 4.0.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 (60) hide show
  1. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/PKG-INFO +1 -1
  2. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/pyproject.toml +1 -1
  3. python_cqrs-4.0.0/src/cqrs/adapters/amqp.py +83 -0
  4. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/adapters/kafka.py +7 -11
  5. python_cqrs-4.0.0/src/cqrs/adapters/protocol.py +28 -0
  6. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/message_brokers/amqp.py +3 -3
  7. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/message_brokers/kafka.py +2 -2
  8. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/producer.py +1 -1
  9. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/python_cqrs.egg-info/PKG-INFO +1 -1
  10. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/python_cqrs.egg-info/SOURCES.txt +1 -0
  11. python_cqrs-3.0.1/src/cqrs/adapters/amqp.py +0 -63
  12. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/LICENSE +0 -0
  13. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/README.md +0 -0
  14. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/setup.cfg +0 -0
  15. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/__init__.py +0 -0
  16. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/adapters/__init__.py +0 -0
  17. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/compressors/__init__.py +0 -0
  18. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/compressors/protocol.py +0 -0
  19. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/compressors/zlib.py +0 -0
  20. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/container/__init__.py +0 -0
  21. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/container/di.py +0 -0
  22. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/container/protocol.py +0 -0
  23. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/decoders/__init__.py +0 -0
  24. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/decoders/kafka/__init__.py +0 -0
  25. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/decoders/kafka/empty_message.py +0 -0
  26. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/deserializers/__init__.py +0 -0
  27. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/deserializers/json.py +0 -0
  28. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/deserializers/protobuf.py +0 -0
  29. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/dispatcher/__init__.py +0 -0
  30. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/dispatcher/dispatcher.py +0 -0
  31. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/events/__init__.py +0 -0
  32. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/events/bootstrap.py +0 -0
  33. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/events/event.py +0 -0
  34. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/events/event_emitter.py +0 -0
  35. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/events/event_handler.py +0 -0
  36. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/events/map.py +0 -0
  37. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/mediator.py +0 -0
  38. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/message_brokers/__init__.py +0 -0
  39. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/message_brokers/devnull.py +0 -0
  40. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/message_brokers/protocol.py +0 -0
  41. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/middlewares/__init__.py +0 -0
  42. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/middlewares/base.py +0 -0
  43. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/middlewares/logging.py +0 -0
  44. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/outbox/__init__.py +0 -0
  45. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/outbox/map.py +0 -0
  46. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/outbox/mock.py +0 -0
  47. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/outbox/repository.py +0 -0
  48. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/outbox/sqlalchemy.py +0 -0
  49. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/requests/__init__.py +0 -0
  50. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/requests/bootstrap.py +0 -0
  51. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/requests/map.py +0 -0
  52. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/requests/request.py +0 -0
  53. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/requests/request_handler.py +0 -0
  54. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/response.py +0 -0
  55. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/serializers/__init__.py +0 -0
  56. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/serializers/default.py +0 -0
  57. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/serializers/protobuf.py +0 -0
  58. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/python_cqrs.egg-info/dependency_links.txt +0 -0
  59. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/python_cqrs.egg-info/requires.txt +0 -0
  60. {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/python_cqrs.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-cqrs
3
- Version: 3.0.1
3
+ Version: 4.0.0
4
4
  Summary: Python CQRS pattern implementation
5
5
  Author-email: Vadim Kozyrevskiy <vadikko2@mail.ru>, Dmitry Kutlubaev <kutlubaev00@mail.ru>
6
6
  Maintainer-email: Vadim Kozyrevskiy <vadikko2@mail.ru>
@@ -29,7 +29,7 @@ maintainers = [{name = "Vadim Kozyrevskiy", email = "vadikko2@mail.ru"}]
29
29
  name = "python-cqrs"
30
30
  readme = "README.md"
31
31
  requires-python = ">=3.10"
32
- version = "3.0.1"
32
+ version = "4.0.0"
33
33
 
34
34
  [project.optional-dependencies]
35
35
  dev = [
@@ -0,0 +1,83 @@
1
+ import asyncio
2
+ import typing
3
+ from functools import partial
4
+
5
+ from cqrs.adapters import protocol
6
+
7
+ import aio_pika
8
+ from aio_pika import abc, pool
9
+
10
+
11
+ async def connection_pool_factory(url: str) -> abc.AbstractRobustConnection:
12
+ return await aio_pika.connect_robust(url=url)
13
+
14
+
15
+ async def channel_pool_factory(
16
+ connection_pool: pool.Pool[aio_pika.abc.AbstractConnection],
17
+ ) -> aio_pika.abc.AbstractChannel:
18
+ async with connection_pool.acquire() as connection:
19
+ return await connection.channel()
20
+
21
+
22
+ class AMQPPublisher(protocol.AMQPPublisher):
23
+ def __init__(self, channel_pool: pool.Pool[aio_pika.abc.AbstractChannel]):
24
+ self.channel_pool = channel_pool
25
+
26
+ async def publish(self, message: abc.AbstractMessage, queue_name: str, exchange_name: str) -> None:
27
+ async with self.channel_pool.acquire() as channel:
28
+ queue = await channel.declare_queue(queue_name)
29
+ exchange = await channel.declare_exchange(exchange_name, type="direct", auto_delete=True)
30
+ await queue.bind(exchange=exchange, routing_key=queue_name)
31
+ await exchange.publish(message=message, routing_key=queue_name)
32
+
33
+
34
+ class AMQPConsumer(protocol.AMQPConsumer):
35
+ def __init__(self, channel_pool: pool.Pool[aio_pika.abc.AbstractChannel]):
36
+ self.channel_pool = channel_pool
37
+
38
+ async def consume(
39
+ self,
40
+ handler: typing.Callable[[abc.AbstractIncomingMessage], typing.Awaitable[None]],
41
+ queue_name: str,
42
+ ) -> None:
43
+ async with self.channel_pool.acquire() as channel:
44
+ await channel.set_qos(prefetch_count=1)
45
+ queue = await channel.declare_queue(queue_name)
46
+ await queue.consume(handler)
47
+ await asyncio.Future()
48
+
49
+
50
+ def amqp_publisher_factory(
51
+ url: typing.Text,
52
+ max_connection_pool_size: int = 2,
53
+ max_channel_pool_size: int = 10,
54
+ ) -> AMQPPublisher:
55
+ max_connection_pool_size = max_connection_pool_size
56
+ max_channel_pool_size = max_channel_pool_size
57
+ connection_pool = pool.Pool(
58
+ partial(connection_pool_factory, url=url),
59
+ max_size=max_connection_pool_size,
60
+ )
61
+ channel_pool = pool.Pool(
62
+ partial(channel_pool_factory, connection_pool=connection_pool),
63
+ max_size=max_channel_pool_size,
64
+ )
65
+ return AMQPPublisher(channel_pool=channel_pool)
66
+
67
+
68
+ def amqp_consumer_factory(
69
+ url: typing.Text,
70
+ max_connection_pool_size: int = 2,
71
+ max_channel_pool_size: int = 10,
72
+ ) -> AMQPConsumer:
73
+ max_connection_pool_size = max_connection_pool_size
74
+ max_channel_pool_size = max_channel_pool_size
75
+ connection_pool = pool.Pool(
76
+ partial(connection_pool_factory, url=url),
77
+ max_size=max_connection_pool_size,
78
+ )
79
+ channel_pool = pool.Pool(
80
+ partial(channel_pool_factory, connection_pool=connection_pool),
81
+ max_size=max_channel_pool_size,
82
+ )
83
+ return AMQPConsumer(channel_pool=channel_pool)
@@ -1,13 +1,16 @@
1
1
  import asyncio
2
2
  import functools
3
3
  import logging
4
+ import ssl
4
5
  import typing
5
6
 
7
+ from cqrs.adapters import protocol
8
+ from cqrs.serializers import default
9
+
6
10
  import aiokafka
7
11
  import retry_async
8
12
  from aiokafka import errors
9
13
 
10
- from cqrs.serializers import default
11
14
 
12
15
  __all__ = (
13
16
  "KafkaProducer",
@@ -44,16 +47,7 @@ logger.setLevel(logging.DEBUG)
44
47
  Serializer = typing.Callable[[typing.Any], typing.ByteString | None]
45
48
 
46
49
 
47
- class _Singleton(type):
48
- _instances = {}
49
-
50
- def __call__(cls, *args, **kwargs):
51
- if cls not in cls._instances:
52
- cls._instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
53
- return cls._instances[cls]
54
-
55
-
56
- class KafkaProducer(metaclass=_Singleton):
50
+ class KafkaProducer(protocol.KafkaProducer):
57
51
  def __init__(
58
52
  self,
59
53
  producer: aiokafka.AIOKafkaProducer,
@@ -89,6 +83,7 @@ def kafka_producer_factory(
89
83
  dsn: typing.Text,
90
84
  security_protocol: SecurityProtocol = "PLAINTEXT",
91
85
  sasl_mechanism: SaslMechanism = "PLAIN",
86
+ ssl_context: ssl.SSLContext | None = None,
92
87
  retry_count: int = 3,
93
88
  retry_delay: int = 1,
94
89
  user: typing.Text | None = None,
@@ -105,6 +100,7 @@ def kafka_producer_factory(
105
100
  sasl_mechanism=sasl_mechanism,
106
101
  sasl_plain_username=user,
107
102
  sasl_plain_password=password,
103
+ ssl_context=ssl_context,
108
104
  loop=loop,
109
105
  )
110
106
  return KafkaProducer(
@@ -0,0 +1,28 @@
1
+ import typing
2
+
3
+ import aio_pika
4
+
5
+
6
+ class KafkaProducer(typing.Protocol):
7
+ async def produce(
8
+ self,
9
+ topic: typing.Text,
10
+ message: typing.Any,
11
+ ) -> None: ...
12
+
13
+
14
+ class AMQPPublisher(typing.Protocol):
15
+ async def publish(
16
+ self,
17
+ message: aio_pika.abc.AbstractMessage,
18
+ queue_name: str,
19
+ exchange_name: str,
20
+ ) -> None: ...
21
+
22
+
23
+ class AMQPConsumer(typing.Protocol):
24
+ async def consume(
25
+ self,
26
+ handler: typing.Callable[[aio_pika.abc.AbstractIncomingMessage], typing.Awaitable[None]],
27
+ queue_name: str,
28
+ ) -> None: ...
@@ -3,13 +3,13 @@ import logging
3
3
  import aio_pika
4
4
  import orjson
5
5
 
6
- from cqrs.adapters import amqp
6
+ from cqrs.adapters import protocol as adapters_protocol
7
7
  from cqrs.message_brokers import protocol
8
8
 
9
9
 
10
10
  class AMQPMessageBroker(protocol.MessageBroker):
11
- def __init__(self, dsn: str, exchange_name: str, pika_log_level: str = "ERROR"):
12
- self.publisher = amqp.AMQPPublisher(url=dsn)
11
+ def __init__(self, publisher: adapters_protocol.AMQPPublisher, exchange_name: str, pika_log_level: str = "ERROR"):
12
+ self.publisher = publisher
13
13
  self.exchange_name = exchange_name
14
14
  logging.getLogger("aiormq").setLevel(pika_log_level)
15
15
  logging.getLogger("aio_pika").setLevel(pika_log_level)
@@ -1,14 +1,14 @@
1
1
  import logging
2
2
  import typing
3
3
 
4
- from cqrs.adapters import kafka
4
+ from cqrs.adapters import protocol as adapters_protocol
5
5
  from cqrs.message_brokers import protocol
6
6
 
7
7
 
8
8
  class KafkaMessageBroker(protocol.MessageBroker):
9
9
  def __init__(
10
10
  self,
11
- producer: kafka.KafkaProducer,
11
+ producer: adapters_protocol.KafkaProducer,
12
12
  aiokafka_log_level: typing.Text = "ERROR",
13
13
  ):
14
14
  self._producer = producer
@@ -37,7 +37,7 @@ class EventProducer:
37
37
  message_name=event.event.event_name,
38
38
  message_id=event.event.event_id,
39
39
  topic=event.topic,
40
- payload=event.event,
40
+ payload=event.event.model_dump(),
41
41
  ),
42
42
  )
43
43
  except Exception as error:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-cqrs
3
- Version: 3.0.1
3
+ Version: 4.0.0
4
4
  Summary: Python CQRS pattern implementation
5
5
  Author-email: Vadim Kozyrevskiy <vadikko2@mail.ru>, Dmitry Kutlubaev <kutlubaev00@mail.ru>
6
6
  Maintainer-email: Vadim Kozyrevskiy <vadikko2@mail.ru>
@@ -8,6 +8,7 @@ src/cqrs/response.py
8
8
  src/cqrs/adapters/__init__.py
9
9
  src/cqrs/adapters/amqp.py
10
10
  src/cqrs/adapters/kafka.py
11
+ src/cqrs/adapters/protocol.py
11
12
  src/cqrs/compressors/__init__.py
12
13
  src/cqrs/compressors/protocol.py
13
14
  src/cqrs/compressors/zlib.py
@@ -1,63 +0,0 @@
1
- import asyncio
2
- import typing
3
- from functools import partial
4
-
5
- import aio_pika
6
- from aio_pika import abc, pool
7
-
8
-
9
- async def connection_pool_factory(url: str) -> abc.AbstractRobustConnection:
10
- return await aio_pika.connect_robust(url=url)
11
-
12
-
13
- async def channel_pool_factory(connection_pool: pool.Pool) -> aio_pika.Channel:
14
- async with connection_pool.acquire() as connection:
15
- return await connection.channel()
16
-
17
-
18
- class AMQPPublisher:
19
- def __init__(self, url: str, max_connection_pool_size=2, max_channel_pool_size=10):
20
- self.url = url
21
- self.max_connection_pool_size = max_connection_pool_size
22
- self.max_channel_pool_size = max_channel_pool_size
23
- self.connection_pool: pool.Pool = pool.Pool(
24
- partial(connection_pool_factory, url=url),
25
- max_size=self.max_connection_pool_size,
26
- )
27
- self.channel_pool: pool.Pool = pool.Pool(
28
- partial(channel_pool_factory, connection_pool=self.connection_pool),
29
- max_size=self.max_channel_pool_size,
30
- )
31
-
32
- async def publish(self, message: abc.AbstractMessage, queue_name: str, exchange_name: str) -> None:
33
- async with self.channel_pool.acquire() as channel:
34
- queue: aio_pika.Queue = await channel.declare_queue(queue_name)
35
- exchange: aio_pika.Exchange = await channel.declare_exchange(exchange_name, type="direct", auto_delete=True)
36
- await queue.bind(exchange=exchange, routing_key=queue_name)
37
- await exchange.publish(message=message, routing_key=queue_name)
38
-
39
-
40
- class AMQPConsumer:
41
- def __init__(self, url: str, max_connection_pool_size=2, max_channel_pool_size=10):
42
- self.url = url
43
- self.max_connection_pool_size = max_connection_pool_size
44
- self.max_channel_pool_size = max_channel_pool_size
45
- self.connection_pool: pool.Pool = pool.Pool(
46
- partial(connection_pool_factory, url=url),
47
- max_size=self.max_connection_pool_size,
48
- )
49
- self.channel_pool: pool.Pool = pool.Pool(
50
- partial(channel_pool_factory, connection_pool=self.connection_pool),
51
- max_size=self.max_channel_pool_size,
52
- )
53
-
54
- async def consume(
55
- self,
56
- handler: typing.Callable[[abc.AbstractIncomingMessage], typing.Awaitable[None]],
57
- queue_name: str,
58
- ) -> None:
59
- async with self.channel_pool.acquire() as channel:
60
- await channel.set_qos(prefetch_count=1)
61
- queue = await channel.declare_queue(queue_name)
62
- await queue._consume(handler)
63
- await asyncio.Future()
File without changes
File without changes
File without changes