rabbitmq-amqp-python-client 0.3.0__tar.gz → 0.4.1__tar.gz

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.

Potentially problematic release.


This version of rabbitmq-amqp-python-client might be problematic. Click here for more details.

Files changed (51) hide show
  1. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/PKG-INFO +2 -2
  2. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/pyproject.toml +3 -3
  3. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/__init__.py +2 -0
  4. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/connection.py +6 -6
  5. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/consumer.py +59 -26
  6. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/entities.py +37 -4
  7. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/options.py +24 -3
  8. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/publisher.py +1 -1
  9. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_data.py +1 -0
  10. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_utils.py +14 -0
  11. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/LICENSE +0 -0
  12. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/README.md +0 -0
  13. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/address_helper.py +0 -0
  14. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/amqp_consumer_handler.py +0 -0
  15. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/asyncio/__init__.py +0 -0
  16. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/asyncio/connection.py +0 -0
  17. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/asyncio/consumer.py +0 -0
  18. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/asyncio/environment.py +0 -0
  19. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/asyncio/management.py +0 -0
  20. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/asyncio/publisher.py +0 -0
  21. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/common.py +0 -0
  22. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/delivery_context.py +0 -0
  23. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/environment.py +0 -0
  24. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/exceptions.py +0 -0
  25. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/management.py +0 -0
  26. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/py.typed +0 -0
  27. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/__init__.py +0 -0
  28. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_common.py +0 -0
  29. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_condition.py +0 -0
  30. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_delivery.py +0 -0
  31. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_endpoints.py +0 -0
  32. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_events.py +0 -0
  33. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_exceptions.py +0 -0
  34. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_handler.py +0 -0
  35. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_handlers.py +0 -0
  36. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_io.py +0 -0
  37. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_message.py +0 -0
  38. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_reactor.py +0 -0
  39. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_selectable.py +0 -0
  40. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_tracing.py +0 -0
  41. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_transport.py +0 -0
  42. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_url.py +0 -0
  43. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/_wrapper.py +0 -0
  44. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/handlers.py +0 -0
  45. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/py.typed +0 -0
  46. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/reactor.py +0 -0
  47. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/tracing.py +0 -0
  48. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/qpid/proton/utils.py +0 -0
  49. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/queues.py +0 -0
  50. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/ssl_configuration.py +0 -0
  51. {rabbitmq_amqp_python_client-0.3.0 → rabbitmq_amqp_python_client-0.4.1}/rabbitmq_amqp_python_client/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rabbitmq-amqp-python-client
3
- Version: 0.3.0
3
+ Version: 0.4.1
4
4
  Summary: Python RabbitMQ client for AMQP 1.0 protocol
5
5
  License: Apache-2.0 license
6
6
  License-File: LICENSE
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Programming Language :: Python :: 3.13
16
16
  Classifier: Programming Language :: Python :: 3.14
17
17
  Requires-Dist: packaging (>=23.0,<24.0)
18
- Requires-Dist: python-qpid-proton (>=0.39.0,<0.40.0)
18
+ Requires-Dist: python-qpid-proton (>=0.40.0,<0.41.0)
19
19
  Requires-Dist: typing-extensions (>=4.13.0,<5.0.0)
20
20
  Description-Content-Type: text/markdown
21
21
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "rabbitmq-amqp-python-client"
3
- version = "0.3.0"
3
+ version = "0.4.1"
4
4
  description = "Python RabbitMQ client for AMQP 1.0 protocol"
5
5
  authors = ["RabbitMQ team"]
6
6
  license = "Apache-2.0 license"
@@ -8,7 +8,7 @@ readme = "README.md"
8
8
 
9
9
  [tool.poetry.dependencies]
10
10
  python = "^3.9"
11
- python-qpid-proton = "^0.39.0"
11
+ python-qpid-proton = "^0.40.0"
12
12
  typing-extensions = "^4.13.0"
13
13
  packaging = "^23.0"
14
14
 
@@ -21,7 +21,7 @@ isort = "^5.9.3"
21
21
  mypy = "^0.910"
22
22
  pytest = "^8.3.4"
23
23
  black = "^24.3.0"
24
- python-qpid-proton = "^0.39.0"
24
+ python-qpid-proton = "^0.40.0"
25
25
  requests = "^2.31.0"
26
26
  pytest-asyncio = "^1.2.0"
27
27
 
@@ -14,6 +14,7 @@ from .connection import Connection
14
14
  from .consumer import Consumer
15
15
  from .entities import (
16
16
  ConsumerOptions,
17
+ DirectReplyToConsumerOptions,
17
18
  ExchangeCustomSpecification,
18
19
  ExchangeSpecification,
19
20
  ExchangeToExchangeBindingSpecification,
@@ -72,6 +73,7 @@ __all__ = [
72
73
  "QuorumQueueSpecification",
73
74
  "ClassicQueueSpecification",
74
75
  "StreamSpecification",
76
+ "DirectReplyToConsumerOptions",
75
77
  "ExchangeToQueueBindingSpecification",
76
78
  "ExchangeToExchangeBindingSpecification",
77
79
  "QueueType",
@@ -379,7 +379,7 @@ class Connection:
379
379
 
380
380
  def consumer(
381
381
  self,
382
- destination: str,
382
+ destination: Optional[str] = None,
383
383
  message_handler: Optional[MessagingHandler] = None,
384
384
  consumer_options: Optional[ConsumerOptions] = None,
385
385
  credit: Optional[int] = None,
@@ -388,7 +388,7 @@ class Connection:
388
388
  Create a new consumer instance.
389
389
 
390
390
  Args:
391
- destination: The address to consume from
391
+ destination: Optional The address to consume from
392
392
  message_handler: Optional handler for processing messages
393
393
  consumer_options: Optional configuration for queue consumption. Each queue has its own consumer options.
394
394
  credit: Optional credit value for flow control
@@ -397,9 +397,11 @@ class Connection:
397
397
  Consumer: A new consumer instance
398
398
 
399
399
  Raises:
400
- ArgumentOutOfRangeException: If destination address format is invalid
400
+ ArgumentOutOfRangeException: If destination address format is invalid.
401
+ Only applies if not using Direct Reply-to.
402
+ The server will provide the queue name in that case.
401
403
  """
402
- if not validate_address(destination):
404
+ if destination is not None and not validate_address(destination):
403
405
  raise ArgumentOutOfRangeException(
404
406
  "destination address must start with /queues or /exchanges"
405
407
  )
@@ -438,9 +440,7 @@ class Connection:
438
440
  time.sleep(delay.total_seconds())
439
441
 
440
442
  try:
441
-
442
443
  self._open_connections(reconnect_handlers=True)
443
-
444
444
  self._connections.append(self)
445
445
 
446
446
  except ConnectionException as e:
@@ -2,7 +2,11 @@ import logging
2
2
  from typing import Literal, Optional, Union, cast
3
3
 
4
4
  from .amqp_consumer_handler import AMQPMessagingHandler
5
- from .entities import ConsumerOptions
5
+ from .entities import (
6
+ ConsumerOptions,
7
+ DirectReplyToConsumerOptions,
8
+ StreamConsumerOptions,
9
+ )
6
10
  from .options import (
7
11
  ReceiverOptionUnsettled,
8
12
  ReceiverOptionUnsettledWithFilters,
@@ -29,16 +33,16 @@ class Consumer:
29
33
  _conn (BlockingConnection): The underlying connection to RabbitMQ
30
34
  _addr (str): The address to consume from
31
35
  _handler (Optional[MessagingHandler]): Optional message handling callback
32
- _stream_options (Optional[StreamConsumerOptions]): Configuration for stream consumption
36
+ _consumer_options (Optional[StreamConsumerOptions]): Configuration for stream consumption
33
37
  _credit (Optional[int]): Flow control credit value
34
38
  """
35
39
 
36
40
  def __init__(
37
41
  self,
38
42
  conn: BlockingConnection,
39
- addr: str,
43
+ addr: Optional[str] = None,
40
44
  handler: Optional[AMQPMessagingHandler] = None,
41
- stream_options: Optional[ConsumerOptions] = None,
45
+ consumer_options: Optional[ConsumerOptions] = None,
42
46
  credit: Optional[int] = None,
43
47
  ):
44
48
  """
@@ -48,14 +52,14 @@ class Consumer:
48
52
  conn: The blocking connection to use for consuming
49
53
  addr: The address to consume from
50
54
  handler: Optional message handler for processing received messages
51
- stream_options: Optional configuration for stream-based consumption
55
+ consumer_options: Optional configuration for stream-based consumption
52
56
  credit: Optional credit value for flow control
53
57
  """
54
58
  self._receiver: Optional[BlockingReceiver] = None
55
59
  self._conn = conn
56
60
  self._addr = addr
57
61
  self._handler = handler
58
- self._stream_options = stream_options
62
+ self._consumer_options = consumer_options
59
63
  self._credit = credit
60
64
  self._consumers: list[Consumer] = []
61
65
  self._open()
@@ -66,21 +70,25 @@ class Consumer:
66
70
  self._receiver = self._create_receiver(self._addr)
67
71
 
68
72
  def _update_connection(self, conn: BlockingConnection) -> None:
73
+ addr = ""
74
+ if self._addr is not None:
75
+ addr = self._addr
76
+
69
77
  self._conn = conn
70
- if self._stream_options is None:
78
+ if self._consumer_options is None:
71
79
  logger.debug("creating new receiver without stream")
72
80
  self._receiver = self._conn.create_receiver(
73
- self._addr,
74
- options=ReceiverOptionUnsettled(self._addr),
81
+ addr,
82
+ options=ReceiverOptionUnsettled(addr),
75
83
  handler=self._handler,
76
84
  )
77
85
  else:
78
86
  logger.debug("creating new stream receiver")
79
- self._stream_options.offset(self._handler.offset - 1) # type: ignore
87
+ self._consumer_options.offset(self._handler.offset - 1) # type: ignore
80
88
  self._receiver = self._conn.create_receiver(
81
- self._addr,
89
+ addr,
82
90
  options=ReceiverOptionUnsettledWithFilters(
83
- self._addr, self._stream_options
91
+ addr, self._consumer_options
84
92
  ),
85
93
  handler=self._handler,
86
94
  )
@@ -142,29 +150,54 @@ class Consumer:
142
150
  self._receiver.container.stop_events()
143
151
  self._receiver.container.stop()
144
152
 
145
- def _create_receiver(self, addr: str) -> BlockingReceiver:
146
- logger.debug("Creating the receiver")
147
- if self._stream_options is None:
148
- receiver = self._conn.create_receiver(
149
- addr, options=ReceiverOptionUnsettled(addr), handler=self._handler
150
- )
153
+ def _create_receiver(self, addr: Optional[str] = None) -> BlockingReceiver:
154
+ credit = 10
155
+ if self._credit is not None:
156
+ credit = self._credit
151
157
 
158
+ if self._consumer_options is not None:
159
+ logger.debug(
160
+ "Creating the receiver, with options: %s",
161
+ type(self._consumer_options).__name__,
162
+ )
152
163
  else:
153
- receiver = self._conn.create_receiver(
164
+ logger.debug("Creating the receiver, without options")
165
+
166
+ if self._consumer_options is None:
167
+ return self._conn.create_receiver(
154
168
  addr,
155
- options=ReceiverOptionUnsettledWithFilters(addr, self._stream_options),
169
+ options=ReceiverOptionUnsettled(addr),
156
170
  handler=self._handler,
171
+ credit=credit,
157
172
  )
158
173
 
159
- if self._credit is not None:
160
- receiver.credit = self._credit
174
+ if isinstance(self._consumer_options, DirectReplyToConsumerOptions):
175
+ logger.debug("Creating dynamic receiver for direct reply-to")
176
+ dynamic_receiver = self._conn.create_dynamic_receiver(
177
+ credit, handler=self._handler
178
+ )
179
+ dynamic_receiver.credit = credit
180
+ return dynamic_receiver
161
181
 
162
- return receiver
182
+ if isinstance(self._consumer_options, StreamConsumerOptions):
183
+ return self._conn.create_receiver(
184
+ addr,
185
+ options=ReceiverOptionUnsettledWithFilters(
186
+ addr, self._consumer_options
187
+ ),
188
+ handler=self._handler,
189
+ )
190
+
191
+ raise Exception(
192
+ "Receiver is not initialized. No valid consumer options provided."
193
+ )
163
194
 
164
195
  @property
165
- def address(self) -> str:
166
- """Get the current publisher address."""
167
- return self._addr
196
+ def address(self) -> Optional[str]:
197
+ if self._receiver is not None:
198
+ return cast(Optional[str], self._receiver.link.remote_source.address)
199
+ else:
200
+ raise Exception("Receiver is not initialized")
168
201
 
169
202
  @property
170
203
  def handler(self) -> Optional[AMQPMessagingHandler]:
@@ -5,7 +5,7 @@ from typing import Any, Dict, Optional, Union
5
5
 
6
6
  from .common import ExchangeType, QueueType
7
7
  from .exceptions import ValidationCodeException
8
- from .qpid.proton._data import Described, symbol
8
+ from .qpid.proton._data import Described, symbol, timestamp
9
9
 
10
10
  SQL_FILTER = "sql-filter"
11
11
  AMQP_SQL_FILTER = "amqp:sql-filter"
@@ -109,11 +109,13 @@ class OffsetSpecification(Enum):
109
109
  first: Start from the first message in the stream
110
110
  next: Start from the next message to arrive
111
111
  last: Start from the last message in the stream
112
+ timestamp: Start from a specific timestamp
112
113
  """
113
114
 
114
115
  first = ("first",)
115
116
  next = ("next",)
116
117
  last = ("last",)
118
+ timestamp = ("timestamp",)
117
119
 
118
120
 
119
121
  @dataclass
@@ -160,6 +162,9 @@ class ConsumerOptions:
160
162
  def filter_set(self) -> Dict[symbol, Described]:
161
163
  raise NotImplementedError("Subclasses should implement this method")
162
164
 
165
+ def direct_reply_to(self) -> bool:
166
+ return False
167
+
163
168
 
164
169
  @dataclass
165
170
  class MessageProperties:
@@ -240,7 +245,9 @@ class StreamConsumerOptions(ConsumerOptions):
240
245
 
241
246
  def __init__(
242
247
  self,
243
- offset_specification: Optional[Union[OffsetSpecification, int]] = None,
248
+ offset_specification: Optional[
249
+ Union[OffsetSpecification, int, datetime]
250
+ ] = None,
244
251
  filter_options: Optional[StreamFilterOptions] = None,
245
252
  ):
246
253
 
@@ -272,7 +279,9 @@ class StreamConsumerOptions(ConsumerOptions):
272
279
  if filter_options.sql is not None and filter_options.sql != "":
273
280
  self._filter_sql(filter_options.sql)
274
281
 
275
- def _offset(self, offset_specification: Union[OffsetSpecification, int]) -> None:
282
+ def _offset(
283
+ self, offset_specification: Union[OffsetSpecification, int, datetime]
284
+ ) -> None:
276
285
  """
277
286
  Set the offset specification for the stream.
278
287
 
@@ -284,11 +293,20 @@ class StreamConsumerOptions(ConsumerOptions):
284
293
  self._filter_set[symbol(STREAM_OFFSET_SPEC)] = Described(
285
294
  symbol(STREAM_OFFSET_SPEC), offset_specification
286
295
  )
287
- else:
296
+ if isinstance(offset_specification, OffsetSpecification):
288
297
  self._filter_set[symbol(STREAM_OFFSET_SPEC)] = Described(
289
298
  symbol(STREAM_OFFSET_SPEC), offset_specification.name
290
299
  )
291
300
 
301
+ if isinstance(offset_specification, datetime):
302
+ # convert datetime to milliseconds since epoch
303
+ milliseconds_since_epoch = int(offset_specification.timestamp() * 1000)
304
+ # Create a timestamp instance
305
+ ts = timestamp(milliseconds_since_epoch)
306
+ self._filter_set[symbol(STREAM_OFFSET_SPEC)] = Described(
307
+ symbol(STREAM_OFFSET_SPEC), ts
308
+ )
309
+
292
310
  def _filter_values(self, filters: list[str]) -> None:
293
311
  """
294
312
  Set the filter values for the stream.
@@ -400,6 +418,21 @@ class StreamConsumerOptions(ConsumerOptions):
400
418
  )
401
419
 
402
420
 
421
+ class DirectReplyToConsumerOptions(ConsumerOptions):
422
+
423
+ def validate(self, versions: Dict[str, bool]) -> None:
424
+ if not versions.get("4.2.0", False):
425
+ raise ValidationCodeException(
426
+ "Direct Reply-To requires RabbitMQ 4.2.0 or higher"
427
+ )
428
+
429
+ def filter_set(self) -> Dict[symbol, Described]:
430
+ return {}
431
+
432
+ def direct_reply_to(self) -> bool:
433
+ return True
434
+
435
+
403
436
  @dataclass
404
437
  class RecoveryConfiguration:
405
438
  """
@@ -1,9 +1,15 @@
1
+ from typing import Optional
2
+
1
3
  from .entities import ConsumerOptions
2
4
  from .qpid.proton._data import ( # noqa: E402
5
+ Data,
3
6
  PropertyDict,
4
7
  symbol,
5
8
  )
6
- from .qpid.proton._endpoints import Link # noqa: E402
9
+ from .qpid.proton._endpoints import ( # noqa: E402
10
+ Link,
11
+ Terminus,
12
+ )
7
13
  from .qpid.proton.reactor import ( # noqa: E402
8
14
  Filter,
9
15
  LinkOption,
@@ -52,8 +58,23 @@ class ReceiverOption(LinkOption): # type: ignore
52
58
  link.source.dynamic = False
53
59
 
54
60
 
61
+ class DynamicReceiverOption(LinkOption): # type: ignore
62
+
63
+ def apply(self, link: Link) -> None:
64
+ link.snd_settle_mode = Link.SND_SETTLED
65
+ # link.rcv_settle_mode = Link.RCV_FIRST
66
+ link.source.expiry_policy = Terminus.EXPIRE_WITH_LINK
67
+ link.properties = PropertyDict({symbol("paired"): True})
68
+ link.source.dynamic = True
69
+ data = link.source.capabilities
70
+ data.put_array(False, Data.SYMBOL)
71
+ data.enter()
72
+ data.put_string("rabbitmq:volatile-queue")
73
+ data.exit()
74
+
75
+
55
76
  class ReceiverOptionUnsettled(LinkOption): # type: ignore
56
- def __init__(self, addr: str):
77
+ def __init__(self, addr: Optional[str]):
57
78
  self._addr = addr
58
79
 
59
80
  def apply(self, link: Link) -> None:
@@ -68,7 +89,7 @@ class ReceiverOptionUnsettled(LinkOption): # type: ignore
68
89
 
69
90
 
70
91
  class ReceiverOptionUnsettledWithFilters(Filter): # type: ignore
71
- def __init__(self, addr: str, consumer_options: ConsumerOptions):
92
+ def __init__(self, addr: Optional[str], consumer_options: ConsumerOptions):
72
93
  super().__init__(consumer_options.filter_set())
73
94
  self._addr = addr
74
95
 
@@ -94,7 +94,7 @@ class Publisher:
94
94
  return self._sender.send(message)
95
95
  else:
96
96
  if message.address != "":
97
- if validate_address(message.address) is False:
97
+ if not validate_address(message.address):
98
98
  raise ArgumentOutOfRangeException(
99
99
  "destination address must start with /queues or /exchanges"
100
100
  )
@@ -18,6 +18,7 @@
18
18
  #
19
19
 
20
20
  import uuid
21
+ from datetime import datetime
21
22
  from typing import (
22
23
  Any,
23
24
  Callable,
@@ -44,6 +44,7 @@ from ._handlers import (
44
44
  )
45
45
  from ._reactor import Container
46
46
  from ._url import Url
47
+ from ...options import DynamicReceiverOption
47
48
 
48
49
  try:
49
50
  from typing import Literal
@@ -505,6 +506,17 @@ class BlockingConnection(Handler):
505
506
  ),
506
507
  )
507
508
 
509
+ def create_dynamic_receiver(
510
+ self, credit: Optional[int] = None, handler: Optional[Handler] = None
511
+ ):
512
+ return self.create_receiver(
513
+ credit=credit,
514
+ dynamic=True,
515
+ options=DynamicReceiverOption(),
516
+ handler=handler,
517
+ name="dynamic-receiver_" + str(id(self)),
518
+ )
519
+
508
520
  def create_receiver(
509
521
  self,
510
522
  address: Optional[str] = None,
@@ -514,6 +526,8 @@ class BlockingConnection(Handler):
514
526
  name: Optional[str] = None,
515
527
  options: Optional[
516
528
  Union[
529
+ "DynamicReceiverOption",
530
+ List["DynamicReceiverOption"],
517
531
  "ReceiverOption",
518
532
  List["ReceiverOption"],
519
533
  "LinkOption",