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.
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/PKG-INFO +1 -1
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/pyproject.toml +1 -1
- python_cqrs-4.0.0/src/cqrs/adapters/amqp.py +83 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/adapters/kafka.py +7 -11
- python_cqrs-4.0.0/src/cqrs/adapters/protocol.py +28 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/message_brokers/amqp.py +3 -3
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/message_brokers/kafka.py +2 -2
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/producer.py +1 -1
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/python_cqrs.egg-info/PKG-INFO +1 -1
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/python_cqrs.egg-info/SOURCES.txt +1 -0
- python_cqrs-3.0.1/src/cqrs/adapters/amqp.py +0 -63
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/LICENSE +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/README.md +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/setup.cfg +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/adapters/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/compressors/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/compressors/protocol.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/compressors/zlib.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/container/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/container/di.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/container/protocol.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/decoders/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/decoders/kafka/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/decoders/kafka/empty_message.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/deserializers/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/deserializers/json.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/deserializers/protobuf.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/dispatcher/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/dispatcher/dispatcher.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/events/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/events/bootstrap.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/events/event.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/events/event_emitter.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/events/event_handler.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/events/map.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/mediator.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/message_brokers/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/message_brokers/devnull.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/message_brokers/protocol.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/middlewares/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/middlewares/base.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/middlewares/logging.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/outbox/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/outbox/map.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/outbox/mock.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/outbox/repository.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/outbox/sqlalchemy.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/requests/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/requests/bootstrap.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/requests/map.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/requests/request.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/requests/request_handler.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/response.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/serializers/__init__.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/serializers/default.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/cqrs/serializers/protobuf.py +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/python_cqrs.egg-info/dependency_links.txt +0 -0
- {python_cqrs-3.0.1 → python_cqrs-4.0.0}/src/python_cqrs.egg-info/requires.txt +0 -0
- {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
|
+
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>
|
|
@@ -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
|
|
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
|
|
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,
|
|
12
|
-
self.publisher =
|
|
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
|
|
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:
|
|
11
|
+
producer: adapters_protocol.KafkaProducer,
|
|
12
12
|
aiokafka_log_level: typing.Text = "ERROR",
|
|
13
13
|
):
|
|
14
14
|
self._producer = producer
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-cqrs
|
|
3
|
-
Version:
|
|
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>
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|