amqp-client-python 0.2.0.post1__py3-none-any.whl → 0.2.1__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.
@@ -2,3 +2,14 @@ from .rabbitmq import EventbusRabbitMQ, AsyncEventbusRabbitMQ, EventbusWrapperRa
2
2
  from .domain.models import Config, Options, SSLOptions
3
3
  from .domain.utils import ConnectionType
4
4
  from pika import DeliveryMode
5
+
6
+ __all__ = [
7
+ "EventbusRabbitMQ",
8
+ "AsyncEventbusRabbitMQ",
9
+ "EventbusWrapperRabbitMQ",
10
+ "Config",
11
+ "Options",
12
+ "SSLOptions",
13
+ "ConnectionType",
14
+ "DeliveryMode",
15
+ ]
@@ -1,3 +1,9 @@
1
1
  from .config import Config
2
2
  from .options import Options
3
3
  from .ssl_options import SSLOptions
4
+
5
+ __all__ = [
6
+ "Config",
7
+ "Options",
8
+ "SSLOptions",
9
+ ]
@@ -1,2 +1,7 @@
1
1
  from .connection_type import ConnectionType
2
2
  from .types import HandlerType
3
+
4
+ __all__ = [
5
+ "ConnectionType",
6
+ "HandlerType",
7
+ ]
@@ -4,3 +4,12 @@ from .async_provider_handler import AsyncProviderHandler
4
4
  from .async_subscriber_handler import AsyncSubscriberHandler
5
5
  from .provider_handler import ProviderHandler
6
6
  from .subscriber_handler import SubscriberHandler
7
+
8
+ __all__ = [
9
+ "IntegrationEvent",
10
+ "IntegrationEventHandler",
11
+ "AsyncProviderHandler",
12
+ "AsyncSubscriberHandler",
13
+ "ProviderHandler",
14
+ "SubscriberHandler",
15
+ ]
@@ -7,3 +7,15 @@ from .publish_timeout_exception import PublishTimeoutException
7
7
  from .auto_reconnect_exception import AutoReconnectException
8
8
  from .blocking_exception import BlockingException
9
9
  from .thread_unsafe_exception import ThreadUnsafeException
10
+
11
+ __all__ = [
12
+ "EventBusException",
13
+ "NackException",
14
+ "RpcProviderException",
15
+ "TimeoutException",
16
+ "ResponseTimeoutException",
17
+ "PublishTimeoutException",
18
+ "AutoReconnectException",
19
+ "BlockingException",
20
+ "ThreadUnsafeException",
21
+ ]
@@ -1,6 +1,6 @@
1
1
  from .async_eventbus_rabbitmq import AsyncEventbusRabbitMQ
2
2
  from .async_connection_factory import AsyncConnectionFactoryRabbitMQ
3
- from .async_connection import AsyncConnection, ConnectionType
3
+ from .async_connection import AsyncConnection
4
4
  from .async_channel_factory import AsyncChannelFactoryRabbitMQ
5
5
  from .async_channel import AsyncChannel
6
6
  from .eventbus_rabbitmq import EventbusRabbitMQ
@@ -9,3 +9,17 @@ from .connection_rabbitmq import ConnectionRabbitMQ
9
9
  from .channel_factory_rabbitmq import ChannelFactoryRabbitMQ
10
10
  from .channel_rabbitmq import ChannelRabbitMQ
11
11
  from .eventbus_wrapper_rabbitmq import EventbusWrapperRabbitMQ
12
+
13
+ __all__ = [
14
+ "AsyncEventbusRabbitMQ",
15
+ "AsyncConnectionFactoryRabbitMQ",
16
+ "AsyncConnection",
17
+ "AsyncChannelFactoryRabbitMQ",
18
+ "AsyncChannel",
19
+ "EventbusRabbitMQ",
20
+ "ConnectionFactoryRabbitMQ",
21
+ "ConnectionRabbitMQ",
22
+ "ChannelFactoryRabbitMQ",
23
+ "ChannelRabbitMQ",
24
+ "EventbusWrapperRabbitMQ",
25
+ ]
@@ -1,3 +1,4 @@
1
+ from collections import deque
1
2
  from typing import MutableMapping, Mapping, Optional, Union, Dict, List
2
3
  from .async_channel_factory import AsyncChannelFactoryRabbitMQ
3
4
  from amqp_client_python.exceptions import (
@@ -59,13 +60,19 @@ class AsyncChannel:
59
60
  self._deliveries: MutableMapping[int, Future] = {}
60
61
  self.response_timeout = 60
61
62
 
63
+ @staticmethod
64
+ def _serialize_payload(body, content_type: str = "application/json"):
65
+ if isinstance(body, (bytes, bytearray, memoryview)):
66
+ return body
67
+ return dumps(body)
68
+
62
69
  @property
63
70
  def is_open(self) -> Optional[bool]:
64
71
  return self._channel and self._channel.is_open
65
72
 
66
- def open(self, connection: AsyncioConnection, callbacks=[]):
73
+ def open(self, connection: AsyncioConnection, callbacks=None):
67
74
  if not self.is_open:
68
- self.callbacks = callbacks
75
+ self.callbacks = callbacks if callbacks is not None else deque()
69
76
  self._connection = connection
70
77
  self.ioloop = connection.ioloop
71
78
  self._channel = self.channel_factory.create_channel(
@@ -87,10 +94,15 @@ class AsyncChannel:
87
94
  self.ioloop.create_task(self.process_callbacks()) # type: ignore
88
95
 
89
96
  async def process_callbacks(self):
90
- for callback, future in self.callbacks:
91
- result = await callback()
92
- if not future.done():
93
- future.set_result(result)
97
+ while self.callbacks:
98
+ callback, future = self.callbacks.popleft()
99
+ try:
100
+ result = await callback()
101
+ if not future.done():
102
+ future.set_result(result)
103
+ except Exception as exc:
104
+ if not future.done():
105
+ future.set_exception(exc)
94
106
 
95
107
  def add_on_channel_close_callback(self):
96
108
  """This method tells pika to call the on_channel_closed method if
@@ -302,7 +314,7 @@ class AsyncChannel:
302
314
  if self.rpc_publisher_started:
303
315
  return True
304
316
  if self.rpc_publisher_starting:
305
- return await self.rpc_consumer_future
317
+ return await self.rpc_publisher_future
306
318
  self.rpc_publisher_starting = True
307
319
  self.rpc_publisher_future = self.ioloop.create_future()
308
320
 
@@ -355,7 +367,7 @@ class AsyncChannel:
355
367
  self._channel.basic_publish(
356
368
  exchange_name,
357
369
  routing_key,
358
- dumps(body), # Serialize payload to JSON
370
+ self._serialize_payload(body, content_type),
359
371
  properties=BasicProperties(
360
372
  reply_to=self._callback_queue, # Response queue address
361
373
  correlation_id=corr_id, # Request/response matching ID
@@ -430,7 +442,7 @@ class AsyncChannel:
430
442
  self._channel.basic_publish(
431
443
  exchange_name,
432
444
  routing_key,
433
- dumps(body),
445
+ self._serialize_payload(body, content_type),
434
446
  properties=BasicProperties(
435
447
  reply_to=self._callback_queue,
436
448
  content_type=content_type,
@@ -485,13 +497,14 @@ class AsyncChannel:
485
497
  exchange_type="topic",
486
498
  durable=True,
487
499
  auto_delete=False,
500
+ auto_decode=True,
488
501
  ):
489
502
  await self.start_rpc_publisher()
490
503
  self.register_queue(
491
504
  exchange_name, queue_name, exchange_type, durable, auto_delete
492
505
  )
493
506
  self.register_handler(
494
- queue_name, routing_key, callback, content_type, timeout
507
+ queue_name, routing_key, callback, content_type, timeout, auto_decode=auto_decode
495
508
  )
496
509
 
497
510
  self.queue_bind(queue_name, exchange_name, routing_key)
@@ -526,13 +539,14 @@ class AsyncChannel:
526
539
  self.consumers[queue_name] = False
527
540
 
528
541
  def register_handler(
529
- self, queue_name, routing_key, callback, content_type, response_timeout
542
+ self, queue_name, routing_key, callback, content_type, response_timeout, auto_decode=True
530
543
  ):
531
544
  if routing_key not in self.subscribes[queue_name]:
532
545
  self.subscribes[queue_name][routing_key] = {
533
546
  "handle": callback,
534
547
  "content_type": content_type,
535
548
  "response_timeout": response_timeout or self.response_timeout,
549
+ "auto_decode": auto_decode,
536
550
  }
537
551
  if "*" in routing_key and routing_key not in self.re_subscribes:
538
552
  self.re_subscribes.append(routing_key)
@@ -548,12 +562,13 @@ class AsyncChannel:
548
562
  exchange_type: str = "topic",
549
563
  durable: bool = True,
550
564
  auto_delete: bool = False,
565
+ auto_decode: bool = True,
551
566
  ) -> None:
552
567
  self.register_queue(
553
568
  exchange_name, queue_name, exchange_type, durable, auto_delete
554
569
  )
555
570
  self.register_handler(
556
- queue_name, routing_key, callback, content_type, timeout
571
+ queue_name, routing_key, callback, content_type, timeout, auto_decode=auto_decode
557
572
  )
558
573
 
559
574
  self.queue_bind(queue_name, exchange_name, routing_key)
@@ -581,7 +596,11 @@ class AsyncChannel:
581
596
 
582
597
  async def process_message(handler: HandlerType):
583
598
  nonlocal body
584
- body = loads(body)
599
+ if handler.get("auto_decode", True) and handler.get("content_type") == "application/json":
600
+ try:
601
+ body = loads(body)
602
+ except Exception:
603
+ pass
585
604
  response = await wait_for(
586
605
  handler["handle"](body),
587
606
  timeout=handler["response_timeout"],
@@ -1,4 +1,6 @@
1
- from typing import Optional, Callable, Awaitable, Tuple, Dict, List, Any
1
+ from collections import deque
2
+ from typing import Optional, Callable, Awaitable, Tuple, Dict, Any
3
+
2
4
  from .async_connection_factory import AsyncConnectionFactoryRabbitMQ, AsyncioConnection
3
5
  from .async_channel import AsyncChannel
4
6
  from ..exceptions import AutoReconnectException
@@ -60,7 +62,7 @@ class AsyncConnection:
60
62
  self.openning = False
61
63
  self.reconnecting = False
62
64
  self.reconnect_delay = 1
63
- self.callbacks: List[Tuple[Callable, Future]] = []
65
+ self.callbacks: deque[Tuple[Callable, Future]] = deque()
64
66
  self.type = connection_type
65
67
  self.backup: Dict[str, Dict[str, Any]] = {
66
68
  "exchange": {},
@@ -87,6 +89,7 @@ class AsyncConnection:
87
89
  if not self.ioloop:
88
90
  self.ioloop = get_event_loop()
89
91
  self.openning = True
92
+ self._closing = False
90
93
  self._connection = self.connection_factory.create_connection(
91
94
  uri=uri,
92
95
  on_connection_open=self.on_connection_open,
@@ -129,6 +132,37 @@ class AsyncConnection:
129
132
  signal=self.signal,
130
133
  )
131
134
  self._channel.publisher_confirms = self.publisher_confirms
135
+
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
+ future: Future = Future(loop=self.ioloop)
162
+ self.callbacks.append((recorvery, future))
163
+ self.reconnect_delay = 1
164
+ self.reconnecting = False
165
+
132
166
  self._channel.open(self._connection, self.callbacks)
133
167
 
134
168
  def on_connection_open_error(self, _unused_connection, err):
@@ -193,30 +227,34 @@ class AsyncConnection:
193
227
  )
194
228
  self.reconnect_delay += 1
195
229
  else:
196
-
197
- async def recorvery():
198
- for routing_key in self.backup["subscribe"]:
199
- params = self.backup["subscribe"][routing_key]
200
- await self.subscribe(
201
- params["queue_name"],
202
- params["exchange_name"],
203
- routing_key,
204
- params["callback"],
205
- params["timeout"],
206
- )
207
- for routing_key in self.backup["rpc_subscribe"]:
208
- params = self.backup["rpc_subscribe"][routing_key]
209
- await self.rpc_subscribe(
210
- params["queue_name"],
211
- params["exchange_name"],
212
- routing_key,
213
- params["callback"],
214
- params["timeout"],
215
- )
216
-
217
- self.ioloop.create_task(self.add_callback(recorvery)) # type: ignore
218
- self.reconnect_delay = 1
219
- self.reconnecting = False
230
+ 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
+ self.reconnect_delay = 1
256
+ self.reconnecting = False
257
+ self.ioloop.create_task(self.add_callback(recorvery)) # type: ignore
220
258
 
221
259
  @property
222
260
  def is_open(self) -> Optional[bool]:
@@ -356,7 +394,14 @@ class AsyncConnection:
356
394
  )
357
395
 
358
396
  async def rpc_subscribe(
359
- self, queue_name, exchange_name, routing_key, callback, timeout
397
+ self,
398
+ queue_name,
399
+ exchange_name,
400
+ routing_key,
401
+ callback,
402
+ timeout,
403
+ content_type="application/json",
404
+ auto_decode=True,
360
405
  ):
361
406
  """
362
407
  Registers an RPC handler for a specific routing key.
@@ -367,24 +412,16 @@ class AsyncConnection:
367
412
  routing_key: The routing key to subscribe to
368
413
  callback: The function to call when a message is received
369
414
  timeout: Timeout in seconds for processing the received message
370
-
371
- Examples:
372
- >>> async def handle_rpc(body):
373
- result = process_request(body)
374
- return json.dumps(result).encode()
375
- >>> await connection.rpc_subscribe(
376
- "rpc_queue",
377
- "rpc_exchange",
378
- "user.find",
379
- handle_rpc,
380
- 10.0
381
- )
415
+ content_type: Content type of expected message
416
+ auto_decode: Whether to auto-decode JSON payload
382
417
  """
383
418
  self.backup["rpc_subscribe"][routing_key] = {
384
419
  "queue_name": queue_name,
385
420
  "exchange_name": exchange_name,
386
421
  "callback": callback,
387
422
  "timeout": timeout,
423
+ "content_type": content_type,
424
+ "auto_decode": auto_decode,
388
425
  }
389
426
  await self._channel.rpc_subscribe(
390
427
  queue_name=queue_name,
@@ -392,6 +429,8 @@ class AsyncConnection:
392
429
  routing_key=routing_key,
393
430
  callback=callback,
394
431
  timeout=timeout,
432
+ content_type=content_type,
433
+ auto_decode=auto_decode,
395
434
  )
396
435
 
397
436
  async def subscribe(
@@ -401,6 +440,8 @@ class AsyncConnection:
401
440
  routing_key: str,
402
441
  callback: Callable[[Any], Awaitable[None]],
403
442
  timeout: Optional[float],
443
+ content_type: str = "application/json",
444
+ auto_decode: bool = True,
404
445
  ):
405
446
  """
406
447
  Subscribes to messages with a specific routing key.
@@ -411,23 +452,16 @@ class AsyncConnection:
411
452
  routing_key: The routing key to subscribe to
412
453
  callback: The function to call when a message is received
413
454
  timeout: Timeout in seconds for processing the received message
414
-
415
- Examples:
416
- >>> async def handle_message(body):
417
- print(f"Received: {body}")
418
- >>> await connection.subscribe(
419
- "notifications_queue",
420
- "notifications",
421
- "email.send",
422
- handle_message,
423
- 5.0
424
- )
455
+ content_type: Content type of expected message
456
+ auto_decode: Whether to auto-decode JSON payload
425
457
  """
426
458
  self.backup["subscribe"][routing_key] = {
427
459
  "queue_name": queue_name,
428
460
  "exchange_name": exchange_name,
429
461
  "callback": callback,
430
462
  "timeout": timeout,
463
+ "content_type": content_type,
464
+ "auto_decode": auto_decode,
431
465
  }
432
466
  await self._channel.subscribe(
433
467
  exchange_name=exchange_name,
@@ -435,6 +469,8 @@ class AsyncConnection:
435
469
  routing_key=routing_key,
436
470
  callback=callback,
437
471
  timeout=timeout,
472
+ content_type=content_type,
473
+ auto_decode=auto_decode,
438
474
  )
439
475
 
440
476
  async def add_callback(
@@ -464,7 +500,7 @@ class AsyncConnection:
464
500
  >>> result = await connection.add_callback(my_operation, 10.0)
465
501
  """
466
502
  try:
467
- if self.is_open and self._channel.is_open:
503
+ if self.is_open and self._channel and self._channel.is_open:
468
504
  return await callback()
469
505
  else:
470
506
  future: Future = Future(loop=self.ioloop)
@@ -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,18 @@ 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
236
245
  await self._rpc_server_connection.rpc_subscribe(
237
246
  self.config.options.rpc_queue_name,
238
247
  self.config.options.rpc_exchange_name,
239
248
  name,
240
249
  handler,
241
250
  timeout,
251
+ **extra_kwargs,
242
252
  )
243
253
 
244
254
  if self._rpc_server_connection.open(self.config.url):
@@ -252,6 +262,8 @@ class AsyncEventbusRabbitMQ:
252
262
  handler: Callable[[Any], Awaitable[None]],
253
263
  timeout: Optional[float] = None,
254
264
  connection_timeout: int = 16,
265
+ content_type: str = "application/json",
266
+ auto_decode: bool = True,
255
267
  ) -> None:
256
268
  """
257
269
  Register a provider to listen on queue of bus
@@ -262,6 +274,8 @@ class AsyncEventbusRabbitMQ:
262
274
  handler: message handler, it will be called when a message is received
263
275
  timeout: timeout in seconds for waiting for process the received message
264
276
  connection_timeout: timeout for waiting for connection restabilishment
277
+ content_type: Content type of expected message
278
+ auto_decode: Whether to auto-decode JSON payload (default True)
265
279
 
266
280
  Raises:
267
281
  AutoReconnectException: when cannout reconnect on the gived timeout
@@ -277,12 +291,18 @@ class AsyncEventbusRabbitMQ:
277
291
  """
278
292
 
279
293
  async def add_subscribe():
294
+ extra_kwargs = {}
295
+ if content_type != "application/json":
296
+ extra_kwargs["content_type"] = content_type
297
+ if not auto_decode:
298
+ extra_kwargs["auto_decode"] = auto_decode
280
299
  await self._sub_connection.subscribe(
281
300
  self.config.options.queue_name,
282
301
  exchange_name,
283
302
  routing_key,
284
303
  handler,
285
304
  timeout,
305
+ **extra_kwargs,
286
306
  )
287
307
 
288
308
  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, routing_key, handler, response_timeout, connection_timeout
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, callback, response_timeout, connection_timeout
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, callback, response_timeout, connection_timeout
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):
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: amqp-client-python
3
- Version: 0.2.0.post1
3
+ Version: 0.2.1
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.8
21
20
  Classifier: Programming Language :: Python :: 3.9
22
21
  Classifier: Programming Language :: Python :: 3.10
23
22
  Classifier: Programming Language :: Python :: 3.11
@@ -81,19 +80,22 @@ pip install amqp-rs
81
80
  ### Table of Compatibility
82
81
  | version | compatible with |
83
82
  | ---- | ---- |
83
+ | 0.2.1 | ~0.2.0 |
84
84
  | 0.2.0 | 0.2.0 |
85
85
  | 0.1.14 | ~0.1.12 |
86
86
 
87
87
  ### Installation
88
88
 
89
- You can install `amqp-client-python` using pip:
89
+ You can install `amqp-client-python` using uv or pip:
90
90
  ```bash
91
+ uv add amqp-client-python
92
+ # or
91
93
  pip install amqp-client-python
92
94
  ```
93
95
 
94
96
  ### Prerequisites
95
97
 
96
- * Python 3.7+
98
+ * Python 3.9+
97
99
  * A running RabbitMQ instance.
98
100
 
99
101
  ### Examples:
@@ -110,15 +112,18 @@ from amqp_client_python import (
110
112
  )
111
113
  config = Config(Options("queue", "rpc_queue", "rpc_exchange"))
112
114
  eventbus = AsyncEventbusRabbitMQ(config)
113
- # publish
115
+ # publish (supports dict/str with auto-JSON, or raw bytes/bytearray without overhead)
116
+ await eventbus.publish("rpc_exchange", "routing.key", {"message": "content"})
117
+ await eventbus.publish("rpc_exchange", "routing.key", b"raw_binary_payload")
114
118
 
115
- eventbus.publish("rpc_exchange", "routing.key", "message_content")
116
- # subscribe
119
+ # subscribe (auto_decode=True delivers parsed JSON dict; auto_decode=False delivers raw bytes)
117
120
  async def subscribe_handler(body) -> None:
118
121
  print(body, type(body), flush=True) # handle messages
119
122
  await eventbus.subscribe("rpc_exchange", "routing.key", subscribe_handler)
123
+
120
124
  # rpc_publish
121
- response = await eventbus.rpc_client("rpc_exchange", "user.find", "message_content")
125
+ response = await eventbus.rpc_client("rpc_exchange", "user.find", {"name": "alice"})
126
+
122
127
  # provider
123
128
  async def rpc_provider_handler(body) -> bytes:
124
129
  print(f"body: {body}")
@@ -1,14 +1,14 @@
1
- amqp_client_python/__init__.py,sha256=6kv8G2KlcvbSVIE8Inxb3ugMNSTjg7KK9_HvyDprfb8,213
1
+ amqp_client_python/__init__.py,sha256=XUO7lawlYTTqfS2-oxRmviPz0_KqwIeD1IpugdzZm4U,401
2
2
  amqp_client_python/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  amqp_client_python/signals.py,sha256=6mW4oWBQnbJwuKox6ceeDbxTM8v9aCDEb0o2zyhpb5g,1263
4
- amqp_client_python/domain/models/__init__.py,sha256=T57j9Sr5Z0pWyGT598CpAx_KhoV-MVpgHTvfESZAa90,92
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
6
  amqp_client_python/domain/models/options.py,sha256=lx-x4uyMpyjelZjMuyUxLsORz1ro1vDjD33j0EGYD-o,2141
7
7
  amqp_client_python/domain/models/ssl_options.py,sha256=xOTSIKfbNZbYepY6o3SxuZvLPX3J5pvRoJXIJlyBsrI,613
8
- amqp_client_python/domain/utils/__init__.py,sha256=ppob9KPfzq9K3Fd2-RJr6CnwmPDOdgaAzPqOV0Q17P0,75
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
10
10
  amqp_client_python/domain/utils/types.py,sha256=veT5lgZhUxophpmWItYaz56XxJTXGJ2L6J6oG_TbSWs,160
11
- amqp_client_python/event/__init__.py,sha256=VxqWqZLw2od3bxGHmf2d2I6QOctJNxUEP8lmjPELHF4,325
11
+ amqp_client_python/event/__init__.py,sha256=vx6_rIQ94xdsPpNBq0peTEDqSU90UNh9YBk1tq5MALg,501
12
12
  amqp_client_python/event/async_provider_handler.py,sha256=tncL6X6R9cLLBZLeZ3xsl2ZJVj6Ag84wq79hNRWZSpc,228
13
13
  amqp_client_python/event/async_subscriber_handler.py,sha256=bsoX5WDAlRwTnZXy_9xwn4rz29Mj1Sci6v36wkzwlnU,210
14
14
  amqp_client_python/event/event.py,sha256=2CSRJJMDI6AZDkO4eXAQdVSW38bdE-CcfbJTdK--cc0,115
@@ -16,7 +16,7 @@ amqp_client_python/event/integration_event.py,sha256=lVBVA4hnZA77_XrS26yWhegK4n9
16
16
  amqp_client_python/event/integration_event_handler.py,sha256=eW6m0SD48J0yJhnYFchMHTs1TgQIFLmoRu_xnOyRRXQ,165
17
17
  amqp_client_python/event/provider_handler.py,sha256=F8M3XhULNZdta9_OLbOqLoeLXw9WIEeucemGjVPmdWQ,217
18
18
  amqp_client_python/event/subscriber_handler.py,sha256=0bb_76kIAofBvBtZvQ2SM2n6e-Ip-L8JA0NvxmimLgc,199
19
- amqp_client_python/exceptions/__init__.py,sha256=86fPynIGbtWiERWOkpdW5YZjg50LHLaATs8K4odUlMs,495
19
+ amqp_client_python/exceptions/__init__.py,sha256=mBe8Cuir3H5ULHgzRPio05aqRDlcrTDToM4DAfPV3V8,755
20
20
  amqp_client_python/exceptions/auto_reconnect_exception.py,sha256=20E806dFoRQcDhdFd0wZWM-QmsxTAKCotZqfepuyNXk,277
21
21
  amqp_client_python/exceptions/blocking_exception.py,sha256=QAEIe20r-ND6FOfJTmFdK4ezjHeDr-SJsrWnbXNfal0,277
22
22
  amqp_client_python/exceptions/eventbus_exception.py,sha256=999Pn1HPCb_k-0zGUVE4wJo94vLQDZamoZcYHVXRmCI,217
@@ -26,20 +26,20 @@ amqp_client_python/exceptions/response_timeout_exception.py,sha256=us_PiJPSk1pnz
26
26
  amqp_client_python/exceptions/rpc_provider_exception.py,sha256=_1Ytb8BOUT_9xEtG_ibGx3cEMMM4kUVtduEVtXn-d6M,280
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
- amqp_client_python/rabbitmq/__init__.py,sha256=ZFaBSr8TDzZlHwaT344ao20W2FD5c3RI2sa5kiEeKeI,630
30
- amqp_client_python/rabbitmq/async_channel.py,sha256=OtW0agoSMG-v38lKJsFNRMaFoqubEauQCmLaLN7bQek,26667
29
+ amqp_client_python/rabbitmq/__init__.py,sha256=4N-j8rbrJj0kOOSIhSWJenj6JOJbzFNnW3bxwPGTyuM,941
30
+ amqp_client_python/rabbitmq/async_channel.py,sha256=RcMVmsiGOpTgkTZMeC9b8TQ0Vm95291YZ_ZlBEL9KTY,27529
31
31
  amqp_client_python/rabbitmq/async_channel_factory.py,sha256=ec_5B3Sgazi_ncIG5lI5nnmkH3NprwvyjMhvgpG1BWA,371
32
- amqp_client_python/rabbitmq/async_connection.py,sha256=lWArTU5lXKMrKnnTivhrWs8c9PJnUNMAD5_QI3FZu-s,16906
32
+ amqp_client_python/rabbitmq/async_connection.py,sha256=D1TNJUUDjxXFPeYPELXItmm7LNeZtBhwSpL3qRAj1zM,18942
33
33
  amqp_client_python/rabbitmq/async_connection_factory.py,sha256=3N5MM1fPUdodAFLh58rf6vvwN9OAclDVzmG85Qve-Ss,849
34
- amqp_client_python/rabbitmq/async_eventbus_rabbitmq.py,sha256=4PCtqhVNT3cq-d0anfdKPzhmmETD-HMqIu-MVuZmZZA,12950
34
+ amqp_client_python/rabbitmq/async_eventbus_rabbitmq.py,sha256=lyvTn5qJV7rr4qlWWIoX9_WMR-pFjLxo0OHiWWjn9y8,13910
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=i3C-sZO9EifanfRXReiib56fxxzjV3Cx71rdbJORg78,7442
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.0.post1.dist-info/METADATA,sha256=w5Hdo9NIX282a9bOtOVK-AIJvvRLU0aUBjr4LPxr2ys,10206
43
- amqp_client_python-0.2.0.post1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
44
- amqp_client_python-0.2.0.post1.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
45
- amqp_client_python-0.2.0.post1.dist-info/RECORD,,
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.29.0
2
+ Generator: hatchling 1.32.4
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any