amqp-client-python 0.2.1__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.
@@ -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,14 @@ 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]] = {}
62
65
 
63
66
  @staticmethod
64
67
  def _serialize_payload(body, content_type: str = "application/json"):
@@ -87,6 +90,7 @@ class AsyncChannel:
87
90
  """
88
91
  LOGGER.info("Channel opened")
89
92
  self._channel = channel
93
+ self._declared_exchanges.clear()
90
94
  self.add_on_channel_close_callback()
91
95
  self.publisher_confirms and self.add_publish_confirms()
92
96
  self._prefetch_count and self.set_qos(self._prefetch_count)
@@ -120,12 +124,14 @@ class AsyncChannel:
120
124
  :param pika.channel.Channel: The closed channel
121
125
  :param Exception reason: why the channel was closed
122
126
  """
123
- LOGGER.info(f"Channel {channel} was closed: {reason}")
127
+ LOGGER.warning(f"Channel {channel} was closed by RabbitMQ: {reason}")
124
128
  self._consuming = False
129
+ if hasattr(self, "_declared_exchanges"):
130
+ self._declared_exchanges.clear()
125
131
  if self._connection.is_closing or self._connection.is_closed:
126
132
  LOGGER.info("Connection is closing or already closed")
127
133
  else:
128
- LOGGER.info("Closing connection")
134
+ LOGGER.info(f"Closing connection due to channel closure: {reason}")
129
135
  self._connection.close()
130
136
 
131
137
  def add_publish_confirms(self):
@@ -160,14 +166,36 @@ class AsyncChannel:
160
166
  be invoked by pika.
161
167
  :param str|unicode exchange_name: The name of the exchange to declare
162
168
  """
163
- LOGGER.info(f"Declaring exchange: {exchange_name}")
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
+ )
164
192
  # Note: using functools.partial is not required, it is demonstrating
165
193
  # how arbitrary data can be passed to the callback when it is called
166
194
  cb = partial(self.on_exchange_declareok, userdata=exchange_name)
167
195
  self._channel.exchange_declare(
168
196
  exchange=exchange_name,
169
- exchange_type=exchange_type,
170
- durable=durable,
197
+ exchange_type=exchange_type_str,
198
+ durable=durable_bool,
171
199
  callback=cb,
172
200
  )
173
201
 
@@ -525,22 +553,42 @@ class AsyncChannel:
525
553
  self,
526
554
  exchange_name,
527
555
  queue_name: str,
528
- exchange_type="direct",
556
+ exchange_type="topic",
529
557
  durable=True,
530
558
  auto_delete=False,
531
559
  ):
532
560
  if queue_name not in self.subscribes:
533
- if not len(self.subscribes):
534
- self.subscribes[queue_name] = {}
535
- self.setup_exchange(exchange_name, exchange_type)
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)
536
568
  self.queue_declare(queue_name, durable=durable, auto_delete=auto_delete)
537
569
 
538
570
  if queue_name not in self.consumers:
539
571
  self.consumers[queue_name] = False
540
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
+
541
587
  def register_handler(
542
588
  self, queue_name, routing_key, callback, content_type, response_timeout, auto_decode=True
543
589
  ):
590
+ if queue_name not in self.subscribes:
591
+ self.subscribes[queue_name] = {}
544
592
  if routing_key not in self.subscribes[queue_name]:
545
593
  self.subscribes[queue_name][routing_key] = {
546
594
  "handle": callback,
@@ -550,6 +598,7 @@ class AsyncChannel:
550
598
  }
551
599
  if "*" in routing_key and routing_key not in self.re_subscribes:
552
600
  self.re_subscribes.append(routing_key)
601
+ self._compile_routing_pattern(queue_name, routing_key)
553
602
 
554
603
  async def subscribe(
555
604
  self,
@@ -622,30 +671,39 @@ class AsyncChannel:
622
671
 
623
672
  async def handle_message(queue_name, basic_deliver, props):
624
673
  try:
625
- if basic_deliver.routing_key in self.subscribes[queue_name]:
626
- await process_message(
627
- self.subscribes[queue_name][basic_deliver.routing_key]
628
- )
629
- else:
630
- for routing_key in self.re_subscribes:
631
- if re.match(routing_key, basic_deliver.routing_key):
632
- return await process_message(
633
- self.subscribes[queue_name][routing_key]
634
- )
635
- if self.rpc_publisher_started and props.reply_to:
636
- self._channel_rpc.basic_publish(
637
- "",
638
- props.reply_to,
639
- "Provider Error: routing_key not binded",
640
- properties=BasicProperties(
641
- correlation_id=props.correlation_id,
642
- content_type="text/plain",
643
- type="error",
644
- ),
645
- )
646
- return not self.auto_ack and self._channel.is_open and self._channel.basic_nack(
647
- basic_deliver.delivery_tag, requeue=False
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
+ ),
648
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
+ )
649
707
 
650
708
  except BaseException as err:
651
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,32 +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
- content_type=params.get("content_type", "application/json"),
147
- auto_decode=params.get("auto_decode", True),
148
- )
149
- for routing_key in list(self.backup["rpc_subscribe"].keys()):
150
- params = self.backup["rpc_subscribe"][routing_key]
151
- await self.rpc_subscribe(
152
- params["queue_name"],
153
- params["exchange_name"],
154
- routing_key,
155
- params["callback"],
156
- params["timeout"],
157
- content_type=params.get("content_type", "application/json"),
158
- auto_decode=params.get("auto_decode", True),
159
- )
160
-
161
137
  future: Future = Future(loop=self.ioloop)
162
- self.callbacks.append((recorvery, future))
138
+ self.callbacks.append((self._recover_subscriptions, future))
163
139
  self.reconnect_delay = 1
164
140
  self.reconnecting = False
165
141
 
@@ -211,12 +187,39 @@ class AsyncConnection:
211
187
  self.reconnecting = True
212
188
  self.retry_connection()
213
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
+
214
220
  def retry_connection(self):
215
221
  """
216
222
  Attempts to reconnect to RabbitMQ with exponential backoff.
217
-
218
- If reconnection is successful, this method will recover previous subscriptions
219
- and RPC handlers.
220
223
  """
221
224
  if not self.is_open:
222
225
  if self.reconnect_delay > 30:
@@ -228,33 +231,9 @@ class AsyncConnection:
228
231
  self.reconnect_delay += 1
229
232
  else:
230
233
  if self.reconnecting:
231
- async def recorvery():
232
- for routing_key in list(self.backup["subscribe"].keys()):
233
- params = self.backup["subscribe"][routing_key]
234
- await self.subscribe(
235
- params["queue_name"],
236
- params["exchange_name"],
237
- routing_key,
238
- params["callback"],
239
- params["timeout"],
240
- content_type=params.get("content_type", "application/json"),
241
- auto_decode=params.get("auto_decode", True),
242
- )
243
- for routing_key in list(self.backup["rpc_subscribe"].keys()):
244
- params = self.backup["rpc_subscribe"][routing_key]
245
- await self.rpc_subscribe(
246
- params["queue_name"],
247
- params["exchange_name"],
248
- routing_key,
249
- params["callback"],
250
- params["timeout"],
251
- content_type=params.get("content_type", "application/json"),
252
- auto_decode=params.get("auto_decode", True),
253
- )
254
-
255
234
  self.reconnect_delay = 1
256
235
  self.reconnecting = False
257
- self.ioloop.create_task(self.add_callback(recorvery)) # type: ignore
236
+ self.ioloop.create_task(self.add_callback(self._recover_subscriptions)) # type: ignore
258
237
 
259
238
  @property
260
239
  def is_open(self) -> Optional[bool]:
@@ -402,6 +381,9 @@ class AsyncConnection:
402
381
  timeout,
403
382
  content_type="application/json",
404
383
  auto_decode=True,
384
+ exchange_type="topic",
385
+ durable=True,
386
+ auto_delete=False,
405
387
  ):
406
388
  """
407
389
  Registers an RPC handler for a specific routing key.
@@ -414,14 +396,22 @@ class AsyncConnection:
414
396
  timeout: Timeout in seconds for processing the received message
415
397
  content_type: Content type of expected message
416
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)
417
402
  """
418
- self.backup["rpc_subscribe"][routing_key] = {
403
+ sub_key = (queue_name, routing_key)
404
+ self.backup["rpc_subscribe"][sub_key] = {
419
405
  "queue_name": queue_name,
420
406
  "exchange_name": exchange_name,
407
+ "routing_key": routing_key,
421
408
  "callback": callback,
422
409
  "timeout": timeout,
423
410
  "content_type": content_type,
424
411
  "auto_decode": auto_decode,
412
+ "exchange_type": exchange_type,
413
+ "durable": durable,
414
+ "auto_delete": auto_delete,
425
415
  }
426
416
  await self._channel.rpc_subscribe(
427
417
  queue_name=queue_name,
@@ -431,6 +421,9 @@ class AsyncConnection:
431
421
  timeout=timeout,
432
422
  content_type=content_type,
433
423
  auto_decode=auto_decode,
424
+ exchange_type=exchange_type,
425
+ durable=durable,
426
+ auto_delete=auto_delete,
434
427
  )
435
428
 
436
429
  async def subscribe(
@@ -442,6 +435,9 @@ class AsyncConnection:
442
435
  timeout: Optional[float],
443
436
  content_type: str = "application/json",
444
437
  auto_decode: bool = True,
438
+ exchange_type: str = "topic",
439
+ durable: bool = True,
440
+ auto_delete: bool = False,
445
441
  ):
446
442
  """
447
443
  Subscribes to messages with a specific routing key.
@@ -454,14 +450,22 @@ class AsyncConnection:
454
450
  timeout: Timeout in seconds for processing the received message
455
451
  content_type: Content type of expected message
456
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)
457
456
  """
458
- self.backup["subscribe"][routing_key] = {
457
+ sub_key = (queue_name, routing_key)
458
+ self.backup["subscribe"][sub_key] = {
459
459
  "queue_name": queue_name,
460
460
  "exchange_name": exchange_name,
461
+ "routing_key": routing_key,
461
462
  "callback": callback,
462
463
  "timeout": timeout,
463
464
  "content_type": content_type,
464
465
  "auto_decode": auto_decode,
466
+ "exchange_type": exchange_type,
467
+ "durable": durable,
468
+ "auto_delete": auto_delete,
465
469
  }
466
470
  await self._channel.subscribe(
467
471
  exchange_name=exchange_name,
@@ -471,6 +475,9 @@ class AsyncConnection:
471
475
  timeout=timeout,
472
476
  content_type=content_type,
473
477
  auto_decode=auto_decode,
478
+ exchange_type=exchange_type,
479
+ durable=durable,
480
+ auto_delete=auto_delete,
474
481
  )
475
482
 
476
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
@@ -242,6 +242,11 @@ class AsyncEventbusRabbitMQ:
242
242
  extra_kwargs["content_type"] = content_type
243
243
  if not auto_decode:
244
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
245
250
  await self._rpc_server_connection.rpc_subscribe(
246
251
  self.config.options.rpc_queue_name,
247
252
  self.config.options.rpc_exchange_name,
@@ -296,6 +301,11 @@ class AsyncEventbusRabbitMQ:
296
301
  extra_kwargs["content_type"] = content_type
297
302
  if not auto_decode:
298
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
299
309
  await self._sub_connection.subscribe(
300
310
  self.config.options.queue_name,
301
311
  exchange_name,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: amqp-client-python
3
- Version: 0.2.1
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.9
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;
@@ -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=lx-x4uyMpyjelZjMuyUxLsORz1ro1vDjD33j0EGYD-o,2141
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,11 +27,11 @@ 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=RcMVmsiGOpTgkTZMeC9b8TQ0Vm95291YZ_ZlBEL9KTY,27529
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=D1TNJUUDjxXFPeYPELXItmm7LNeZtBhwSpL3qRAj1zM,18942
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=lyvTn5qJV7rr4qlWWIoX9_WMR-pFjLxo0OHiWWjn9y8,13910
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
@@ -39,7 +39,7 @@ amqp_client_python/rabbitmq/connection_rabbitmq.py,sha256=9A8NAPAB3i-5E43SV5sxjK
39
39
  amqp_client_python/rabbitmq/eventbus_rabbitmq.py,sha256=GZoJGVeBkMTPrQ5dIJqad-MdGmuuJsxGaXV1YGLdk5o,6980
40
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.1.dist-info/METADATA,sha256=KYfngfw5-_f4Nsv-WRt0Sds7lNHVCgTReE5qy-o2_6I,10455
43
- amqp_client_python-0.2.1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
44
- amqp_client_python-0.2.1.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
45
- amqp_client_python-0.2.1.dist-info/RECORD,,
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.32.4
2
+ Generator: hatchling 1.33.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any