amqp-client-python 0.2.1.dev1__py3-none-any.whl → 0.2.2.dev1__py3-none-any.whl
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.
- amqp_client_python/domain/models/options.py +18 -0
- amqp_client_python/rabbitmq/async_channel.py +110 -39
- amqp_client_python/rabbitmq/async_connection.py +86 -73
- amqp_client_python/rabbitmq/async_eventbus_rabbitmq.py +31 -1
- amqp_client_python/rabbitmq/eventbus_wrapper_rabbitmq.py +29 -3
- {amqp_client_python-0.2.1.dev1.dist-info → amqp_client_python-0.2.2.dev1.dist-info}/METADATA +15 -20
- {amqp_client_python-0.2.1.dev1.dist-info → amqp_client_python-0.2.2.dev1.dist-info}/RECORD +9 -9
- {amqp_client_python-0.2.1.dev1.dist-info → amqp_client_python-0.2.2.dev1.dist-info}/WHEEL +1 -1
- {amqp_client_python-0.2.1.dev1.dist-info → amqp_client_python-0.2.2.dev1.dist-info}/licenses/LICENSE +0 -0
|
@@ -15,6 +15,12 @@ class Options:
|
|
|
15
15
|
vhost: str = "/",
|
|
16
16
|
heartbeat: Optional[int] = 60,
|
|
17
17
|
publisher_confirms: bool = False,
|
|
18
|
+
rpc_exchange_type: str = "topic",
|
|
19
|
+
rpc_queue_durable: bool = True,
|
|
20
|
+
rpc_queue_auto_delete: bool = False,
|
|
21
|
+
exchange_type: str = "topic",
|
|
22
|
+
queue_durable: bool = True,
|
|
23
|
+
queue_auto_delete: bool = False,
|
|
18
24
|
**kwargs: Dict[str, Any]
|
|
19
25
|
) -> None:
|
|
20
26
|
"""
|
|
@@ -32,6 +38,12 @@ class Options:
|
|
|
32
38
|
vhost: vhost information
|
|
33
39
|
heartbeat: interval between heartbeat
|
|
34
40
|
publisher_confirms: wait for publisher confirmations
|
|
41
|
+
rpc_exchange_type: exchange type for RPC server (default 'topic')
|
|
42
|
+
rpc_queue_durable: durability for RPC server queue (default True)
|
|
43
|
+
rpc_queue_auto_delete: auto_delete for RPC server queue (default False)
|
|
44
|
+
exchange_type: exchange type for pub/sub subscriptions (default 'topic')
|
|
45
|
+
queue_durable: durability for pub/sub subscriptions queue (default True)
|
|
46
|
+
queue_auto_delete: auto_delete for pub/sub subscriptions queue (default False)
|
|
35
47
|
**kwargs: pika options
|
|
36
48
|
|
|
37
49
|
Examples:
|
|
@@ -56,4 +68,10 @@ class Options:
|
|
|
56
68
|
self.vhost = vhost
|
|
57
69
|
self.heartbeat = heartbeat
|
|
58
70
|
self.publisher_confirms = publisher_confirms
|
|
71
|
+
self.rpc_exchange_type = kwargs.pop("rpc_exchange_type", rpc_exchange_type)
|
|
72
|
+
self.rpc_queue_durable = kwargs.pop("rpc_queue_durable", rpc_queue_durable)
|
|
73
|
+
self.rpc_queue_auto_delete = kwargs.pop("rpc_queue_auto_delete", rpc_queue_auto_delete)
|
|
74
|
+
self.exchange_type = kwargs.pop("exchange_type", exchange_type)
|
|
75
|
+
self.queue_durable = kwargs.pop("queue_durable", queue_durable)
|
|
76
|
+
self.queue_auto_delete = kwargs.pop("queue_auto_delete", queue_auto_delete)
|
|
59
77
|
self.kwargs = kwargs
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from collections import deque
|
|
2
|
-
from typing import MutableMapping, Mapping, Optional, Union, Dict, List
|
|
2
|
+
from typing import MutableMapping, Mapping, Optional, Union, Dict, List, Tuple, Any
|
|
3
3
|
from .async_channel_factory import AsyncChannelFactoryRabbitMQ
|
|
4
4
|
from amqp_client_python.exceptions import (
|
|
5
5
|
NackException,
|
|
@@ -54,11 +54,20 @@ class AsyncChannel:
|
|
|
54
54
|
self.auto_ack = auto_ack
|
|
55
55
|
self.subscribes: Dict[str, Dict[str, HandlerType]] = {}
|
|
56
56
|
self.re_subscribes: List[str] = []
|
|
57
|
+
self._compiled_patterns: Dict[str, List[Tuple[str, Any]]] = {}
|
|
58
|
+
self._queue_exchange_type: Dict[str, str] = {}
|
|
57
59
|
self.consumers: Dict[str, bool] = {}
|
|
58
60
|
self.publisher_confirms = False
|
|
59
61
|
self._message_number = 0
|
|
60
62
|
self._deliveries: MutableMapping[int, Future] = {}
|
|
61
63
|
self.response_timeout = 60
|
|
64
|
+
self._declared_exchanges: Dict[str, Tuple[str, bool]] = {}
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def _serialize_payload(body, content_type: str = "application/json"):
|
|
68
|
+
if isinstance(body, (bytes, bytearray, memoryview)):
|
|
69
|
+
return body
|
|
70
|
+
return dumps(body)
|
|
62
71
|
|
|
63
72
|
@property
|
|
64
73
|
def is_open(self) -> Optional[bool]:
|
|
@@ -81,6 +90,7 @@ class AsyncChannel:
|
|
|
81
90
|
"""
|
|
82
91
|
LOGGER.info("Channel opened")
|
|
83
92
|
self._channel = channel
|
|
93
|
+
self._declared_exchanges.clear()
|
|
84
94
|
self.add_on_channel_close_callback()
|
|
85
95
|
self.publisher_confirms and self.add_publish_confirms()
|
|
86
96
|
self._prefetch_count and self.set_qos(self._prefetch_count)
|
|
@@ -114,12 +124,14 @@ class AsyncChannel:
|
|
|
114
124
|
:param pika.channel.Channel: The closed channel
|
|
115
125
|
:param Exception reason: why the channel was closed
|
|
116
126
|
"""
|
|
117
|
-
LOGGER.
|
|
127
|
+
LOGGER.warning(f"Channel {channel} was closed by RabbitMQ: {reason}")
|
|
118
128
|
self._consuming = False
|
|
129
|
+
if hasattr(self, "_declared_exchanges"):
|
|
130
|
+
self._declared_exchanges.clear()
|
|
119
131
|
if self._connection.is_closing or self._connection.is_closed:
|
|
120
132
|
LOGGER.info("Connection is closing or already closed")
|
|
121
133
|
else:
|
|
122
|
-
LOGGER.info("Closing connection")
|
|
134
|
+
LOGGER.info(f"Closing connection due to channel closure: {reason}")
|
|
123
135
|
self._connection.close()
|
|
124
136
|
|
|
125
137
|
def add_publish_confirms(self):
|
|
@@ -154,14 +166,36 @@ class AsyncChannel:
|
|
|
154
166
|
be invoked by pika.
|
|
155
167
|
:param str|unicode exchange_name: The name of the exchange to declare
|
|
156
168
|
"""
|
|
157
|
-
|
|
169
|
+
if not exchange_name:
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
exchange_type_str = str(getattr(exchange_type, "value", exchange_type))
|
|
173
|
+
durable_bool = bool(durable)
|
|
174
|
+
|
|
175
|
+
if not hasattr(self, "_declared_exchanges"):
|
|
176
|
+
self._declared_exchanges = {}
|
|
177
|
+
|
|
178
|
+
if exchange_name in self._declared_exchanges:
|
|
179
|
+
prev_type, prev_durable = self._declared_exchanges[exchange_name]
|
|
180
|
+
if (prev_type, prev_durable) == (exchange_type_str, durable_bool):
|
|
181
|
+
return
|
|
182
|
+
LOGGER.warning(
|
|
183
|
+
f"Exchange '{exchange_name}' was already declared as type='{prev_type}', durable={prev_durable}. "
|
|
184
|
+
f"Re-declaring with type='{exchange_type_str}', durable={durable_bool} on the same channel may "
|
|
185
|
+
f"trigger PRECONDITION_FAILED."
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
self._declared_exchanges[exchange_name] = (exchange_type_str, durable_bool)
|
|
189
|
+
LOGGER.info(
|
|
190
|
+
f"Declaring exchange: {exchange_name} (type={exchange_type_str}, durable={durable_bool})"
|
|
191
|
+
)
|
|
158
192
|
# Note: using functools.partial is not required, it is demonstrating
|
|
159
193
|
# how arbitrary data can be passed to the callback when it is called
|
|
160
194
|
cb = partial(self.on_exchange_declareok, userdata=exchange_name)
|
|
161
195
|
self._channel.exchange_declare(
|
|
162
196
|
exchange=exchange_name,
|
|
163
|
-
exchange_type=
|
|
164
|
-
durable=
|
|
197
|
+
exchange_type=exchange_type_str,
|
|
198
|
+
durable=durable_bool,
|
|
165
199
|
callback=cb,
|
|
166
200
|
)
|
|
167
201
|
|
|
@@ -361,7 +395,7 @@ class AsyncChannel:
|
|
|
361
395
|
self._channel.basic_publish(
|
|
362
396
|
exchange_name,
|
|
363
397
|
routing_key,
|
|
364
|
-
|
|
398
|
+
self._serialize_payload(body, content_type),
|
|
365
399
|
properties=BasicProperties(
|
|
366
400
|
reply_to=self._callback_queue, # Response queue address
|
|
367
401
|
correlation_id=corr_id, # Request/response matching ID
|
|
@@ -436,7 +470,7 @@ class AsyncChannel:
|
|
|
436
470
|
self._channel.basic_publish(
|
|
437
471
|
exchange_name,
|
|
438
472
|
routing_key,
|
|
439
|
-
|
|
473
|
+
self._serialize_payload(body, content_type),
|
|
440
474
|
properties=BasicProperties(
|
|
441
475
|
reply_to=self._callback_queue,
|
|
442
476
|
content_type=content_type,
|
|
@@ -491,13 +525,14 @@ class AsyncChannel:
|
|
|
491
525
|
exchange_type="topic",
|
|
492
526
|
durable=True,
|
|
493
527
|
auto_delete=False,
|
|
528
|
+
auto_decode=True,
|
|
494
529
|
):
|
|
495
530
|
await self.start_rpc_publisher()
|
|
496
531
|
self.register_queue(
|
|
497
532
|
exchange_name, queue_name, exchange_type, durable, auto_delete
|
|
498
533
|
)
|
|
499
534
|
self.register_handler(
|
|
500
|
-
queue_name, routing_key, callback, content_type, timeout
|
|
535
|
+
queue_name, routing_key, callback, content_type, timeout, auto_decode=auto_decode
|
|
501
536
|
)
|
|
502
537
|
|
|
503
538
|
self.queue_bind(queue_name, exchange_name, routing_key)
|
|
@@ -518,30 +553,52 @@ class AsyncChannel:
|
|
|
518
553
|
self,
|
|
519
554
|
exchange_name,
|
|
520
555
|
queue_name: str,
|
|
521
|
-
exchange_type="
|
|
556
|
+
exchange_type="topic",
|
|
522
557
|
durable=True,
|
|
523
558
|
auto_delete=False,
|
|
524
559
|
):
|
|
525
560
|
if queue_name not in self.subscribes:
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
self
|
|
561
|
+
self.subscribes[queue_name] = {}
|
|
562
|
+
exchange_type_str = str(getattr(exchange_type, "value", exchange_type))
|
|
563
|
+
if not hasattr(self, "_queue_exchange_type"):
|
|
564
|
+
self._queue_exchange_type = {}
|
|
565
|
+
self._queue_exchange_type[queue_name] = exchange_type_str
|
|
566
|
+
|
|
567
|
+
self.setup_exchange(exchange_name, exchange_type, durable=durable)
|
|
529
568
|
self.queue_declare(queue_name, durable=durable, auto_delete=auto_delete)
|
|
530
569
|
|
|
531
570
|
if queue_name not in self.consumers:
|
|
532
571
|
self.consumers[queue_name] = False
|
|
533
572
|
|
|
573
|
+
def _compile_routing_pattern(self, queue_name: str, routing_key: str):
|
|
574
|
+
if not hasattr(self, "_compiled_patterns"):
|
|
575
|
+
self._compiled_patterns = {}
|
|
576
|
+
if queue_name not in self._compiled_patterns:
|
|
577
|
+
self._compiled_patterns[queue_name] = []
|
|
578
|
+
|
|
579
|
+
if "*" in routing_key or "#" in routing_key:
|
|
580
|
+
# AMQP topic wildcard pattern (* = single word [^.]+, # = zero or more words .*)
|
|
581
|
+
amqp_regex = "^" + re.escape(routing_key).replace(r"\*", r"[^.]+").replace(r"\#", r".*") + "$"
|
|
582
|
+
try:
|
|
583
|
+
self._compiled_patterns[queue_name].append((routing_key, re.compile(amqp_regex)))
|
|
584
|
+
except re.error:
|
|
585
|
+
pass
|
|
586
|
+
|
|
534
587
|
def register_handler(
|
|
535
|
-
self, queue_name, routing_key, callback, content_type, response_timeout
|
|
588
|
+
self, queue_name, routing_key, callback, content_type, response_timeout, auto_decode=True
|
|
536
589
|
):
|
|
590
|
+
if queue_name not in self.subscribes:
|
|
591
|
+
self.subscribes[queue_name] = {}
|
|
537
592
|
if routing_key not in self.subscribes[queue_name]:
|
|
538
593
|
self.subscribes[queue_name][routing_key] = {
|
|
539
594
|
"handle": callback,
|
|
540
595
|
"content_type": content_type,
|
|
541
596
|
"response_timeout": response_timeout or self.response_timeout,
|
|
597
|
+
"auto_decode": auto_decode,
|
|
542
598
|
}
|
|
543
599
|
if "*" in routing_key and routing_key not in self.re_subscribes:
|
|
544
600
|
self.re_subscribes.append(routing_key)
|
|
601
|
+
self._compile_routing_pattern(queue_name, routing_key)
|
|
545
602
|
|
|
546
603
|
async def subscribe(
|
|
547
604
|
self,
|
|
@@ -554,12 +611,13 @@ class AsyncChannel:
|
|
|
554
611
|
exchange_type: str = "topic",
|
|
555
612
|
durable: bool = True,
|
|
556
613
|
auto_delete: bool = False,
|
|
614
|
+
auto_decode: bool = True,
|
|
557
615
|
) -> None:
|
|
558
616
|
self.register_queue(
|
|
559
617
|
exchange_name, queue_name, exchange_type, durable, auto_delete
|
|
560
618
|
)
|
|
561
619
|
self.register_handler(
|
|
562
|
-
queue_name, routing_key, callback, content_type, timeout
|
|
620
|
+
queue_name, routing_key, callback, content_type, timeout, auto_decode=auto_decode
|
|
563
621
|
)
|
|
564
622
|
|
|
565
623
|
self.queue_bind(queue_name, exchange_name, routing_key)
|
|
@@ -587,7 +645,11 @@ class AsyncChannel:
|
|
|
587
645
|
|
|
588
646
|
async def process_message(handler: HandlerType):
|
|
589
647
|
nonlocal body
|
|
590
|
-
|
|
648
|
+
if handler.get("auto_decode", True) and handler.get("content_type") == "application/json":
|
|
649
|
+
try:
|
|
650
|
+
body = loads(body)
|
|
651
|
+
except Exception:
|
|
652
|
+
pass
|
|
591
653
|
response = await wait_for(
|
|
592
654
|
handler["handle"](body),
|
|
593
655
|
timeout=handler["response_timeout"],
|
|
@@ -609,30 +671,39 @@ class AsyncChannel:
|
|
|
609
671
|
|
|
610
672
|
async def handle_message(queue_name, basic_deliver, props):
|
|
611
673
|
try:
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
)
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
674
|
+
handlers = self.subscribes.get(queue_name, {})
|
|
675
|
+
handler = handlers.get(basic_deliver.routing_key)
|
|
676
|
+
if handler:
|
|
677
|
+
return await process_message(handler)
|
|
678
|
+
|
|
679
|
+
queue_type = getattr(self, "_queue_exchange_type", {}).get(queue_name, "topic")
|
|
680
|
+
if queue_type != "direct":
|
|
681
|
+
patterns = getattr(self, "_compiled_patterns", {}).get(queue_name)
|
|
682
|
+
if patterns:
|
|
683
|
+
for pattern_key, pattern in patterns:
|
|
684
|
+
if pattern.match(basic_deliver.routing_key):
|
|
685
|
+
pattern_handler = handlers.get(pattern_key)
|
|
686
|
+
if pattern_handler:
|
|
687
|
+
return await process_message(pattern_handler)
|
|
688
|
+
elif self.re_subscribes:
|
|
689
|
+
for routing_key in self.re_subscribes:
|
|
690
|
+
if routing_key in handlers and re.match(routing_key, basic_deliver.routing_key):
|
|
691
|
+
return await process_message(handlers[routing_key])
|
|
692
|
+
|
|
693
|
+
if self.rpc_publisher_started and props.reply_to:
|
|
694
|
+
self._channel_rpc.basic_publish(
|
|
695
|
+
"",
|
|
696
|
+
props.reply_to,
|
|
697
|
+
"Provider Error: routing_key not binded",
|
|
698
|
+
properties=BasicProperties(
|
|
699
|
+
correlation_id=props.correlation_id,
|
|
700
|
+
content_type="text/plain",
|
|
701
|
+
type="error",
|
|
702
|
+
),
|
|
635
703
|
)
|
|
704
|
+
return not self.auto_ack and self._channel.is_open and self._channel.basic_nack(
|
|
705
|
+
basic_deliver.delivery_tag, requeue=False
|
|
706
|
+
)
|
|
636
707
|
|
|
637
708
|
except BaseException as err:
|
|
638
709
|
if self.rpc_publisher_started and props.reply_to:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from collections import deque
|
|
2
|
-
from typing import Optional, Callable, Awaitable, Tuple, Dict, Any
|
|
2
|
+
from typing import Optional, Callable, Awaitable, Tuple, Dict, Any, Union
|
|
3
3
|
|
|
4
4
|
from .async_connection_factory import AsyncConnectionFactoryRabbitMQ, AsyncioConnection
|
|
5
5
|
from .async_channel import AsyncChannel
|
|
@@ -64,7 +64,7 @@ class AsyncConnection:
|
|
|
64
64
|
self.reconnect_delay = 1
|
|
65
65
|
self.callbacks: deque[Tuple[Callable, Future]] = deque()
|
|
66
66
|
self.type = connection_type
|
|
67
|
-
self.backup: Dict[str, Dict[str, Any]] = {
|
|
67
|
+
self.backup: Dict[str, Dict[Union[str, Tuple[str, str]], Any]] = {
|
|
68
68
|
"exchange": {},
|
|
69
69
|
"queue": {},
|
|
70
70
|
"subscribe": {},
|
|
@@ -134,28 +134,8 @@ class AsyncConnection:
|
|
|
134
134
|
self._channel.publisher_confirms = self.publisher_confirms
|
|
135
135
|
|
|
136
136
|
if self.reconnecting or self.backup["subscribe"] or self.backup["rpc_subscribe"]:
|
|
137
|
-
async def recorvery():
|
|
138
|
-
for routing_key in list(self.backup["subscribe"].keys()):
|
|
139
|
-
params = self.backup["subscribe"][routing_key]
|
|
140
|
-
await self.subscribe(
|
|
141
|
-
params["queue_name"],
|
|
142
|
-
params["exchange_name"],
|
|
143
|
-
routing_key,
|
|
144
|
-
params["callback"],
|
|
145
|
-
params["timeout"],
|
|
146
|
-
)
|
|
147
|
-
for routing_key in list(self.backup["rpc_subscribe"].keys()):
|
|
148
|
-
params = self.backup["rpc_subscribe"][routing_key]
|
|
149
|
-
await self.rpc_subscribe(
|
|
150
|
-
params["queue_name"],
|
|
151
|
-
params["exchange_name"],
|
|
152
|
-
routing_key,
|
|
153
|
-
params["callback"],
|
|
154
|
-
params["timeout"],
|
|
155
|
-
)
|
|
156
|
-
|
|
157
137
|
future: Future = Future(loop=self.ioloop)
|
|
158
|
-
self.callbacks.append((
|
|
138
|
+
self.callbacks.append((self._recover_subscriptions, future))
|
|
159
139
|
self.reconnect_delay = 1
|
|
160
140
|
self.reconnecting = False
|
|
161
141
|
|
|
@@ -207,12 +187,39 @@ class AsyncConnection:
|
|
|
207
187
|
self.reconnecting = True
|
|
208
188
|
self.retry_connection()
|
|
209
189
|
|
|
190
|
+
async def _recover_subscriptions(self):
|
|
191
|
+
for key, params in list(self.backup["subscribe"].items()):
|
|
192
|
+
routing_key = params.get("routing_key") or (key[1] if isinstance(key, tuple) else key)
|
|
193
|
+
await self.subscribe(
|
|
194
|
+
params["queue_name"],
|
|
195
|
+
params["exchange_name"],
|
|
196
|
+
routing_key,
|
|
197
|
+
params["callback"],
|
|
198
|
+
params["timeout"],
|
|
199
|
+
content_type=params.get("content_type", "application/json"),
|
|
200
|
+
auto_decode=params.get("auto_decode", True),
|
|
201
|
+
exchange_type=params.get("exchange_type", "topic"),
|
|
202
|
+
durable=params.get("durable", True),
|
|
203
|
+
auto_delete=params.get("auto_delete", False),
|
|
204
|
+
)
|
|
205
|
+
for key, params in list(self.backup["rpc_subscribe"].items()):
|
|
206
|
+
routing_key = params.get("routing_key") or (key[1] if isinstance(key, tuple) else key)
|
|
207
|
+
await self.rpc_subscribe(
|
|
208
|
+
params["queue_name"],
|
|
209
|
+
params["exchange_name"],
|
|
210
|
+
routing_key,
|
|
211
|
+
params["callback"],
|
|
212
|
+
params["timeout"],
|
|
213
|
+
content_type=params.get("content_type", "application/json"),
|
|
214
|
+
auto_decode=params.get("auto_decode", True),
|
|
215
|
+
exchange_type=params.get("exchange_type", "topic"),
|
|
216
|
+
durable=params.get("durable", True),
|
|
217
|
+
auto_delete=params.get("auto_delete", False),
|
|
218
|
+
)
|
|
219
|
+
|
|
210
220
|
def retry_connection(self):
|
|
211
221
|
"""
|
|
212
222
|
Attempts to reconnect to RabbitMQ with exponential backoff.
|
|
213
|
-
|
|
214
|
-
If reconnection is successful, this method will recover previous subscriptions
|
|
215
|
-
and RPC handlers.
|
|
216
223
|
"""
|
|
217
224
|
if not self.is_open:
|
|
218
225
|
if self.reconnect_delay > 30:
|
|
@@ -224,29 +231,9 @@ class AsyncConnection:
|
|
|
224
231
|
self.reconnect_delay += 1
|
|
225
232
|
else:
|
|
226
233
|
if self.reconnecting:
|
|
227
|
-
async def recorvery():
|
|
228
|
-
for routing_key in list(self.backup["subscribe"].keys()):
|
|
229
|
-
params = self.backup["subscribe"][routing_key]
|
|
230
|
-
await self.subscribe(
|
|
231
|
-
params["queue_name"],
|
|
232
|
-
params["exchange_name"],
|
|
233
|
-
routing_key,
|
|
234
|
-
params["callback"],
|
|
235
|
-
params["timeout"],
|
|
236
|
-
)
|
|
237
|
-
for routing_key in list(self.backup["rpc_subscribe"].keys()):
|
|
238
|
-
params = self.backup["rpc_subscribe"][routing_key]
|
|
239
|
-
await self.rpc_subscribe(
|
|
240
|
-
params["queue_name"],
|
|
241
|
-
params["exchange_name"],
|
|
242
|
-
routing_key,
|
|
243
|
-
params["callback"],
|
|
244
|
-
params["timeout"],
|
|
245
|
-
)
|
|
246
|
-
|
|
247
234
|
self.reconnect_delay = 1
|
|
248
235
|
self.reconnecting = False
|
|
249
|
-
self.ioloop.create_task(self.add_callback(
|
|
236
|
+
self.ioloop.create_task(self.add_callback(self._recover_subscriptions)) # type: ignore
|
|
250
237
|
|
|
251
238
|
@property
|
|
252
239
|
def is_open(self) -> Optional[bool]:
|
|
@@ -386,7 +373,17 @@ class AsyncConnection:
|
|
|
386
373
|
)
|
|
387
374
|
|
|
388
375
|
async def rpc_subscribe(
|
|
389
|
-
self,
|
|
376
|
+
self,
|
|
377
|
+
queue_name,
|
|
378
|
+
exchange_name,
|
|
379
|
+
routing_key,
|
|
380
|
+
callback,
|
|
381
|
+
timeout,
|
|
382
|
+
content_type="application/json",
|
|
383
|
+
auto_decode=True,
|
|
384
|
+
exchange_type="topic",
|
|
385
|
+
durable=True,
|
|
386
|
+
auto_delete=False,
|
|
390
387
|
):
|
|
391
388
|
"""
|
|
392
389
|
Registers an RPC handler for a specific routing key.
|
|
@@ -397,24 +394,24 @@ class AsyncConnection:
|
|
|
397
394
|
routing_key: The routing key to subscribe to
|
|
398
395
|
callback: The function to call when a message is received
|
|
399
396
|
timeout: Timeout in seconds for processing the received message
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
>>> await connection.rpc_subscribe(
|
|
406
|
-
"rpc_queue",
|
|
407
|
-
"rpc_exchange",
|
|
408
|
-
"user.find",
|
|
409
|
-
handle_rpc,
|
|
410
|
-
10.0
|
|
411
|
-
)
|
|
397
|
+
content_type: Content type of expected message
|
|
398
|
+
auto_decode: Whether to auto-decode JSON payload
|
|
399
|
+
exchange_type: Exchange type for RPC server (default 'topic')
|
|
400
|
+
durable: Durability of queue (default True)
|
|
401
|
+
auto_delete: Auto delete flag for queue (default False)
|
|
412
402
|
"""
|
|
413
|
-
|
|
403
|
+
sub_key = (queue_name, routing_key)
|
|
404
|
+
self.backup["rpc_subscribe"][sub_key] = {
|
|
414
405
|
"queue_name": queue_name,
|
|
415
406
|
"exchange_name": exchange_name,
|
|
407
|
+
"routing_key": routing_key,
|
|
416
408
|
"callback": callback,
|
|
417
409
|
"timeout": timeout,
|
|
410
|
+
"content_type": content_type,
|
|
411
|
+
"auto_decode": auto_decode,
|
|
412
|
+
"exchange_type": exchange_type,
|
|
413
|
+
"durable": durable,
|
|
414
|
+
"auto_delete": auto_delete,
|
|
418
415
|
}
|
|
419
416
|
await self._channel.rpc_subscribe(
|
|
420
417
|
queue_name=queue_name,
|
|
@@ -422,6 +419,11 @@ class AsyncConnection:
|
|
|
422
419
|
routing_key=routing_key,
|
|
423
420
|
callback=callback,
|
|
424
421
|
timeout=timeout,
|
|
422
|
+
content_type=content_type,
|
|
423
|
+
auto_decode=auto_decode,
|
|
424
|
+
exchange_type=exchange_type,
|
|
425
|
+
durable=durable,
|
|
426
|
+
auto_delete=auto_delete,
|
|
425
427
|
)
|
|
426
428
|
|
|
427
429
|
async def subscribe(
|
|
@@ -431,6 +433,11 @@ class AsyncConnection:
|
|
|
431
433
|
routing_key: str,
|
|
432
434
|
callback: Callable[[Any], Awaitable[None]],
|
|
433
435
|
timeout: Optional[float],
|
|
436
|
+
content_type: str = "application/json",
|
|
437
|
+
auto_decode: bool = True,
|
|
438
|
+
exchange_type: str = "topic",
|
|
439
|
+
durable: bool = True,
|
|
440
|
+
auto_delete: bool = False,
|
|
434
441
|
):
|
|
435
442
|
"""
|
|
436
443
|
Subscribes to messages with a specific routing key.
|
|
@@ -441,23 +448,24 @@ class AsyncConnection:
|
|
|
441
448
|
routing_key: The routing key to subscribe to
|
|
442
449
|
callback: The function to call when a message is received
|
|
443
450
|
timeout: Timeout in seconds for processing the received message
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
"notifications_queue",
|
|
450
|
-
"notifications",
|
|
451
|
-
"email.send",
|
|
452
|
-
handle_message,
|
|
453
|
-
5.0
|
|
454
|
-
)
|
|
451
|
+
content_type: Content type of expected message
|
|
452
|
+
auto_decode: Whether to auto-decode JSON payload
|
|
453
|
+
exchange_type: Exchange type for subscription (default 'topic')
|
|
454
|
+
durable: Durability of queue (default True)
|
|
455
|
+
auto_delete: Auto delete flag for queue (default False)
|
|
455
456
|
"""
|
|
456
|
-
|
|
457
|
+
sub_key = (queue_name, routing_key)
|
|
458
|
+
self.backup["subscribe"][sub_key] = {
|
|
457
459
|
"queue_name": queue_name,
|
|
458
460
|
"exchange_name": exchange_name,
|
|
461
|
+
"routing_key": routing_key,
|
|
459
462
|
"callback": callback,
|
|
460
463
|
"timeout": timeout,
|
|
464
|
+
"content_type": content_type,
|
|
465
|
+
"auto_decode": auto_decode,
|
|
466
|
+
"exchange_type": exchange_type,
|
|
467
|
+
"durable": durable,
|
|
468
|
+
"auto_delete": auto_delete,
|
|
461
469
|
}
|
|
462
470
|
await self._channel.subscribe(
|
|
463
471
|
exchange_name=exchange_name,
|
|
@@ -465,6 +473,11 @@ class AsyncConnection:
|
|
|
465
473
|
routing_key=routing_key,
|
|
466
474
|
callback=callback,
|
|
467
475
|
timeout=timeout,
|
|
476
|
+
content_type=content_type,
|
|
477
|
+
auto_decode=auto_decode,
|
|
478
|
+
exchange_type=exchange_type,
|
|
479
|
+
durable=durable,
|
|
480
|
+
auto_delete=auto_delete,
|
|
468
481
|
)
|
|
469
482
|
|
|
470
483
|
async def add_callback(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Callable, Awaitable, Optional, Union, Any, List
|
|
2
2
|
from .async_connection import AsyncConnection
|
|
3
3
|
from ..domain.utils import ConnectionType
|
|
4
|
-
from amqp_client_python.domain.models import Config
|
|
4
|
+
from amqp_client_python.domain.models import Config, Options
|
|
5
5
|
from asyncio import AbstractEventLoop, get_event_loop
|
|
6
6
|
from pika import DeliveryMode
|
|
7
7
|
from amqp_client_python.signals import Signal
|
|
@@ -212,6 +212,8 @@ class AsyncEventbusRabbitMQ:
|
|
|
212
212
|
handler: Callable[[Any], Awaitable[Union[bytes, str]]],
|
|
213
213
|
timeout: Optional[int] = None,
|
|
214
214
|
connection_timeout: int = 16,
|
|
215
|
+
content_type: str = "application/json",
|
|
216
|
+
auto_decode: bool = True,
|
|
215
217
|
) -> None:
|
|
216
218
|
"""
|
|
217
219
|
Register a provider to listen on RPC request queue
|
|
@@ -221,6 +223,8 @@ class AsyncEventbusRabbitMQ:
|
|
|
221
223
|
handler: message handler, it will be called when a message is received
|
|
222
224
|
timeout: timeout in seconds for waiting for process the received message
|
|
223
225
|
connection_timeout: timeout for waiting for connection restabilishment
|
|
226
|
+
content_type: Content type of expected message
|
|
227
|
+
auto_decode: Whether to auto-decode JSON payload (default True)
|
|
224
228
|
|
|
225
229
|
Raises:
|
|
226
230
|
AutoReconnectException: when cannout reconnect on the gived timeout
|
|
@@ -233,12 +237,23 @@ class AsyncEventbusRabbitMQ:
|
|
|
233
237
|
"""
|
|
234
238
|
|
|
235
239
|
async def add_resource():
|
|
240
|
+
extra_kwargs = {}
|
|
241
|
+
if content_type != "application/json":
|
|
242
|
+
extra_kwargs["content_type"] = content_type
|
|
243
|
+
if not auto_decode:
|
|
244
|
+
extra_kwargs["auto_decode"] = auto_decode
|
|
245
|
+
options = getattr(self.config, "options", None)
|
|
246
|
+
if isinstance(options, Options):
|
|
247
|
+
extra_kwargs["exchange_type"] = options.rpc_exchange_type
|
|
248
|
+
extra_kwargs["durable"] = options.rpc_queue_durable
|
|
249
|
+
extra_kwargs["auto_delete"] = options.rpc_queue_auto_delete
|
|
236
250
|
await self._rpc_server_connection.rpc_subscribe(
|
|
237
251
|
self.config.options.rpc_queue_name,
|
|
238
252
|
self.config.options.rpc_exchange_name,
|
|
239
253
|
name,
|
|
240
254
|
handler,
|
|
241
255
|
timeout,
|
|
256
|
+
**extra_kwargs,
|
|
242
257
|
)
|
|
243
258
|
|
|
244
259
|
if self._rpc_server_connection.open(self.config.url):
|
|
@@ -252,6 +267,8 @@ class AsyncEventbusRabbitMQ:
|
|
|
252
267
|
handler: Callable[[Any], Awaitable[None]],
|
|
253
268
|
timeout: Optional[float] = None,
|
|
254
269
|
connection_timeout: int = 16,
|
|
270
|
+
content_type: str = "application/json",
|
|
271
|
+
auto_decode: bool = True,
|
|
255
272
|
) -> None:
|
|
256
273
|
"""
|
|
257
274
|
Register a provider to listen on queue of bus
|
|
@@ -262,6 +279,8 @@ class AsyncEventbusRabbitMQ:
|
|
|
262
279
|
handler: message handler, it will be called when a message is received
|
|
263
280
|
timeout: timeout in seconds for waiting for process the received message
|
|
264
281
|
connection_timeout: timeout for waiting for connection restabilishment
|
|
282
|
+
content_type: Content type of expected message
|
|
283
|
+
auto_decode: Whether to auto-decode JSON payload (default True)
|
|
265
284
|
|
|
266
285
|
Raises:
|
|
267
286
|
AutoReconnectException: when cannout reconnect on the gived timeout
|
|
@@ -277,12 +296,23 @@ class AsyncEventbusRabbitMQ:
|
|
|
277
296
|
"""
|
|
278
297
|
|
|
279
298
|
async def add_subscribe():
|
|
299
|
+
extra_kwargs = {}
|
|
300
|
+
if content_type != "application/json":
|
|
301
|
+
extra_kwargs["content_type"] = content_type
|
|
302
|
+
if not auto_decode:
|
|
303
|
+
extra_kwargs["auto_decode"] = auto_decode
|
|
304
|
+
options = getattr(self.config, "options", None)
|
|
305
|
+
if isinstance(options, Options):
|
|
306
|
+
extra_kwargs["exchange_type"] = options.exchange_type
|
|
307
|
+
extra_kwargs["durable"] = options.queue_durable
|
|
308
|
+
extra_kwargs["auto_delete"] = options.queue_auto_delete
|
|
280
309
|
await self._sub_connection.subscribe(
|
|
281
310
|
self.config.options.queue_name,
|
|
282
311
|
exchange_name,
|
|
283
312
|
routing_key,
|
|
284
313
|
handler,
|
|
285
314
|
timeout,
|
|
315
|
+
**extra_kwargs,
|
|
286
316
|
)
|
|
287
317
|
|
|
288
318
|
if self._sub_connection.open(self.config.url):
|
|
@@ -142,6 +142,8 @@ class EventbusWrapperRabbitMQ:
|
|
|
142
142
|
handler: Callable[[Any], Awaitable[None]],
|
|
143
143
|
response_timeout: Optional[int] = None,
|
|
144
144
|
connection_timeout: int = 16,
|
|
145
|
+
content_type: str = "application/json",
|
|
146
|
+
auto_decode: bool = True,
|
|
145
147
|
) -> Future:
|
|
146
148
|
if self._thread.ident == current_thread().ident:
|
|
147
149
|
raise BlockingException(
|
|
@@ -154,6 +156,8 @@ class EventbusWrapperRabbitMQ:
|
|
|
154
156
|
handler,
|
|
155
157
|
response_timeout,
|
|
156
158
|
connection_timeout,
|
|
159
|
+
content_type=content_type,
|
|
160
|
+
auto_decode=auto_decode,
|
|
157
161
|
),
|
|
158
162
|
self._loop,
|
|
159
163
|
)
|
|
@@ -165,13 +169,21 @@ class EventbusWrapperRabbitMQ:
|
|
|
165
169
|
handler: Callable[[Any], Awaitable[None]],
|
|
166
170
|
response_timeout: Optional[int] = None,
|
|
167
171
|
connection_timeout: int = 16,
|
|
172
|
+
content_type: str = "application/json",
|
|
173
|
+
auto_decode: bool = True,
|
|
168
174
|
):
|
|
169
175
|
if self._thread.ident != current_thread().ident:
|
|
170
176
|
raise ThreadUnsafeException(
|
|
171
177
|
"Cannot run async call on this thread, try to use sync thread safe methods"
|
|
172
178
|
)
|
|
173
179
|
await self._async_eventbus.subscribe(
|
|
174
|
-
exchange_name,
|
|
180
|
+
exchange_name,
|
|
181
|
+
routing_key,
|
|
182
|
+
handler,
|
|
183
|
+
response_timeout,
|
|
184
|
+
connection_timeout,
|
|
185
|
+
content_type=content_type,
|
|
186
|
+
auto_decode=auto_decode,
|
|
175
187
|
)
|
|
176
188
|
|
|
177
189
|
def provide_resource(
|
|
@@ -180,6 +192,8 @@ class EventbusWrapperRabbitMQ:
|
|
|
180
192
|
callback: Callable[[List[Any]], Awaitable[Union[bytes, str]]],
|
|
181
193
|
response_timeout: Optional[int] = None,
|
|
182
194
|
connection_timeout: int = 16,
|
|
195
|
+
content_type: str = "application/json",
|
|
196
|
+
auto_decode: bool = True,
|
|
183
197
|
) -> Future:
|
|
184
198
|
if self._thread.ident == current_thread().ident:
|
|
185
199
|
raise BlockingException(
|
|
@@ -187,7 +201,12 @@ class EventbusWrapperRabbitMQ:
|
|
|
187
201
|
)
|
|
188
202
|
return run_coroutine_threadsafe(
|
|
189
203
|
self._async_eventbus.provide_resource(
|
|
190
|
-
name,
|
|
204
|
+
name,
|
|
205
|
+
callback,
|
|
206
|
+
response_timeout,
|
|
207
|
+
connection_timeout,
|
|
208
|
+
content_type=content_type,
|
|
209
|
+
auto_decode=auto_decode,
|
|
191
210
|
),
|
|
192
211
|
self._loop,
|
|
193
212
|
)
|
|
@@ -198,13 +217,20 @@ class EventbusWrapperRabbitMQ:
|
|
|
198
217
|
callback: Callable[[List[Any]], Awaitable[Union[bytes, str]]],
|
|
199
218
|
response_timeout: Optional[int] = None,
|
|
200
219
|
connection_timeout: int = 16,
|
|
220
|
+
content_type: str = "application/json",
|
|
221
|
+
auto_decode: bool = True,
|
|
201
222
|
):
|
|
202
223
|
if self._thread.ident != current_thread().ident:
|
|
203
224
|
raise ThreadUnsafeException(
|
|
204
225
|
"Cannot run async call on this thread, try to use sync thread safe methods"
|
|
205
226
|
)
|
|
206
227
|
await self._async_eventbus.provide_resource(
|
|
207
|
-
name,
|
|
228
|
+
name,
|
|
229
|
+
callback,
|
|
230
|
+
response_timeout,
|
|
231
|
+
connection_timeout,
|
|
232
|
+
content_type=content_type,
|
|
233
|
+
auto_decode=auto_decode,
|
|
208
234
|
)
|
|
209
235
|
|
|
210
236
|
def dispose(self):
|
{amqp_client_python-0.2.1.dev1.dist-info → amqp_client_python-0.2.2.dev1.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: amqp-client-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2.dev1
|
|
4
4
|
Summary: Python AMQP Client Library
|
|
5
5
|
Project-URL: Homepage, https://github.com/nutes-uepb/amqp-client-python
|
|
6
6
|
Project-URL: Repository, https://github.com/nutes-uepb/amqp-client-python
|
|
@@ -17,7 +17,6 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
17
17
|
Classifier: Natural Language :: English
|
|
18
18
|
Classifier: Operating System :: OS Independent
|
|
19
19
|
Classifier: Programming Language :: Python :: 3
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.10
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -28,7 +27,7 @@ Classifier: Topic :: Internet
|
|
|
28
27
|
Classifier: Topic :: Software Development :: Libraries
|
|
29
28
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
30
29
|
Classifier: Topic :: System :: Networking
|
|
31
|
-
Requires-Python: <4.0,>=3.
|
|
30
|
+
Requires-Python: <4.0,>=3.10
|
|
32
31
|
Requires-Dist: pika<2.0.0,>=1.3.0
|
|
33
32
|
Description-Content-Type: text/markdown
|
|
34
33
|
|
|
@@ -57,16 +56,6 @@ A Python client providing a high-level abstraction for interacting with RabbitMQ
|
|
|
57
56
|
|
|
58
57
|
**Discord Server**: <a href="https://discord.gg/RkXNeZpNZk" target="_blank">https://discord.gg/RkXNeZpNZk</a>
|
|
59
58
|
|
|
60
|
-
### 🦀 Looking for higher performance? Try `amqp-rs`!
|
|
61
|
-
If you need higher throughput, thread safety, and built-in compression, check out [**amqp-rs**](https://pypi.org/project/amqp-rs/).
|
|
62
|
-
|
|
63
|
-
It is a high-performance Python extension developed in Rust using PyO3 and `tokio`. It shares a similar API and design philosophy with this library, but adds native thread safety, `zstd`/`zlib`/`lz4` compression, robust TLS/SSL support, and graceful shutdowns.
|
|
64
|
-
|
|
65
|
-
You can install it via `pip` or `uv`:
|
|
66
|
-
```bash
|
|
67
|
-
pip install amqp-rs
|
|
68
|
-
```
|
|
69
|
-
|
|
70
59
|
---
|
|
71
60
|
### Features:
|
|
72
61
|
- Automatic creation and management of queues, exchanges and channels;
|
|
@@ -80,19 +69,22 @@ pip install amqp-rs
|
|
|
80
69
|
### Table of Compatibility
|
|
81
70
|
| version | compatible with |
|
|
82
71
|
| ---- | ---- |
|
|
72
|
+
| 0.2.1 | ~0.2.0 |
|
|
83
73
|
| 0.2.0 | 0.2.0 |
|
|
84
74
|
| 0.1.14 | ~0.1.12 |
|
|
85
75
|
|
|
86
76
|
### Installation
|
|
87
77
|
|
|
88
|
-
You can install `amqp-client-python` using pip:
|
|
78
|
+
You can install `amqp-client-python` using uv or pip:
|
|
89
79
|
```bash
|
|
80
|
+
uv add amqp-client-python
|
|
81
|
+
# or
|
|
90
82
|
pip install amqp-client-python
|
|
91
83
|
```
|
|
92
84
|
|
|
93
85
|
### Prerequisites
|
|
94
86
|
|
|
95
|
-
* Python 3.
|
|
87
|
+
* Python 3.9+
|
|
96
88
|
* A running RabbitMQ instance.
|
|
97
89
|
|
|
98
90
|
### Examples:
|
|
@@ -109,15 +101,18 @@ from amqp_client_python import (
|
|
|
109
101
|
)
|
|
110
102
|
config = Config(Options("queue", "rpc_queue", "rpc_exchange"))
|
|
111
103
|
eventbus = AsyncEventbusRabbitMQ(config)
|
|
112
|
-
# publish
|
|
104
|
+
# publish (supports dict/str with auto-JSON, or raw bytes/bytearray without overhead)
|
|
105
|
+
await eventbus.publish("rpc_exchange", "routing.key", {"message": "content"})
|
|
106
|
+
await eventbus.publish("rpc_exchange", "routing.key", b"raw_binary_payload")
|
|
113
107
|
|
|
114
|
-
|
|
115
|
-
# subscribe
|
|
108
|
+
# subscribe (auto_decode=True delivers parsed JSON dict; auto_decode=False delivers raw bytes)
|
|
116
109
|
async def subscribe_handler(body) -> None:
|
|
117
110
|
print(body, type(body), flush=True) # handle messages
|
|
118
111
|
await eventbus.subscribe("rpc_exchange", "routing.key", subscribe_handler)
|
|
112
|
+
|
|
119
113
|
# rpc_publish
|
|
120
|
-
response = await eventbus.rpc_client("rpc_exchange", "user.find", "
|
|
114
|
+
response = await eventbus.rpc_client("rpc_exchange", "user.find", {"name": "alice"})
|
|
115
|
+
|
|
121
116
|
# provider
|
|
122
117
|
async def rpc_provider_handler(body) -> bytes:
|
|
123
118
|
print(f"body: {body}")
|
|
@@ -3,7 +3,7 @@ amqp_client_python/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
3
3
|
amqp_client_python/signals.py,sha256=6mW4oWBQnbJwuKox6ceeDbxTM8v9aCDEb0o2zyhpb5g,1263
|
|
4
4
|
amqp_client_python/domain/models/__init__.py,sha256=EgEV-FeMUVMj_7RvhPbnZaqnJaNqTYnIAiEiqSy0ies,154
|
|
5
5
|
amqp_client_python/domain/models/config.py,sha256=g2AWrlEvHCS1h2zlny29phREyG9GmD0rtGQHS3C7LVU,2268
|
|
6
|
-
amqp_client_python/domain/models/options.py,sha256=
|
|
6
|
+
amqp_client_python/domain/models/options.py,sha256=uCApGDYfwKUVytoQ-g81mbHoEWvVp5kVObUlYpWBD1c,3376
|
|
7
7
|
amqp_client_python/domain/models/ssl_options.py,sha256=xOTSIKfbNZbYepY6o3SxuZvLPX3J5pvRoJXIJlyBsrI,613
|
|
8
8
|
amqp_client_python/domain/utils/__init__.py,sha256=lZL3bwchrTiiKx5d1RSVUJACAuPb7zOnoH9tZ65t_Ok,131
|
|
9
9
|
amqp_client_python/domain/utils/connection_type.py,sha256=AnEF8Nd3cQBCGX8rPKwEkKts8kwnGUAVr0nxnbpynLE,164
|
|
@@ -27,19 +27,19 @@ amqp_client_python/exceptions/rpc_provider_exception.py,sha256=_1Ytb8BOUT_9xEtG_
|
|
|
27
27
|
amqp_client_python/exceptions/thread_unsafe_exception.py,sha256=zcC4VyuxXWo66Tts1AQtkg07GK3OjXu3LEtt1AUj4jk,281
|
|
28
28
|
amqp_client_python/exceptions/timeout_exception.py,sha256=0YT-vivS44mHF4Jc3o2rlZFc6l_zzNjcxCHPS9ArgN0,271
|
|
29
29
|
amqp_client_python/rabbitmq/__init__.py,sha256=4N-j8rbrJj0kOOSIhSWJenj6JOJbzFNnW3bxwPGTyuM,941
|
|
30
|
-
amqp_client_python/rabbitmq/async_channel.py,sha256=
|
|
30
|
+
amqp_client_python/rabbitmq/async_channel.py,sha256=cAis6UoMlmhb1HtMavnb36KC-AkK7c-aVc1GufO6bUs,30495
|
|
31
31
|
amqp_client_python/rabbitmq/async_channel_factory.py,sha256=ec_5B3Sgazi_ncIG5lI5nnmkH3NprwvyjMhvgpG1BWA,371
|
|
32
|
-
amqp_client_python/rabbitmq/async_connection.py,sha256=
|
|
32
|
+
amqp_client_python/rabbitmq/async_connection.py,sha256=kq0jjXsuivmi101gj809ME_-LcrLYbey9rO9k0h-d0g,19063
|
|
33
33
|
amqp_client_python/rabbitmq/async_connection_factory.py,sha256=3N5MM1fPUdodAFLh58rf6vvwN9OAclDVzmG85Qve-Ss,849
|
|
34
|
-
amqp_client_python/rabbitmq/async_eventbus_rabbitmq.py,sha256=
|
|
34
|
+
amqp_client_python/rabbitmq/async_eventbus_rabbitmq.py,sha256=mjMtdm6hmony1PXZqOc9molVZ2GRT9wrwRvFRd4AQBs,14553
|
|
35
35
|
amqp_client_python/rabbitmq/channel_factory_rabbitmq.py,sha256=h_vGqHgL0DOtXC0tmVd5gzXVlNKL48rxpKpwaN3AlOM,310
|
|
36
36
|
amqp_client_python/rabbitmq/channel_rabbitmq.py,sha256=S3SqGCfVRoIuPyZW40bQZBpWEgbe5YlvZ8ei23a2VVw,13737
|
|
37
37
|
amqp_client_python/rabbitmq/connection_factory_rabbitmq.py,sha256=EKPG5gEsGwtpSKOhoPI5vK1sJOUARbOflOug6VREIrk,1022
|
|
38
38
|
amqp_client_python/rabbitmq/connection_rabbitmq.py,sha256=9A8NAPAB3i-5E43SV5sxjKFFoRb4pIhzAAUHrvOvOng,9772
|
|
39
39
|
amqp_client_python/rabbitmq/eventbus_rabbitmq.py,sha256=GZoJGVeBkMTPrQ5dIJqad-MdGmuuJsxGaXV1YGLdk5o,6980
|
|
40
|
-
amqp_client_python/rabbitmq/eventbus_wrapper_rabbitmq.py,sha256=
|
|
40
|
+
amqp_client_python/rabbitmq/eventbus_wrapper_rabbitmq.py,sha256=ee1tWk2_sQ3tRlvItZCqZtqfDLrjn-K4NCQKw7hNiwg,8225
|
|
41
41
|
amqp_client_python/rabbitmq/ioloop_factory.py,sha256=vfgndKLcazQU0a4elI5RNHnQCTEua--Jjo99bzX8iHo,856
|
|
42
|
-
amqp_client_python-0.2.
|
|
43
|
-
amqp_client_python-0.2.
|
|
44
|
-
amqp_client_python-0.2.
|
|
45
|
-
amqp_client_python-0.2.
|
|
42
|
+
amqp_client_python-0.2.2.dev1.dist-info/METADATA,sha256=JuldbMrR7B8K4abQT-WwYksmSKoBEY0ApFNYdBhtCd0,9888
|
|
43
|
+
amqp_client_python-0.2.2.dev1.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
44
|
+
amqp_client_python-0.2.2.dev1.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
45
|
+
amqp_client_python-0.2.2.dev1.dist-info/RECORD,,
|
{amqp_client_python-0.2.1.dev1.dist-info → amqp_client_python-0.2.2.dev1.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|