amqp-client-python 0.2.0__py3-none-any.whl → 0.2.1.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.
@@ -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
+ ]
@@ -10,15 +10,11 @@ class Config:
10
10
  self, options: Options, ssl_options: Optional[SSLOptions] = None
11
11
  ) -> None:
12
12
  """
13
- Create an Config object thats hold and mount the connection information.
13
+ Create a Config object that holds and manages the connection information.
14
14
 
15
15
  Args:
16
- options: hold information for estabilish connection
17
- ssl_options: hold information for estabilish SSL connections
18
-
19
- Returns:
20
-
21
- Raises:
16
+ options: holds information for establishing connection
17
+ ssl_options: holds information for establishing SSL connections
22
18
 
23
19
  Examples:
24
20
  >>> Config(
@@ -32,16 +28,13 @@ class Config:
32
28
 
33
29
  def build(self) -> "Config":
34
30
  """
35
- Create an Config object thats hold and mount the connection information.
36
-
37
- Args:
31
+ Create a Config object that holds and manages the connection information.
38
32
 
39
33
  Returns:
40
34
  Config object
41
35
 
42
- Raises:
43
-
44
36
  Examples:
37
+ >>> # config = Config(...)
45
38
  >>> config.build()
46
39
  """
47
40
  opt = {
@@ -18,7 +18,7 @@ class Options:
18
18
  **kwargs: Dict[str, Any]
19
19
  ) -> None:
20
20
  """
21
- Create an Options object that hold the credentials and configs options.
21
+ Create an Options object that holds the credentials and configuration options.
22
22
 
23
23
  Args:
24
24
  queue_name: name of queue that will be used for subscriptions
@@ -34,10 +34,6 @@ class Options:
34
34
  publisher_confirms: wait for publisher confirmations
35
35
  **kwargs: pika options
36
36
 
37
- Returns:
38
-
39
- Raises:
40
-
41
37
  Examples:
42
38
  >>> Options("example", "example.rpc", "example.rpc", "amqp://admin:admin@localhost:5672/")
43
39
 
@@ -3,18 +3,13 @@ class SSLOptions:
3
3
  self, certfile_path: str, keyfile_path: str, ca_certs_path: str
4
4
  ) -> None:
5
5
  """
6
- Create an SslOptions object that hold the certs paths.
6
+ Create an SslOptions object that holds the cert paths.
7
7
 
8
8
  Args:
9
9
  certfile_path: cert file path string
10
10
  keyfile_path: private key file path string
11
11
  ca_certs_path: ca file path string
12
12
 
13
- Returns:
14
- Um dicionário com as notas da escala e os graus.
15
-
16
- Raises:
17
-
18
13
  Examples:
19
14
  >>> SSLOptions("./.certs/cert.pem", "./.certs/privkey.pem", "./.certs/ca.pem")
20
15
  """
@@ -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 (
@@ -63,9 +64,9 @@ class AsyncChannel:
63
64
  def is_open(self) -> Optional[bool]:
64
65
  return self._channel and self._channel.is_open
65
66
 
66
- def open(self, connection: AsyncioConnection, callbacks=[]):
67
+ def open(self, connection: AsyncioConnection, callbacks=None):
67
68
  if not self.is_open:
68
- self.callbacks = callbacks
69
+ self.callbacks = callbacks if callbacks is not None else deque()
69
70
  self._connection = connection
70
71
  self.ioloop = connection.ioloop
71
72
  self._channel = self.channel_factory.create_channel(
@@ -87,10 +88,15 @@ class AsyncChannel:
87
88
  self.ioloop.create_task(self.process_callbacks()) # type: ignore
88
89
 
89
90
  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)
91
+ while self.callbacks:
92
+ callback, future = self.callbacks.popleft()
93
+ try:
94
+ result = await callback()
95
+ if not future.done():
96
+ future.set_result(result)
97
+ except Exception as exc:
98
+ if not future.done():
99
+ future.set_exception(exc)
94
100
 
95
101
  def add_on_channel_close_callback(self):
96
102
  """This method tells pika to call the on_channel_closed method if
@@ -108,7 +114,6 @@ class AsyncChannel:
108
114
  :param pika.channel.Channel: The closed channel
109
115
  :param Exception reason: why the channel was closed
110
116
  """
111
- print("channel closed:", reason, flush=True)
112
117
  LOGGER.info(f"Channel {channel} was closed: {reason}")
113
118
  self._consuming = False
114
119
  if self._connection.is_closing or self._connection.is_closed:
@@ -238,10 +243,10 @@ class AsyncChannel:
238
243
  )
239
244
  else:
240
245
  future.set_result(body)
241
- return not self.auto_ack and self._channel_rpc.basic_ack(
246
+ return not self.auto_ack and self._channel_rpc.is_open and self._channel_rpc.basic_ack(
242
247
  basic_deliver.delivery_tag
243
248
  )
244
- not self.auto_ack and self._channel_rpc.basic_nack(
249
+ not self.auto_ack and self._channel_rpc.is_open and self._channel_rpc.basic_nack(
245
250
  basic_deliver.delivery_tag, requeue=False
246
251
  )
247
252
 
@@ -303,7 +308,7 @@ class AsyncChannel:
303
308
  if self.rpc_publisher_started:
304
309
  return True
305
310
  if self.rpc_publisher_starting:
306
- return await self.rpc_consumer_future
311
+ return await self.rpc_publisher_future
307
312
  self.rpc_publisher_starting = True
308
313
  self.rpc_publisher_future = self.ioloop.create_future()
309
314
 
@@ -334,34 +339,41 @@ class AsyncChannel:
334
339
  routing_key: str,
335
340
  body,
336
341
  content_type,
337
- timeout,
342
+ response_timeout,
338
343
  delivery_mode=DeliveryMode.Transient,
339
344
  expiration: Optional[Union[str, None]] = None,
340
345
  **key_args,
341
346
  ):
347
+ # Create future to track RPC response
342
348
  future = self.ioloop.create_future() # type: ignore
349
+ # Generate unique correlation ID for RPC matching
343
350
  corr_id = str(uuid4())
351
+ # Store future in tracking dictionary
344
352
  self.rpc_futures[corr_id] = {"response": future}
353
+ # Setup cleanup callback for completed RPC
345
354
  clean_response = partial(self.clean_rpc_response, corr_id)
346
355
  future.add_done_callback(clean_response)
347
356
 
357
+ # Ensure RPC response consumer is operational
348
358
  await self.start_rpc_consumer()
349
359
 
360
+ # Publish RPC request with metadata headers
350
361
  self._channel.basic_publish(
351
362
  exchange_name,
352
363
  routing_key,
353
- dumps(body),
364
+ dumps(body), # Serialize payload to JSON
354
365
  properties=BasicProperties(
355
- reply_to=self._callback_queue,
356
- correlation_id=corr_id,
357
- content_type=content_type,
358
- delivery_mode=delivery_mode,
359
- expiration=expiration,
366
+ reply_to=self._callback_queue, # Response queue address
367
+ correlation_id=corr_id, # Request/response matching ID
368
+ content_type=content_type, # Payload format descriptor
369
+ delivery_mode=delivery_mode, # Persistent/Transient delivery
370
+ expiration=expiration, # Message TTL control
360
371
  **key_args,
361
372
  ),
362
373
  mandatory=False,
363
374
  )
364
375
 
376
+ # Setup timeout handler for unfulfilled requests
365
377
  def not_arrived(id: str):
366
378
  if id in self.rpc_futures:
367
379
  future = self.rpc_futures[id]
@@ -375,27 +387,40 @@ class AsyncChannel:
375
387
  )
376
388
 
377
389
  func = partial(not_arrived, corr_id)
378
- self.ioloop.call_later(timeout, func) # type: ignore
390
+ # Schedule timeout check using event loop
391
+ self.ioloop.call_later(response_timeout, func) # type: ignore
379
392
 
393
+ # Handle publisher confirms if enabled
380
394
  if self.publisher_confirms:
381
395
  return await self.handle_publish(future, corr_id)
396
+ # Return final response or exception
382
397
  return await future
383
398
 
384
399
  async def handle_publish(self, future, corr_id):
400
+ """Manages dual confirmation flow (publish + response)"""
401
+
402
+ # Create separate tracking for publish confirmation
385
403
  publish_future = self.ioloop.create_future()
404
+ # Store both futures in tracking system
386
405
  self.rpc_futures[corr_id]["published"] = publish_future
406
+ # Initiate confirmation tracking
387
407
  self.publish_confirmation(publish_future)
408
+ # Concurrently wait for first completed operation:
409
+ # - Publisher confirmation (ACK/NACK)
410
+ # - RPC response arrival
388
411
  done_all, pending_all = await wait(
389
412
  [publish_future, future], return_when=FIRST_COMPLETED
390
413
  )
414
+ # Extract potential exception from completed operation
391
415
  exception = done_all.pop().exception()
392
416
  if exception:
417
+ # Cancel remaining operations
393
418
  [undone.cancel() for undone in pending_all]
394
419
  raise exception
395
- elif len(pending_all):
420
+ elif len(pending_all): # Response still pending after confirmation
396
421
  return await future
397
422
  else:
398
- return future.result()
423
+ return future.result() # Return final result directly
399
424
 
400
425
  async def publish(
401
426
  self,
@@ -436,9 +461,15 @@ class AsyncChannel:
436
461
  return await publish_future
437
462
 
438
463
  def publish_confirmation(self, future: Future):
464
+ """Tracks message publishes requiring confirmation"""
465
+
466
+ # Increment sequence counter for message tracking
439
467
  self._message_number += 1
468
+ # Store future reference for confirmation handling
440
469
  self.futures[self._message_number] = future
470
+ # Link to delivery tracking system
441
471
  self._deliveries[self._message_number] = future
472
+ # Register cleanup callback using partial binding
442
473
  clean = partial(self.clean_publish_confirmation, self._message_number)
443
474
  future.add_done_callback(clean)
444
475
 
@@ -455,7 +486,7 @@ class AsyncChannel:
455
486
  routing_key: str,
456
487
  queue_name: str,
457
488
  callback,
458
- response_timeout,
489
+ timeout,
459
490
  content_type="application/json",
460
491
  exchange_type="topic",
461
492
  durable=True,
@@ -466,7 +497,7 @@ class AsyncChannel:
466
497
  exchange_name, queue_name, exchange_type, durable, auto_delete
467
498
  )
468
499
  self.register_handler(
469
- queue_name, routing_key, callback, content_type, response_timeout
500
+ queue_name, routing_key, callback, content_type, timeout
470
501
  )
471
502
 
472
503
  self.queue_bind(queue_name, exchange_name, routing_key)
@@ -518,7 +549,7 @@ class AsyncChannel:
518
549
  routing_key: str,
519
550
  queue_name: str,
520
551
  callback,
521
- response_timeout: Optional[int],
552
+ timeout: Optional[int],
522
553
  content_type: str = "application/json",
523
554
  exchange_type: str = "topic",
524
555
  durable: bool = True,
@@ -528,7 +559,7 @@ class AsyncChannel:
528
559
  exchange_name, queue_name, exchange_type, durable, auto_delete
529
560
  )
530
561
  self.register_handler(
531
- queue_name, routing_key, callback, content_type, response_timeout
562
+ queue_name, routing_key, callback, content_type, timeout
532
563
  )
533
564
 
534
565
  self.queue_bind(queue_name, exchange_name, routing_key)
@@ -572,7 +603,7 @@ class AsyncChannel:
572
603
  type="normal",
573
604
  ),
574
605
  )
575
- not self.auto_ack and not self._channel.basic_ack(
606
+ not self.auto_ack and self._channel.is_open and not self._channel.basic_ack(
576
607
  basic_deliver.delivery_tag
577
608
  )
578
609
 
@@ -599,7 +630,7 @@ class AsyncChannel:
599
630
  type="error",
600
631
  ),
601
632
  )
602
- return not self.auto_ack and self._channel.basic_nack(
633
+ return not self.auto_ack and self._channel.is_open and self._channel.basic_nack(
603
634
  basic_deliver.delivery_tag, requeue=False
604
635
  )
605
636
 
@@ -615,7 +646,7 @@ class AsyncChannel:
615
646
  type="error",
616
647
  ),
617
648
  )
618
- not self.auto_ack and self._channel.basic_nack(
649
+ not self.auto_ack and self._channel.is_open and self._channel.basic_nack(
619
650
  basic_deliver.delivery_tag, requeue=False
620
651
  )
621
652