amqp-client-python 0.2.0__py3-none-any.whl → 0.2.0.post1__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/config.py +5 -12
- amqp_client_python/domain/models/options.py +1 -5
- amqp_client_python/domain/models/ssl_options.py +1 -6
- amqp_client_python/rabbitmq/async_channel.py +45 -20
- amqp_client_python/rabbitmq/async_connection.py +233 -17
- amqp_client_python/rabbitmq/async_eventbus_rabbitmq.py +67 -43
- amqp_client_python/rabbitmq/channel_rabbitmq.py +26 -14
- amqp_client_python/rabbitmq/connection_rabbitmq.py +5 -5
- amqp_client_python/rabbitmq/eventbus_rabbitmq.py +4 -4
- {amqp_client_python-0.2.0.dist-info → amqp_client_python-0.2.0.post1.dist-info}/METADATA +40 -13
- {amqp_client_python-0.2.0.dist-info → amqp_client_python-0.2.0.post1.dist-info}/RECORD +15 -15
- {amqp_client_python-0.2.0.dist-info → amqp_client_python-0.2.0.post1.dist-info}/WHEEL +1 -1
- {amqp_client_python-0.2.0.dist-info → amqp_client_python-0.2.0.post1.dist-info/licenses}/LICENSE +0 -0
|
@@ -10,15 +10,11 @@ class Config:
|
|
|
10
10
|
self, options: Options, ssl_options: Optional[SSLOptions] = None
|
|
11
11
|
) -> None:
|
|
12
12
|
"""
|
|
13
|
-
Create
|
|
13
|
+
Create a Config object that holds and manages the connection information.
|
|
14
14
|
|
|
15
15
|
Args:
|
|
16
|
-
options:
|
|
17
|
-
ssl_options:
|
|
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
|
|
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
|
|
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
|
|
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
|
"""
|
|
@@ -108,7 +108,6 @@ class AsyncChannel:
|
|
|
108
108
|
:param pika.channel.Channel: The closed channel
|
|
109
109
|
:param Exception reason: why the channel was closed
|
|
110
110
|
"""
|
|
111
|
-
print("channel closed:", reason, flush=True)
|
|
112
111
|
LOGGER.info(f"Channel {channel} was closed: {reason}")
|
|
113
112
|
self._consuming = False
|
|
114
113
|
if self._connection.is_closing or self._connection.is_closed:
|
|
@@ -238,10 +237,10 @@ class AsyncChannel:
|
|
|
238
237
|
)
|
|
239
238
|
else:
|
|
240
239
|
future.set_result(body)
|
|
241
|
-
return not self.auto_ack and self._channel_rpc.basic_ack(
|
|
240
|
+
return not self.auto_ack and self._channel_rpc.is_open and self._channel_rpc.basic_ack(
|
|
242
241
|
basic_deliver.delivery_tag
|
|
243
242
|
)
|
|
244
|
-
not self.auto_ack and self._channel_rpc.basic_nack(
|
|
243
|
+
not self.auto_ack and self._channel_rpc.is_open and self._channel_rpc.basic_nack(
|
|
245
244
|
basic_deliver.delivery_tag, requeue=False
|
|
246
245
|
)
|
|
247
246
|
|
|
@@ -334,34 +333,41 @@ class AsyncChannel:
|
|
|
334
333
|
routing_key: str,
|
|
335
334
|
body,
|
|
336
335
|
content_type,
|
|
337
|
-
|
|
336
|
+
response_timeout,
|
|
338
337
|
delivery_mode=DeliveryMode.Transient,
|
|
339
338
|
expiration: Optional[Union[str, None]] = None,
|
|
340
339
|
**key_args,
|
|
341
340
|
):
|
|
341
|
+
# Create future to track RPC response
|
|
342
342
|
future = self.ioloop.create_future() # type: ignore
|
|
343
|
+
# Generate unique correlation ID for RPC matching
|
|
343
344
|
corr_id = str(uuid4())
|
|
345
|
+
# Store future in tracking dictionary
|
|
344
346
|
self.rpc_futures[corr_id] = {"response": future}
|
|
347
|
+
# Setup cleanup callback for completed RPC
|
|
345
348
|
clean_response = partial(self.clean_rpc_response, corr_id)
|
|
346
349
|
future.add_done_callback(clean_response)
|
|
347
350
|
|
|
351
|
+
# Ensure RPC response consumer is operational
|
|
348
352
|
await self.start_rpc_consumer()
|
|
349
353
|
|
|
354
|
+
# Publish RPC request with metadata headers
|
|
350
355
|
self._channel.basic_publish(
|
|
351
356
|
exchange_name,
|
|
352
357
|
routing_key,
|
|
353
|
-
dumps(body),
|
|
358
|
+
dumps(body), # Serialize payload to JSON
|
|
354
359
|
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,
|
|
360
|
+
reply_to=self._callback_queue, # Response queue address
|
|
361
|
+
correlation_id=corr_id, # Request/response matching ID
|
|
362
|
+
content_type=content_type, # Payload format descriptor
|
|
363
|
+
delivery_mode=delivery_mode, # Persistent/Transient delivery
|
|
364
|
+
expiration=expiration, # Message TTL control
|
|
360
365
|
**key_args,
|
|
361
366
|
),
|
|
362
367
|
mandatory=False,
|
|
363
368
|
)
|
|
364
369
|
|
|
370
|
+
# Setup timeout handler for unfulfilled requests
|
|
365
371
|
def not_arrived(id: str):
|
|
366
372
|
if id in self.rpc_futures:
|
|
367
373
|
future = self.rpc_futures[id]
|
|
@@ -375,27 +381,40 @@ class AsyncChannel:
|
|
|
375
381
|
)
|
|
376
382
|
|
|
377
383
|
func = partial(not_arrived, corr_id)
|
|
378
|
-
|
|
384
|
+
# Schedule timeout check using event loop
|
|
385
|
+
self.ioloop.call_later(response_timeout, func) # type: ignore
|
|
379
386
|
|
|
387
|
+
# Handle publisher confirms if enabled
|
|
380
388
|
if self.publisher_confirms:
|
|
381
389
|
return await self.handle_publish(future, corr_id)
|
|
390
|
+
# Return final response or exception
|
|
382
391
|
return await future
|
|
383
392
|
|
|
384
393
|
async def handle_publish(self, future, corr_id):
|
|
394
|
+
"""Manages dual confirmation flow (publish + response)"""
|
|
395
|
+
|
|
396
|
+
# Create separate tracking for publish confirmation
|
|
385
397
|
publish_future = self.ioloop.create_future()
|
|
398
|
+
# Store both futures in tracking system
|
|
386
399
|
self.rpc_futures[corr_id]["published"] = publish_future
|
|
400
|
+
# Initiate confirmation tracking
|
|
387
401
|
self.publish_confirmation(publish_future)
|
|
402
|
+
# Concurrently wait for first completed operation:
|
|
403
|
+
# - Publisher confirmation (ACK/NACK)
|
|
404
|
+
# - RPC response arrival
|
|
388
405
|
done_all, pending_all = await wait(
|
|
389
406
|
[publish_future, future], return_when=FIRST_COMPLETED
|
|
390
407
|
)
|
|
408
|
+
# Extract potential exception from completed operation
|
|
391
409
|
exception = done_all.pop().exception()
|
|
392
410
|
if exception:
|
|
411
|
+
# Cancel remaining operations
|
|
393
412
|
[undone.cancel() for undone in pending_all]
|
|
394
413
|
raise exception
|
|
395
|
-
elif len(pending_all):
|
|
414
|
+
elif len(pending_all): # Response still pending after confirmation
|
|
396
415
|
return await future
|
|
397
416
|
else:
|
|
398
|
-
return future.result()
|
|
417
|
+
return future.result() # Return final result directly
|
|
399
418
|
|
|
400
419
|
async def publish(
|
|
401
420
|
self,
|
|
@@ -436,9 +455,15 @@ class AsyncChannel:
|
|
|
436
455
|
return await publish_future
|
|
437
456
|
|
|
438
457
|
def publish_confirmation(self, future: Future):
|
|
458
|
+
"""Tracks message publishes requiring confirmation"""
|
|
459
|
+
|
|
460
|
+
# Increment sequence counter for message tracking
|
|
439
461
|
self._message_number += 1
|
|
462
|
+
# Store future reference for confirmation handling
|
|
440
463
|
self.futures[self._message_number] = future
|
|
464
|
+
# Link to delivery tracking system
|
|
441
465
|
self._deliveries[self._message_number] = future
|
|
466
|
+
# Register cleanup callback using partial binding
|
|
442
467
|
clean = partial(self.clean_publish_confirmation, self._message_number)
|
|
443
468
|
future.add_done_callback(clean)
|
|
444
469
|
|
|
@@ -455,7 +480,7 @@ class AsyncChannel:
|
|
|
455
480
|
routing_key: str,
|
|
456
481
|
queue_name: str,
|
|
457
482
|
callback,
|
|
458
|
-
|
|
483
|
+
timeout,
|
|
459
484
|
content_type="application/json",
|
|
460
485
|
exchange_type="topic",
|
|
461
486
|
durable=True,
|
|
@@ -466,7 +491,7 @@ class AsyncChannel:
|
|
|
466
491
|
exchange_name, queue_name, exchange_type, durable, auto_delete
|
|
467
492
|
)
|
|
468
493
|
self.register_handler(
|
|
469
|
-
queue_name, routing_key, callback, content_type,
|
|
494
|
+
queue_name, routing_key, callback, content_type, timeout
|
|
470
495
|
)
|
|
471
496
|
|
|
472
497
|
self.queue_bind(queue_name, exchange_name, routing_key)
|
|
@@ -518,7 +543,7 @@ class AsyncChannel:
|
|
|
518
543
|
routing_key: str,
|
|
519
544
|
queue_name: str,
|
|
520
545
|
callback,
|
|
521
|
-
|
|
546
|
+
timeout: Optional[int],
|
|
522
547
|
content_type: str = "application/json",
|
|
523
548
|
exchange_type: str = "topic",
|
|
524
549
|
durable: bool = True,
|
|
@@ -528,7 +553,7 @@ class AsyncChannel:
|
|
|
528
553
|
exchange_name, queue_name, exchange_type, durable, auto_delete
|
|
529
554
|
)
|
|
530
555
|
self.register_handler(
|
|
531
|
-
queue_name, routing_key, callback, content_type,
|
|
556
|
+
queue_name, routing_key, callback, content_type, timeout
|
|
532
557
|
)
|
|
533
558
|
|
|
534
559
|
self.queue_bind(queue_name, exchange_name, routing_key)
|
|
@@ -572,7 +597,7 @@ class AsyncChannel:
|
|
|
572
597
|
type="normal",
|
|
573
598
|
),
|
|
574
599
|
)
|
|
575
|
-
not self.auto_ack and not self._channel.basic_ack(
|
|
600
|
+
not self.auto_ack and self._channel.is_open and not self._channel.basic_ack(
|
|
576
601
|
basic_deliver.delivery_tag
|
|
577
602
|
)
|
|
578
603
|
|
|
@@ -599,7 +624,7 @@ class AsyncChannel:
|
|
|
599
624
|
type="error",
|
|
600
625
|
),
|
|
601
626
|
)
|
|
602
|
-
return not self.auto_ack and self._channel.basic_nack(
|
|
627
|
+
return not self.auto_ack and self._channel.is_open and self._channel.basic_nack(
|
|
603
628
|
basic_deliver.delivery_tag, requeue=False
|
|
604
629
|
)
|
|
605
630
|
|
|
@@ -615,7 +640,7 @@ class AsyncChannel:
|
|
|
615
640
|
type="error",
|
|
616
641
|
),
|
|
617
642
|
)
|
|
618
|
-
not self.auto_ack and self._channel.basic_nack(
|
|
643
|
+
not self.auto_ack and self._channel.is_open and self._channel.basic_nack(
|
|
619
644
|
basic_deliver.delivery_tag, requeue=False
|
|
620
645
|
)
|
|
621
646
|
|
|
@@ -21,6 +21,33 @@ class AsyncConnection:
|
|
|
21
21
|
connection_type: Optional[ConnectionType] = None,
|
|
22
22
|
signal=Signal(),
|
|
23
23
|
) -> None:
|
|
24
|
+
"""
|
|
25
|
+
Manages asynchronous connections to RabbitMQ with automatic reconnection capabilities.
|
|
26
|
+
|
|
27
|
+
This class handles connection lifecycle, channel management, and provides methods for
|
|
28
|
+
publishing messages, subscribing to queues, and implementing RPC patterns.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
ioloop: The asyncio event loop to use for async operations
|
|
32
|
+
publisher_confirms: Whether to enable publisher confirmations for this connection
|
|
33
|
+
prefetch_count: Maximum number of unacknowledged messages to prefetch
|
|
34
|
+
auto_ack: Whether to automatically acknowledge messages
|
|
35
|
+
connection_type: Type of connection (PUBLISH, SUBSCRIBE, RPC_CLIENT, RPC_SERVER)
|
|
36
|
+
signal: Signal object for event handling
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
None: None
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
>>> connection = AsyncConnection(
|
|
43
|
+
get_event_loop(),
|
|
44
|
+
publisher_confirms=True,
|
|
45
|
+
prefetch_count=10,
|
|
46
|
+
auto_ack=False,
|
|
47
|
+
connection_type=ConnectionType.PUBLISH
|
|
48
|
+
)
|
|
49
|
+
>>> connection.open("amqp://guest:guest@localhost:5672/%2F")
|
|
50
|
+
"""
|
|
24
51
|
self.ioloop = ioloop
|
|
25
52
|
self.publisher_confirms = publisher_confirms
|
|
26
53
|
self.connection_factory = AsyncConnectionFactoryRabbitMQ()
|
|
@@ -43,6 +70,18 @@ class AsyncConnection:
|
|
|
43
70
|
}
|
|
44
71
|
|
|
45
72
|
def open(self, uri):
|
|
73
|
+
"""
|
|
74
|
+
Opens a connection to RabbitMQ using the provided URI.
|
|
75
|
+
|
|
76
|
+
If the connection is not already open and not in the process of opening,
|
|
77
|
+
this method will initiate a new connection.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
uri: The AMQP URI to connect to (e.g., "amqp://guest:guest@localhost:5672/")
|
|
81
|
+
|
|
82
|
+
Examples:
|
|
83
|
+
>>> connection.open("amqp://guest:guest@localhost:5672/")
|
|
84
|
+
"""
|
|
46
85
|
self.url = uri
|
|
47
86
|
if not self.is_open and not self.openning:
|
|
48
87
|
if not self.ioloop:
|
|
@@ -55,13 +94,31 @@ class AsyncConnection:
|
|
|
55
94
|
on_connection_closed=self.on_connection_closed,
|
|
56
95
|
custum_ioloop=self.ioloop,
|
|
57
96
|
)
|
|
97
|
+
return True
|
|
98
|
+
return False
|
|
58
99
|
|
|
59
100
|
async def close(self):
|
|
101
|
+
"""
|
|
102
|
+
Closes the connection to RabbitMQ gracefully.
|
|
103
|
+
|
|
104
|
+
This method will only close the connection if it is currently open.
|
|
105
|
+
|
|
106
|
+
Examples:
|
|
107
|
+
>>> await connection.close()
|
|
108
|
+
"""
|
|
60
109
|
if self.is_open:
|
|
61
110
|
self._closing = True
|
|
62
111
|
self._connection.close()
|
|
63
112
|
|
|
64
113
|
def on_connection_open(self, _unused_connection):
|
|
114
|
+
"""
|
|
115
|
+
Callback invoked when the connection to RabbitMQ is established.
|
|
116
|
+
|
|
117
|
+
This method creates a channel and emits a connection event.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
_unused_connection: The connection object (not used)
|
|
121
|
+
"""
|
|
65
122
|
LOGGER.info(f"connection openned {_unused_connection}, {self._connection}")
|
|
66
123
|
self.signal.emmit(Event.CONNECTED, condiction=self.type, loop=self.ioloop)
|
|
67
124
|
self.openning = False
|
|
@@ -75,17 +132,29 @@ class AsyncConnection:
|
|
|
75
132
|
self._channel.open(self._connection, self.callbacks)
|
|
76
133
|
|
|
77
134
|
def on_connection_open_error(self, _unused_connection, err):
|
|
135
|
+
"""
|
|
136
|
+
Callback invoked when the connection to RabbitMQ is established.
|
|
137
|
+
|
|
138
|
+
This method creates a channel and emits a connection event.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
_unused_connection: The connection object (not used)
|
|
142
|
+
"""
|
|
78
143
|
LOGGER.info(f"connection open error: {err}, will attempt a connection")
|
|
79
144
|
self.openning = False
|
|
80
145
|
self.reconnect()
|
|
81
146
|
|
|
82
147
|
def on_connection_closed(self, _unused_connection, reason):
|
|
83
|
-
"""
|
|
148
|
+
"""
|
|
149
|
+
Callback invoked when the connection to RabbitMQ is closed unexpectedly.
|
|
150
|
+
|
|
151
|
+
This method is invoked by pika when the connection to RabbitMQ is
|
|
84
152
|
closed unexpectedly. Since it is unexpected, we will reconnect to
|
|
85
153
|
RabbitMQ if it disconnects.
|
|
86
|
-
|
|
87
|
-
:
|
|
88
|
-
connection
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
_unused_connection: The closed connection object
|
|
157
|
+
reason: Exception representing reason for loss of connection
|
|
89
158
|
"""
|
|
90
159
|
self._channel = None
|
|
91
160
|
if self._closing:
|
|
@@ -98,15 +167,23 @@ class AsyncConnection:
|
|
|
98
167
|
self.reconnect()
|
|
99
168
|
|
|
100
169
|
def reconnect(self):
|
|
101
|
-
"""
|
|
102
|
-
|
|
103
|
-
|
|
170
|
+
"""
|
|
171
|
+
Initiates the reconnection process.
|
|
172
|
+
|
|
173
|
+
Will be invoked if the connection can't be opened or is closed.
|
|
174
|
+
Indicates that a reconnect is necessary.
|
|
104
175
|
"""
|
|
105
176
|
if not self.reconnecting:
|
|
106
177
|
self.reconnecting = True
|
|
107
178
|
self.retry_connection()
|
|
108
179
|
|
|
109
180
|
def retry_connection(self):
|
|
181
|
+
"""
|
|
182
|
+
Attempts to reconnect to RabbitMQ with exponential backoff.
|
|
183
|
+
|
|
184
|
+
If reconnection is successful, this method will recover previous subscriptions
|
|
185
|
+
and RPC handlers.
|
|
186
|
+
"""
|
|
110
187
|
if not self.is_open:
|
|
111
188
|
if self.reconnect_delay > 30:
|
|
112
189
|
self.reconnect_delay = 30
|
|
@@ -125,7 +202,7 @@ class AsyncConnection:
|
|
|
125
202
|
params["exchange_name"],
|
|
126
203
|
routing_key,
|
|
127
204
|
params["callback"],
|
|
128
|
-
params["
|
|
205
|
+
params["timeout"],
|
|
129
206
|
)
|
|
130
207
|
for routing_key in self.backup["rpc_subscribe"]:
|
|
131
208
|
params = self.backup["rpc_subscribe"][routing_key]
|
|
@@ -134,7 +211,7 @@ class AsyncConnection:
|
|
|
134
211
|
params["exchange_name"],
|
|
135
212
|
routing_key,
|
|
136
213
|
params["callback"],
|
|
137
|
-
params["
|
|
214
|
+
params["timeout"],
|
|
138
215
|
)
|
|
139
216
|
|
|
140
217
|
self.ioloop.create_task(self.add_callback(recorvery)) # type: ignore
|
|
@@ -143,6 +220,12 @@ class AsyncConnection:
|
|
|
143
220
|
|
|
144
221
|
@property
|
|
145
222
|
def is_open(self) -> Optional[bool]:
|
|
223
|
+
"""
|
|
224
|
+
Checks if the connection is currently open.
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Optional[bool]: True if the connection is open, False otherwise, or None if no connection exists
|
|
228
|
+
"""
|
|
146
229
|
return self._connection and self._connection.is_open
|
|
147
230
|
|
|
148
231
|
def stop(self) -> None:
|
|
@@ -170,17 +253,49 @@ class AsyncConnection:
|
|
|
170
253
|
routing_key: str,
|
|
171
254
|
body: Any,
|
|
172
255
|
content_type: str,
|
|
173
|
-
|
|
256
|
+
response_timeout,
|
|
174
257
|
delivery_mode,
|
|
175
258
|
expiration,
|
|
176
259
|
**kwargs,
|
|
177
260
|
):
|
|
261
|
+
"""
|
|
262
|
+
Sends an RPC request and waits for a response.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
exchange_name: The exchange to publish to
|
|
266
|
+
routing_key: The routing key for the message
|
|
267
|
+
body: The message body to send
|
|
268
|
+
content_type: Content type of the message
|
|
269
|
+
response_timeout: Timeout in seconds for waiting for a response
|
|
270
|
+
delivery_mode: Delivery mode (persistent or transient)
|
|
271
|
+
expiration: Maximum lifetime of the message in the queue
|
|
272
|
+
**kwargs: Additional message properties
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Any: The response from the RPC server
|
|
276
|
+
|
|
277
|
+
Raises:
|
|
278
|
+
PublishTimeoutException: If publisher confirmation times out
|
|
279
|
+
NackException: If the message is rejected by the broker
|
|
280
|
+
ResponseTimeoutException: If no response is received within the response_timeout
|
|
281
|
+
|
|
282
|
+
Examples:
|
|
283
|
+
>>> response = await connection.rpc_client(
|
|
284
|
+
"rpc_exchange",
|
|
285
|
+
"user.find",
|
|
286
|
+
{"id": 123},
|
|
287
|
+
"application/json",
|
|
288
|
+
5.0,
|
|
289
|
+
DeliveryMode.Transient,
|
|
290
|
+
"60000"
|
|
291
|
+
)
|
|
292
|
+
"""
|
|
178
293
|
return await self._channel.rpc_client(
|
|
179
294
|
exchange_name,
|
|
180
295
|
routing_key,
|
|
181
296
|
body,
|
|
182
297
|
content_type,
|
|
183
|
-
|
|
298
|
+
response_timeout,
|
|
184
299
|
delivery_mode,
|
|
185
300
|
expiration,
|
|
186
301
|
**kwargs,
|
|
@@ -197,6 +312,38 @@ class AsyncConnection:
|
|
|
197
312
|
expiration,
|
|
198
313
|
**kwargs,
|
|
199
314
|
):
|
|
315
|
+
"""
|
|
316
|
+
Publishes a message to RabbitMQ.
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
exchange_name: The exchange to publish to
|
|
320
|
+
routing_key: The routing key for the message
|
|
321
|
+
body: The message body to send
|
|
322
|
+
content_type: Content type of the message
|
|
323
|
+
timeout: Timeout in seconds for publisher confirmation
|
|
324
|
+
delivery_mode: Delivery mode (persistent or transient)
|
|
325
|
+
expiration: Maximum lifetime of the message in the queue
|
|
326
|
+
**kwargs: Additional message properties
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
Optional[bool]: True if publisher confirms enabled and message was confirmed,
|
|
330
|
+
None if publisher confirms disabled
|
|
331
|
+
|
|
332
|
+
Raises:
|
|
333
|
+
PublishTimeoutException: If publisher confirmation times out
|
|
334
|
+
NackException: If the message is rejected by the broker
|
|
335
|
+
|
|
336
|
+
Examples:
|
|
337
|
+
>>> result = await connection.publish(
|
|
338
|
+
"notifications",
|
|
339
|
+
"email.send",
|
|
340
|
+
{"to": "user@example.com", "subject": "Hello"},
|
|
341
|
+
"application/json",
|
|
342
|
+
5.0,
|
|
343
|
+
DeliveryMode.Persistent,
|
|
344
|
+
"60000"
|
|
345
|
+
)
|
|
346
|
+
"""
|
|
200
347
|
return await self._channel.publish(
|
|
201
348
|
exchange_name,
|
|
202
349
|
routing_key,
|
|
@@ -209,37 +356,85 @@ class AsyncConnection:
|
|
|
209
356
|
)
|
|
210
357
|
|
|
211
358
|
async def rpc_subscribe(
|
|
212
|
-
self, queue_name, exchange_name, routing_key, callback,
|
|
359
|
+
self, queue_name, exchange_name, routing_key, callback, timeout
|
|
213
360
|
):
|
|
361
|
+
"""
|
|
362
|
+
Registers an RPC handler for a specific routing key.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
queue_name: The queue to consume from
|
|
366
|
+
exchange_name: The exchange to bind to
|
|
367
|
+
routing_key: The routing key to subscribe to
|
|
368
|
+
callback: The function to call when a message is received
|
|
369
|
+
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
|
+
)
|
|
382
|
+
"""
|
|
214
383
|
self.backup["rpc_subscribe"][routing_key] = {
|
|
215
384
|
"queue_name": queue_name,
|
|
216
385
|
"exchange_name": exchange_name,
|
|
217
386
|
"callback": callback,
|
|
218
|
-
"
|
|
387
|
+
"timeout": timeout,
|
|
219
388
|
}
|
|
220
389
|
await self._channel.rpc_subscribe(
|
|
221
390
|
queue_name=queue_name,
|
|
222
391
|
exchange_name=exchange_name,
|
|
223
392
|
routing_key=routing_key,
|
|
224
393
|
callback=callback,
|
|
225
|
-
|
|
394
|
+
timeout=timeout,
|
|
226
395
|
)
|
|
227
396
|
|
|
228
397
|
async def subscribe(
|
|
229
|
-
self,
|
|
398
|
+
self,
|
|
399
|
+
queue_name: str,
|
|
400
|
+
exchange_name: str,
|
|
401
|
+
routing_key: str,
|
|
402
|
+
callback: Callable[[Any], Awaitable[None]],
|
|
403
|
+
timeout: Optional[float],
|
|
230
404
|
):
|
|
405
|
+
"""
|
|
406
|
+
Subscribes to messages with a specific routing key.
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
queue_name: The queue to consume from
|
|
410
|
+
exchange_name: The exchange to bind to
|
|
411
|
+
routing_key: The routing key to subscribe to
|
|
412
|
+
callback: The function to call when a message is received
|
|
413
|
+
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
|
+
)
|
|
425
|
+
"""
|
|
231
426
|
self.backup["subscribe"][routing_key] = {
|
|
232
427
|
"queue_name": queue_name,
|
|
233
428
|
"exchange_name": exchange_name,
|
|
234
429
|
"callback": callback,
|
|
235
|
-
"
|
|
430
|
+
"timeout": timeout,
|
|
236
431
|
}
|
|
237
432
|
await self._channel.subscribe(
|
|
238
433
|
exchange_name=exchange_name,
|
|
239
434
|
queue_name=queue_name,
|
|
240
435
|
routing_key=routing_key,
|
|
241
436
|
callback=callback,
|
|
242
|
-
|
|
437
|
+
timeout=timeout,
|
|
243
438
|
)
|
|
244
439
|
|
|
245
440
|
async def add_callback(
|
|
@@ -247,6 +442,27 @@ class AsyncConnection:
|
|
|
247
442
|
callback: Callable[..., Awaitable[Any]],
|
|
248
443
|
connection_timeout: Optional[float] = None,
|
|
249
444
|
):
|
|
445
|
+
"""
|
|
446
|
+
Executes a callback when the connection is ready or queues it for later execution.
|
|
447
|
+
|
|
448
|
+
If the connection and channel are open, executes the callback immediately.
|
|
449
|
+
Otherwise, queues the callback to be executed when the connection is established.
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
callback: The async function to call
|
|
453
|
+
connection_timeout: Maximum time to wait for the connection to be established
|
|
454
|
+
|
|
455
|
+
Returns:
|
|
456
|
+
Any: The result of the callback
|
|
457
|
+
|
|
458
|
+
Raises:
|
|
459
|
+
AutoReconnectException: When the connection cannot be established within the timeout
|
|
460
|
+
|
|
461
|
+
Examples:
|
|
462
|
+
>>> async def my_operation():
|
|
463
|
+
return await connection.publish(...)
|
|
464
|
+
>>> result = await connection.add_callback(my_operation, 10.0)
|
|
465
|
+
"""
|
|
250
466
|
try:
|
|
251
467
|
if self.is_open and self._channel.is_open:
|
|
252
468
|
return await callback()
|
|
@@ -23,8 +23,8 @@ class AsyncEventbusRabbitMQ:
|
|
|
23
23
|
rpc_server_auto_ack: bool = False,
|
|
24
24
|
) -> None:
|
|
25
25
|
"""
|
|
26
|
-
Create an AsyncEventbusRabbitMQ object
|
|
27
|
-
|
|
26
|
+
Create an AsyncEventbusRabbitMQ object that interacts with Bus that provides
|
|
27
|
+
some connection management abstractions
|
|
28
28
|
|
|
29
29
|
Args:
|
|
30
30
|
config: the Config object
|
|
@@ -40,27 +40,25 @@ class AsyncEventbusRabbitMQ:
|
|
|
40
40
|
rpc_server_auto_ack: set to True to ack messages before processing on rpc server connection
|
|
41
41
|
|
|
42
42
|
Returns:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Raises:
|
|
43
|
+
None: None
|
|
46
44
|
|
|
47
45
|
Examples:
|
|
48
46
|
>>> async_eventbus = AsyncEventbusRabbitMQ(
|
|
49
47
|
config, loop, rpc_client_publisher_confirms=True,
|
|
50
48
|
rpc_server_publisher_confirms=False, rpc_server_auto_ack=False)
|
|
51
49
|
### register subscribe
|
|
52
|
-
>>> def
|
|
50
|
+
>>> def subscribe_handler(body):
|
|
53
51
|
print(f"do something with: {body}")
|
|
54
|
-
>>>
|
|
55
|
-
>>> await eventbus.subscribe(subscribe_event, handler, "user.find")
|
|
52
|
+
>>> await eventbus.subscribe("rpc_exchange", "user.find", subscribe_handler)
|
|
56
53
|
### provide resource
|
|
57
|
-
>>> def
|
|
54
|
+
>>> def rpc_provider_handler(body) -> Union[str, bytes]:
|
|
58
55
|
print(f"do something with: {body}")
|
|
59
|
-
return "response"
|
|
60
|
-
>>> await eventbus.provide_resource("user.find2",
|
|
56
|
+
return b"response"
|
|
57
|
+
>>> await eventbus.provide_resource("user.find2", rpc_provider_handler)
|
|
61
58
|
"""
|
|
62
59
|
self._loop: Optional[AbstractEventLoop] = loop
|
|
63
60
|
self._signal = Signal()
|
|
61
|
+
self._used_connections: List[AsyncConnection] = []
|
|
64
62
|
self._pub_connection = AsyncConnection(
|
|
65
63
|
self._loop,
|
|
66
64
|
pub_publisher_confirms,
|
|
@@ -108,7 +106,7 @@ class AsyncEventbusRabbitMQ:
|
|
|
108
106
|
**kwargs
|
|
109
107
|
) -> bytes:
|
|
110
108
|
"""
|
|
111
|
-
Sends a publish message to queue of the bus and waits for a response
|
|
109
|
+
Sends a publish message to a queue of the bus and waits for a response
|
|
112
110
|
|
|
113
111
|
Args:
|
|
114
112
|
exchange: exchange name
|
|
@@ -125,14 +123,14 @@ class AsyncEventbusRabbitMQ:
|
|
|
125
123
|
|
|
126
124
|
Raises:
|
|
127
125
|
AutoReconnectException: when cannout reconnect on the gived timeout
|
|
128
|
-
PublishTimeoutException: if publish confirmation is
|
|
126
|
+
PublishTimeoutException: if publish confirmation is set to True and \
|
|
129
127
|
does not receive confirmation on the gived timeout
|
|
130
|
-
NackException: if publish confirmation is
|
|
128
|
+
NackException: if publish confirmation is set to True and receives a nack
|
|
131
129
|
ResponseTimeoutException: if response timeout is reached
|
|
132
130
|
RpcProviderException: if the rpc provider responded with an error
|
|
133
131
|
|
|
134
132
|
Examples:
|
|
135
|
-
>>> await eventbus.rpc_client("example.rpc", "user.find",
|
|
133
|
+
>>> await eventbus.rpc_client("example.rpc", "user.find", {"name": "example"}, "application/json")
|
|
136
134
|
"""
|
|
137
135
|
|
|
138
136
|
async def add_rpc_client():
|
|
@@ -165,10 +163,10 @@ class AsyncEventbusRabbitMQ:
|
|
|
165
163
|
**kwargs
|
|
166
164
|
) -> Optional[bool]:
|
|
167
165
|
"""
|
|
168
|
-
Sends a publish message to the bus following parameters passed
|
|
166
|
+
Sends a publish message to the bus following the parameters passed
|
|
169
167
|
|
|
170
168
|
Args:
|
|
171
|
-
|
|
169
|
+
exchange_name: exchange name
|
|
172
170
|
routing_key: routing key name
|
|
173
171
|
body: body that will be sent
|
|
174
172
|
content_type: content type of message
|
|
@@ -178,15 +176,14 @@ class AsyncEventbusRabbitMQ:
|
|
|
178
176
|
expiration: maximum lifetime of message to stay on the queue
|
|
179
177
|
|
|
180
178
|
Returns:
|
|
181
|
-
None: if publish confirmation is
|
|
182
|
-
True: if successful when publish confirmation is
|
|
179
|
+
None: if publish confirmation is set to False
|
|
180
|
+
True: if successful when publish confirmation is set to True
|
|
183
181
|
|
|
184
182
|
Raises:
|
|
185
183
|
AutoReconnectException: when cannout reconnect on the gived timeout
|
|
186
|
-
PublishTimeoutException: if publish confirmation is
|
|
184
|
+
PublishTimeoutException: if publish confirmation is set to True and \
|
|
187
185
|
does not receive confirmation on the gived timeout
|
|
188
|
-
NackException: if publish confirmation is
|
|
189
|
-
|
|
186
|
+
NackException: if publish confirmation is set to True and receives a nack
|
|
190
187
|
|
|
191
188
|
Examples:
|
|
192
189
|
>>> exchange_name = "example.rpc"
|
|
@@ -212,30 +209,27 @@ class AsyncEventbusRabbitMQ:
|
|
|
212
209
|
async def provide_resource(
|
|
213
210
|
self,
|
|
214
211
|
name: str,
|
|
215
|
-
handler: Callable[[
|
|
216
|
-
|
|
212
|
+
handler: Callable[[Any], Awaitable[Union[bytes, str]]],
|
|
213
|
+
timeout: Optional[int] = None,
|
|
217
214
|
connection_timeout: int = 16,
|
|
218
215
|
) -> None:
|
|
219
216
|
"""
|
|
220
|
-
Register a provider to listen on
|
|
217
|
+
Register a provider to listen on RPC request queue
|
|
221
218
|
|
|
222
219
|
Args:
|
|
223
220
|
name: routing_key name
|
|
224
221
|
handler: message handler, it will be called when a message is received
|
|
225
|
-
|
|
222
|
+
timeout: timeout in seconds for waiting for process the received message
|
|
226
223
|
connection_timeout: timeout for waiting for connection restabilishment
|
|
227
224
|
|
|
228
|
-
Returns:
|
|
229
|
-
None: None
|
|
230
|
-
|
|
231
225
|
Raises:
|
|
232
226
|
AutoReconnectException: when cannout reconnect on the gived timeout
|
|
233
227
|
|
|
234
228
|
Examples:
|
|
235
|
-
>>> async def
|
|
229
|
+
>>> async def handler(body) -> Union[bytes, str]:
|
|
236
230
|
print(f"received message: {body}")
|
|
237
231
|
return b"[]"
|
|
238
|
-
>>> await eventbus.provide_resource("user.find",
|
|
232
|
+
>>> await eventbus.provide_resource("user.find", handler)
|
|
239
233
|
"""
|
|
240
234
|
|
|
241
235
|
async def add_resource():
|
|
@@ -244,10 +238,11 @@ class AsyncEventbusRabbitMQ:
|
|
|
244
238
|
self.config.options.rpc_exchange_name,
|
|
245
239
|
name,
|
|
246
240
|
handler,
|
|
247
|
-
|
|
241
|
+
timeout,
|
|
248
242
|
)
|
|
249
243
|
|
|
250
|
-
self._rpc_server_connection.open(self.config.url)
|
|
244
|
+
if self._rpc_server_connection.open(self.config.url):
|
|
245
|
+
self._used_connections.append(self._rpc_server_connection)
|
|
251
246
|
await self._rpc_server_connection.add_callback(add_resource, connection_timeout)
|
|
252
247
|
|
|
253
248
|
async def subscribe(
|
|
@@ -255,7 +250,7 @@ class AsyncEventbusRabbitMQ:
|
|
|
255
250
|
exchange_name: str,
|
|
256
251
|
routing_key: str,
|
|
257
252
|
handler: Callable[[Any], Awaitable[None]],
|
|
258
|
-
|
|
253
|
+
timeout: Optional[float] = None,
|
|
259
254
|
connection_timeout: int = 16,
|
|
260
255
|
) -> None:
|
|
261
256
|
"""
|
|
@@ -265,12 +260,9 @@ class AsyncEventbusRabbitMQ:
|
|
|
265
260
|
exchange_name: exchange name
|
|
266
261
|
routing_key: routing_key name
|
|
267
262
|
handler: message handler, it will be called when a message is received
|
|
268
|
-
|
|
263
|
+
timeout: timeout in seconds for waiting for process the received message
|
|
269
264
|
connection_timeout: timeout for waiting for connection restabilishment
|
|
270
265
|
|
|
271
|
-
Returns:
|
|
272
|
-
None: None
|
|
273
|
-
|
|
274
266
|
Raises:
|
|
275
267
|
AutoReconnectException: when cannout reconnect on the gived timeout
|
|
276
268
|
|
|
@@ -279,9 +271,9 @@ class AsyncEventbusRabbitMQ:
|
|
|
279
271
|
print(f"received message: {body}")
|
|
280
272
|
>>> exchange_name = "example"
|
|
281
273
|
>>> routing_key = "user.find3"
|
|
282
|
-
>>>
|
|
274
|
+
>>> timeout = 20
|
|
283
275
|
>>> connection_timeout = 16
|
|
284
|
-
>>> await eventbus.subscribe(exchange_name, routing_key, handle,
|
|
276
|
+
>>> await eventbus.subscribe(exchange_name, routing_key, handle, timeout, connection_timeout)
|
|
285
277
|
"""
|
|
286
278
|
|
|
287
279
|
async def add_subscribe():
|
|
@@ -290,13 +282,45 @@ class AsyncEventbusRabbitMQ:
|
|
|
290
282
|
exchange_name,
|
|
291
283
|
routing_key,
|
|
292
284
|
handler,
|
|
293
|
-
|
|
285
|
+
timeout,
|
|
294
286
|
)
|
|
295
287
|
|
|
296
|
-
self._sub_connection.open(self.config.url)
|
|
288
|
+
if self._sub_connection.open(self.config.url):
|
|
289
|
+
self._used_connections.append(self._sub_connection)
|
|
297
290
|
await self._sub_connection.add_callback(add_subscribe, connection_timeout)
|
|
298
291
|
|
|
299
|
-
async def
|
|
292
|
+
async def restore(self) -> None:
|
|
293
|
+
"""
|
|
294
|
+
Restores connections and subscriptions after a disconnection.
|
|
295
|
+
|
|
296
|
+
This method attempts to reopen connections that were closed and
|
|
297
|
+
re-establishes any previously registered subscriptions and RPC providers.
|
|
298
|
+
|
|
299
|
+
Examples:
|
|
300
|
+
>>> # After a dispose, call restore to re-establish connections
|
|
301
|
+
>>> await eventbus.restore()
|
|
302
|
+
"""
|
|
303
|
+
for connection in self._used_connections:
|
|
304
|
+
if not connection.is_open:
|
|
305
|
+
connection.open(self.config.url)
|
|
306
|
+
|
|
307
|
+
async def dispose(self, stop_event_loop: bool = True) -> None:
|
|
308
|
+
"""
|
|
309
|
+
Closes all connections and optionally stops the event loop.
|
|
310
|
+
|
|
311
|
+
This method properly disposes of all resources used by the event bus,
|
|
312
|
+
including publisher, subscriber, RPC client, and RPC server connections.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
stop_event_loop: Whether to stop the event loop after closing connections.
|
|
316
|
+
|
|
317
|
+
Examples:
|
|
318
|
+
>>> # Close all connections and stop the event loop
|
|
319
|
+
>>> await eventbus.dispose()
|
|
320
|
+
|
|
321
|
+
>>> # Close all connections but keep the event loop running
|
|
322
|
+
>>> await eventbus.dispose(stop_event_loop=False)
|
|
323
|
+
"""
|
|
300
324
|
await self._pub_connection.close()
|
|
301
325
|
await self._sub_connection.close()
|
|
302
326
|
await self._rpc_client_connection.close()
|
|
@@ -127,7 +127,7 @@ class ChannelRabbitMQ:
|
|
|
127
127
|
self.rpc_publisher = False
|
|
128
128
|
self.rpc_consumer = False
|
|
129
129
|
if not self._stopping:
|
|
130
|
-
if not self._connection.is_open
|
|
130
|
+
if not self._connection.is_open:
|
|
131
131
|
if self._connection.ioloop_is_open:
|
|
132
132
|
self._connection.ioloop.call_later(5, self.reconnect)
|
|
133
133
|
# self._connection._connection.ioloop.call_later(5, self._connection.pause)
|
|
@@ -146,7 +146,7 @@ class ChannelRabbitMQ:
|
|
|
146
146
|
LOGGER.debug("Closing the channel")
|
|
147
147
|
self._channel.close()
|
|
148
148
|
|
|
149
|
-
def start_rpc_consumer(self):
|
|
149
|
+
def start_rpc_consumer(self, exchange, routing_key, message, content_type, correlation_id):
|
|
150
150
|
self.rpc_consumer = True
|
|
151
151
|
LOGGER.info("Starting rpc consumer")
|
|
152
152
|
|
|
@@ -162,6 +162,17 @@ class ChannelRabbitMQ:
|
|
|
162
162
|
consumer_tag=None,
|
|
163
163
|
)
|
|
164
164
|
|
|
165
|
+
self._channel.basic_publish(
|
|
166
|
+
exchange,
|
|
167
|
+
routing_key,
|
|
168
|
+
message,
|
|
169
|
+
properties=BasicProperties(
|
|
170
|
+
reply_to=self._callback_queue,
|
|
171
|
+
correlation_id=correlation_id,
|
|
172
|
+
content_type=content_type,
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
|
|
165
176
|
LOGGER.info(f"Declaring queue {self._callback_queue}")
|
|
166
177
|
self._channel_rpc.queue_declare(
|
|
167
178
|
queue=self._callback_queue,
|
|
@@ -189,18 +200,19 @@ class ChannelRabbitMQ:
|
|
|
189
200
|
last_id = str(uuid4())
|
|
190
201
|
self.futures[last_id] = future
|
|
191
202
|
self.response = None
|
|
192
|
-
self._channel.basic_publish(
|
|
193
|
-
exchange,
|
|
194
|
-
routing_key,
|
|
195
|
-
message,
|
|
196
|
-
properties=BasicProperties(
|
|
197
|
-
reply_to=self._callback_queue,
|
|
198
|
-
correlation_id=last_id,
|
|
199
|
-
content_type=content_type,
|
|
200
|
-
),
|
|
201
|
-
)
|
|
202
203
|
if not self.rpc_consumer:
|
|
203
|
-
self.start_rpc_consumer()
|
|
204
|
+
self.start_rpc_consumer(exchange, routing_key, message, content_type, last_id)
|
|
205
|
+
else:
|
|
206
|
+
self._channel.basic_publish(
|
|
207
|
+
exchange,
|
|
208
|
+
routing_key,
|
|
209
|
+
message,
|
|
210
|
+
properties=BasicProperties(
|
|
211
|
+
reply_to=self._callback_queue,
|
|
212
|
+
correlation_id=last_id,
|
|
213
|
+
content_type=content_type,
|
|
214
|
+
),
|
|
215
|
+
)
|
|
204
216
|
|
|
205
217
|
def prevent_infinite_loop(last_id):
|
|
206
218
|
if last_id in self.futures:
|
|
@@ -332,5 +344,5 @@ class ChannelRabbitMQ:
|
|
|
332
344
|
future.set_result(body)
|
|
333
345
|
|
|
334
346
|
def close(self):
|
|
335
|
-
if self.is_open
|
|
347
|
+
if self.is_open:
|
|
336
348
|
self._channel.close()
|
|
@@ -56,7 +56,7 @@ class ConnectionRabbitMQ:
|
|
|
56
56
|
|
|
57
57
|
def reconnect(self):
|
|
58
58
|
LOGGER.debug("reconnect %s", self.ioloop_is_open)
|
|
59
|
-
if not self.is_open
|
|
59
|
+
if not self.is_open:
|
|
60
60
|
self._connection = self._connectionFactory.create_connection(
|
|
61
61
|
self._uri,
|
|
62
62
|
self.on_connection_open,
|
|
@@ -119,7 +119,7 @@ class ConnectionRabbitMQ:
|
|
|
119
119
|
"durable": durable,
|
|
120
120
|
"callback": callback,
|
|
121
121
|
}
|
|
122
|
-
if self.is_open
|
|
122
|
+
if self.is_open:
|
|
123
123
|
self._channel.declare_exchange(
|
|
124
124
|
exchange=exchange,
|
|
125
125
|
durable=durable,
|
|
@@ -190,7 +190,7 @@ class ConnectionRabbitMQ:
|
|
|
190
190
|
self.ioloop.add_callback_threadsafe(channel_openned)
|
|
191
191
|
|
|
192
192
|
def channel_open(self, callback=lambda x: x):
|
|
193
|
-
if not self.is_open
|
|
193
|
+
if not self.is_open:
|
|
194
194
|
raise EventBusException("No connection open!")
|
|
195
195
|
if not self.channel_is_open():
|
|
196
196
|
self._channel.open(self, callback)
|
|
@@ -200,7 +200,7 @@ class ConnectionRabbitMQ:
|
|
|
200
200
|
return self._connection and self._connection.is_open
|
|
201
201
|
|
|
202
202
|
def channel_is_open(self):
|
|
203
|
-
return self._channel and self._channel.is_open
|
|
203
|
+
return self._channel and self._channel.is_open
|
|
204
204
|
|
|
205
205
|
def publish(self, exchange_name: str, routing_key: str, message: str):
|
|
206
206
|
if not self.ioloop_is_open:
|
|
@@ -271,5 +271,5 @@ class ConnectionRabbitMQ:
|
|
|
271
271
|
|
|
272
272
|
def close(self):
|
|
273
273
|
self._channel.close()
|
|
274
|
-
if self.is_open
|
|
274
|
+
if self.is_open:
|
|
275
275
|
self._connection.close()
|
|
@@ -185,13 +185,13 @@ class EventbusRabbitMQ:
|
|
|
185
185
|
self.event_loop.add_callback_threadsafe(rpc_server_setup)
|
|
186
186
|
|
|
187
187
|
def dispose(self, stop_event_loop=True):
|
|
188
|
-
if self.pub_connection.is_open
|
|
188
|
+
if self.pub_connection.is_open:
|
|
189
189
|
self.pub_connection.close()
|
|
190
|
-
if self.sub_connection.is_open
|
|
190
|
+
if self.sub_connection.is_open:
|
|
191
191
|
self.sub_connection.close()
|
|
192
|
-
if self.rpc_client_connection.is_open
|
|
192
|
+
if self.rpc_client_connection.is_open:
|
|
193
193
|
self.rpc_client_connection.close()
|
|
194
|
-
if self.rpc_server_connection.is_open
|
|
194
|
+
if self.rpc_server_connection.is_open:
|
|
195
195
|
self.rpc_server_connection.close()
|
|
196
196
|
if stop_event_loop:
|
|
197
197
|
self.event_loop.add_callback_threadsafe(self.event_loop.stop)
|
|
@@ -1,38 +1,41 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: amqp-client-python
|
|
3
|
-
Version: 0.2.0
|
|
3
|
+
Version: 0.2.0.post1
|
|
4
4
|
Summary: Python AMQP Client Library
|
|
5
|
-
|
|
5
|
+
Project-URL: Homepage, https://github.com/nutes-uepb/amqp-client-python
|
|
6
|
+
Project-URL: Repository, https://github.com/nutes-uepb/amqp-client-python
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/nutes-uepb/amqp-client-python/issues
|
|
8
|
+
Project-URL: Source, https://github.com/nutes-uepb/amqp-client-python
|
|
9
|
+
Author-email: NUTES UEPB <dev.seniorsaudemovel@gmail.com>
|
|
10
|
+
Maintainer-email: "João Pedro Miranda C. Hluchan" <berrytern@gmail.com>
|
|
6
11
|
License: Apache-2.0
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Author-email: dev.seniorsaudemovel@gmail.com
|
|
10
|
-
Requires-Python: >=3.7,<4.0
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: amqp-client-python,dependency,packaging
|
|
11
14
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
15
|
Classifier: Intended Audience :: Developers
|
|
13
16
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
17
|
Classifier: Natural Language :: English
|
|
15
18
|
Classifier: Operating System :: OS Independent
|
|
16
19
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
18
20
|
Classifier: Programming Language :: Python :: 3.8
|
|
19
21
|
Classifier: Programming Language :: Python :: 3.9
|
|
20
22
|
Classifier: Programming Language :: Python :: 3.10
|
|
21
23
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
24
|
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
27
|
Classifier: Topic :: Communications
|
|
24
28
|
Classifier: Topic :: Internet
|
|
25
29
|
Classifier: Topic :: Software Development :: Libraries
|
|
26
30
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
31
|
Classifier: Topic :: System :: Networking
|
|
28
|
-
Requires-
|
|
29
|
-
|
|
30
|
-
Project-URL: Repository, https://github.com/nutes-uepb/amqp-client-python
|
|
32
|
+
Requires-Python: <4.0,>=3.9
|
|
33
|
+
Requires-Dist: pika<2.0.0,>=1.3.0
|
|
31
34
|
Description-Content-Type: text/markdown
|
|
32
35
|
|
|
33
36
|
# AMQP Client Python
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
A Python client providing a high-level abstraction for interacting with RabbitMQ, simplifying message publishing, subscribing, and RPC patterns.
|
|
36
39
|
|
|
37
40
|
--------
|
|
38
41
|
|
|
@@ -53,6 +56,18 @@ Client with high level of abstraction for manipulation of messages in the event
|
|
|
53
56
|
|
|
54
57
|
**Source Code**: <a href="https://github.com/nutes-uepb/amqp-client-python" target="_blank">https://github.com/nutes-uepb/amqp-client-python</a>
|
|
55
58
|
|
|
59
|
+
**Discord Server**: <a href="https://discord.gg/RkXNeZpNZk" target="_blank">https://discord.gg/RkXNeZpNZk</a>
|
|
60
|
+
|
|
61
|
+
### 🦀 Looking for higher performance? Try `amqp-rs`!
|
|
62
|
+
If you need higher throughput, thread safety, and built-in compression, check out [**amqp-rs**](https://pypi.org/project/amqp-rs/).
|
|
63
|
+
|
|
64
|
+
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.
|
|
65
|
+
|
|
66
|
+
You can install it via `pip` or `uv`:
|
|
67
|
+
```bash
|
|
68
|
+
pip install amqp-rs
|
|
69
|
+
```
|
|
70
|
+
|
|
56
71
|
---
|
|
57
72
|
### Features:
|
|
58
73
|
- Automatic creation and management of queues, exchanges and channels;
|
|
@@ -69,6 +84,18 @@ Client with high level of abstraction for manipulation of messages in the event
|
|
|
69
84
|
| 0.2.0 | 0.2.0 |
|
|
70
85
|
| 0.1.14 | ~0.1.12 |
|
|
71
86
|
|
|
87
|
+
### Installation
|
|
88
|
+
|
|
89
|
+
You can install `amqp-client-python` using pip:
|
|
90
|
+
```bash
|
|
91
|
+
pip install amqp-client-python
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Prerequisites
|
|
95
|
+
|
|
96
|
+
* Python 3.7+
|
|
97
|
+
* A running RabbitMQ instance.
|
|
98
|
+
|
|
72
99
|
### Examples:
|
|
73
100
|
#### you can use [sync](https://github.com/nutes-uepb/amqp-client-python/blob/develop/amqp_client_python/rabbitmq/eventbus_rabbitmq.py) , [async eventbus](https://github.com/nutes-uepb/amqp-client-python/blob/develop/amqp_client_python/rabbitmq/async_eventbus_rabbitmq.py) and [sync wrapper](https://github.com/nutes-uepb/amqp-client-python/blob/develop/amqp_client_python/rabbitmq/eventbus_wrapper_rabbitmq.py) of async eventbus
|
|
74
101
|
<details><summary>async usage </summary>
|
|
@@ -229,4 +256,4 @@ When using [**EventbusRabbitMQ**](https://github.com/nutes-uepb/amqp-client-pyth
|
|
|
229
256
|
[known-vulnerabilities-image]: https://snyk.io/test/github/nutes-uepb/amqp-client-python/badge.svg?targetFile=requirements.txt
|
|
230
257
|
[known-vulnerabilities-url]: https://snyk.io/test/github/nutes-uepb/amqp-client-python?targetFile=requirements.txt
|
|
231
258
|
[releases-image]: https://img.shields.io/github/release-date/nutes-uepb/amqp-client-python.svg
|
|
232
|
-
[releases-url]: https://github.com/nutes-uepb/amqp-client-python/releases
|
|
259
|
+
[releases-url]: https://github.com/nutes-uepb/amqp-client-python/releases
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
amqp_client_python/__init__.py,sha256=6kv8G2KlcvbSVIE8Inxb3ugMNSTjg7KK9_HvyDprfb8,213
|
|
2
|
+
amqp_client_python/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
amqp_client_python/signals.py,sha256=6mW4oWBQnbJwuKox6ceeDbxTM8v9aCDEb0o2zyhpb5g,1263
|
|
2
4
|
amqp_client_python/domain/models/__init__.py,sha256=T57j9Sr5Z0pWyGT598CpAx_KhoV-MVpgHTvfESZAa90,92
|
|
3
|
-
amqp_client_python/domain/models/config.py,sha256=
|
|
4
|
-
amqp_client_python/domain/models/options.py,sha256=
|
|
5
|
-
amqp_client_python/domain/models/ssl_options.py,sha256=
|
|
5
|
+
amqp_client_python/domain/models/config.py,sha256=g2AWrlEvHCS1h2zlny29phREyG9GmD0rtGQHS3C7LVU,2268
|
|
6
|
+
amqp_client_python/domain/models/options.py,sha256=lx-x4uyMpyjelZjMuyUxLsORz1ro1vDjD33j0EGYD-o,2141
|
|
7
|
+
amqp_client_python/domain/models/ssl_options.py,sha256=xOTSIKfbNZbYepY6o3SxuZvLPX3J5pvRoJXIJlyBsrI,613
|
|
6
8
|
amqp_client_python/domain/utils/__init__.py,sha256=ppob9KPfzq9K3Fd2-RJr6CnwmPDOdgaAzPqOV0Q17P0,75
|
|
7
9
|
amqp_client_python/domain/utils/connection_type.py,sha256=AnEF8Nd3cQBCGX8rPKwEkKts8kwnGUAVr0nxnbpynLE,164
|
|
8
10
|
amqp_client_python/domain/utils/types.py,sha256=veT5lgZhUxophpmWItYaz56XxJTXGJ2L6J6oG_TbSWs,160
|
|
@@ -24,22 +26,20 @@ amqp_client_python/exceptions/response_timeout_exception.py,sha256=us_PiJPSk1pnz
|
|
|
24
26
|
amqp_client_python/exceptions/rpc_provider_exception.py,sha256=_1Ytb8BOUT_9xEtG_ibGx3cEMMM4kUVtduEVtXn-d6M,280
|
|
25
27
|
amqp_client_python/exceptions/thread_unsafe_exception.py,sha256=zcC4VyuxXWo66Tts1AQtkg07GK3OjXu3LEtt1AUj4jk,281
|
|
26
28
|
amqp_client_python/exceptions/timeout_exception.py,sha256=0YT-vivS44mHF4Jc3o2rlZFc6l_zzNjcxCHPS9ArgN0,271
|
|
27
|
-
amqp_client_python/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
29
|
amqp_client_python/rabbitmq/__init__.py,sha256=ZFaBSr8TDzZlHwaT344ao20W2FD5c3RI2sa5kiEeKeI,630
|
|
29
|
-
amqp_client_python/rabbitmq/async_channel.py,sha256=
|
|
30
|
+
amqp_client_python/rabbitmq/async_channel.py,sha256=OtW0agoSMG-v38lKJsFNRMaFoqubEauQCmLaLN7bQek,26667
|
|
30
31
|
amqp_client_python/rabbitmq/async_channel_factory.py,sha256=ec_5B3Sgazi_ncIG5lI5nnmkH3NprwvyjMhvgpG1BWA,371
|
|
31
|
-
amqp_client_python/rabbitmq/async_connection.py,sha256=
|
|
32
|
+
amqp_client_python/rabbitmq/async_connection.py,sha256=lWArTU5lXKMrKnnTivhrWs8c9PJnUNMAD5_QI3FZu-s,16906
|
|
32
33
|
amqp_client_python/rabbitmq/async_connection_factory.py,sha256=3N5MM1fPUdodAFLh58rf6vvwN9OAclDVzmG85Qve-Ss,849
|
|
33
|
-
amqp_client_python/rabbitmq/async_eventbus_rabbitmq.py,sha256=
|
|
34
|
+
amqp_client_python/rabbitmq/async_eventbus_rabbitmq.py,sha256=4PCtqhVNT3cq-d0anfdKPzhmmETD-HMqIu-MVuZmZZA,12950
|
|
34
35
|
amqp_client_python/rabbitmq/channel_factory_rabbitmq.py,sha256=h_vGqHgL0DOtXC0tmVd5gzXVlNKL48rxpKpwaN3AlOM,310
|
|
35
|
-
amqp_client_python/rabbitmq/channel_rabbitmq.py,sha256=
|
|
36
|
+
amqp_client_python/rabbitmq/channel_rabbitmq.py,sha256=S3SqGCfVRoIuPyZW40bQZBpWEgbe5YlvZ8ei23a2VVw,13737
|
|
36
37
|
amqp_client_python/rabbitmq/connection_factory_rabbitmq.py,sha256=EKPG5gEsGwtpSKOhoPI5vK1sJOUARbOflOug6VREIrk,1022
|
|
37
|
-
amqp_client_python/rabbitmq/connection_rabbitmq.py,sha256=
|
|
38
|
-
amqp_client_python/rabbitmq/eventbus_rabbitmq.py,sha256=
|
|
38
|
+
amqp_client_python/rabbitmq/connection_rabbitmq.py,sha256=9A8NAPAB3i-5E43SV5sxjKFFoRb4pIhzAAUHrvOvOng,9772
|
|
39
|
+
amqp_client_python/rabbitmq/eventbus_rabbitmq.py,sha256=GZoJGVeBkMTPrQ5dIJqad-MdGmuuJsxGaXV1YGLdk5o,6980
|
|
39
40
|
amqp_client_python/rabbitmq/eventbus_wrapper_rabbitmq.py,sha256=i3C-sZO9EifanfRXReiib56fxxzjV3Cx71rdbJORg78,7442
|
|
40
41
|
amqp_client_python/rabbitmq/ioloop_factory.py,sha256=vfgndKLcazQU0a4elI5RNHnQCTEua--Jjo99bzX8iHo,856
|
|
41
|
-
amqp_client_python/
|
|
42
|
-
amqp_client_python-0.2.0.dist-info/
|
|
43
|
-
amqp_client_python-0.2.0.dist-info/
|
|
44
|
-
amqp_client_python-0.2.0.dist-info/
|
|
45
|
-
amqp_client_python-0.2.0.dist-info/RECORD,,
|
|
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,,
|
{amqp_client_python-0.2.0.dist-info → amqp_client_python-0.2.0.post1.dist-info/licenses}/LICENSE
RENAMED
|
File without changes
|