rabbitmq-amqp-python-client 0.4.0__tar.gz → 0.5.0__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 (52) hide show
  1. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/PKG-INFO +1 -1
  2. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/pyproject.toml +1 -1
  3. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/__init__.py +6 -2
  4. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/amqp_consumer_handler.py +6 -3
  5. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/asyncio/connection.py +2 -2
  6. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/asyncio/consumer.py +16 -4
  7. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/connection.py +2 -2
  8. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/consumer.py +71 -13
  9. rabbitmq_amqp_python_client-0.5.0/rabbitmq_amqp_python_client/delivery_context.py +219 -0
  10. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/entities.py +118 -19
  11. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/exceptions.py +9 -0
  12. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/options.py +25 -2
  13. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_data.py +1 -0
  14. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_reactor.py +1 -1
  15. rabbitmq_amqp_python_client-0.4.0/rabbitmq_amqp_python_client/delivery_context.py +0 -114
  16. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/LICENSE +0 -0
  17. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/README.md +0 -0
  18. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/address_helper.py +0 -0
  19. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/asyncio/__init__.py +0 -0
  20. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/asyncio/environment.py +0 -0
  21. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/asyncio/management.py +0 -0
  22. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/asyncio/publisher.py +0 -0
  23. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/common.py +0 -0
  24. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/environment.py +0 -0
  25. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/management.py +0 -0
  26. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/publisher.py +0 -0
  27. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/py.typed +0 -0
  28. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/__init__.py +0 -0
  29. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_common.py +0 -0
  30. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_condition.py +0 -0
  31. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_delivery.py +0 -0
  32. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_endpoints.py +0 -0
  33. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_events.py +0 -0
  34. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_exceptions.py +0 -0
  35. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_handler.py +0 -0
  36. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_handlers.py +0 -0
  37. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_io.py +0 -0
  38. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_message.py +0 -0
  39. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_selectable.py +0 -0
  40. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_tracing.py +0 -0
  41. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_transport.py +0 -0
  42. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_url.py +0 -0
  43. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_utils.py +0 -0
  44. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/_wrapper.py +0 -0
  45. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/handlers.py +0 -0
  46. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/py.typed +0 -0
  47. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/reactor.py +0 -0
  48. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/tracing.py +0 -0
  49. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/qpid/proton/utils.py +0 -0
  50. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/queues.py +0 -0
  51. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/rabbitmq_amqp_python_client/ssl_configuration.py +0 -0
  52. {rabbitmq_amqp_python_client-0.4.0 → rabbitmq_amqp_python_client-0.5.0}/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.4.0
3
+ Version: 0.5.0
4
4
  Summary: Python RabbitMQ client for AMQP 1.0 protocol
5
5
  License: Apache-2.0 license
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "rabbitmq-amqp-python-client"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "Python RabbitMQ client for AMQP 1.0 protocol"
5
5
  authors = ["RabbitMQ team"]
6
6
  license = "Apache-2.0 license"
@@ -13,8 +13,9 @@ from .common import ExchangeType, QueueType
13
13
  from .connection import Connection
14
14
  from .consumer import Consumer
15
15
  from .entities import (
16
+ AbcConsumerOptions,
16
17
  ConsumerOptions,
17
- DirectReplyToConsumerOptions,
18
+ ConsumerSettleStrategy,
18
19
  ExchangeCustomSpecification,
19
20
  ExchangeSpecification,
20
21
  ExchangeToExchangeBindingSpecification,
@@ -29,6 +30,7 @@ from .entities import (
29
30
  from .environment import Environment
30
31
  from .exceptions import (
31
32
  ArgumentOutOfRangeException,
33
+ InvalidOperationException,
32
34
  ValidationCodeException,
33
35
  )
34
36
  from .management import Management
@@ -72,8 +74,8 @@ __all__ = [
72
74
  "ExchangeSpecification",
73
75
  "QuorumQueueSpecification",
74
76
  "ClassicQueueSpecification",
77
+ "ConsumerSettleStrategy",
75
78
  "StreamSpecification",
76
- "DirectReplyToConsumerOptions",
77
79
  "ExchangeToQueueBindingSpecification",
78
80
  "ExchangeToExchangeBindingSpecification",
79
81
  "QueueType",
@@ -99,6 +101,7 @@ __all__ = [
99
101
  "ConnectionClosed",
100
102
  "StreamConsumerOptions",
101
103
  "StreamFilterOptions",
104
+ "AbcConsumerOptions",
102
105
  "ConsumerOptions",
103
106
  "MessageProperties",
104
107
  "OffsetSpecification",
@@ -113,4 +116,5 @@ __all__ = [
113
116
  "AsyncPublisher",
114
117
  "AsyncManagement",
115
118
  "AsyncEnvironment",
119
+ "InvalidOperationException",
116
120
  ]
@@ -1,11 +1,14 @@
1
- from .delivery_context import DeliveryContext
1
+ from .delivery_context import (
2
+ AbcDeliveryContext,
3
+ DeliveryContext,
4
+ )
2
5
  from .qpid.proton._events import Event
3
6
  from .qpid.proton.handlers import MessagingHandler
4
7
 
5
8
  """
6
9
  AMQPMessagingHandler extends the QPID MessagingHandler.
7
10
  It is an helper to set the default values needed for manually accepting and settling messages.
8
- self.delivery_context is an instance of DeliveryContext, which is used to accept, reject,
11
+ self.delivery_context is an instance of IDeliveryContext (defaults to DeliveryContext), which is used to accept, reject,
9
12
  requeue or requeue with annotations a message.
10
13
  It is not mandatory to use this class, but it is a good practice to use it.
11
14
  """
@@ -20,7 +23,7 @@ class AMQPMessagingHandler(MessagingHandler): # type: ignore
20
23
  different methods of the delivery_context what to do with the message
21
24
  """
22
25
  super().__init__(auto_accept=auto_accept, auto_settle=auto_settle)
23
- self.delivery_context: DeliveryContext = DeliveryContext()
26
+ self.delivery_context: AbcDeliveryContext = DeliveryContext()
24
27
  self._offset = 0
25
28
 
26
29
  def on_amqp_message(self, event: Event) -> None:
@@ -8,7 +8,7 @@ from ..address_helper import validate_address
8
8
  from ..connection import Connection
9
9
  from ..consumer import Consumer
10
10
  from ..entities import (
11
- ConsumerOptions,
11
+ AbcConsumerOptions,
12
12
  OAuth2Options,
13
13
  RecoveryConfiguration,
14
14
  )
@@ -228,7 +228,7 @@ class AsyncConnection:
228
228
  self,
229
229
  destination: str,
230
230
  message_handler: Optional[MessagingHandler] = None,
231
- consumer_options: Optional[ConsumerOptions] = None,
231
+ consumer_options: Optional[AbcConsumerOptions] = None,
232
232
  credit: Optional[int] = None,
233
233
  ) -> AsyncConsumer:
234
234
  """
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import logging
5
5
  import threading
6
+ import warnings
6
7
  from types import TracebackType
7
8
  from typing import (
8
9
  Any,
@@ -15,7 +16,7 @@ from typing import (
15
16
 
16
17
  from ..amqp_consumer_handler import AMQPMessagingHandler
17
18
  from ..consumer import Consumer
18
- from ..entities import ConsumerOptions
19
+ from ..entities import AbcConsumerOptions
19
20
  from ..qpid.proton._exceptions import Timeout
20
21
  from ..qpid.proton._message import Message
21
22
  from ..qpid.proton.utils import BlockingConnection
@@ -39,7 +40,7 @@ class AsyncConsumer:
39
40
  _conn (BlockingConnection): The shared blocking connection
40
41
  _addr (str): The address to consume from
41
42
  _handler (Optional[AMQPMessagingHandler]): Optional message handling callback
42
- _stream_options (Optional[ConsumerOptions]): Configuration for stream consumption
43
+ _stream_options (Optional[AbcConsumerOptions]): Configuration for stream consumption
43
44
  _credit (Optional[int]): Flow control credit value
44
45
  _connection_lock (asyncio.Lock): Lock for coordinating access to the shared connection
45
46
  _remove_callback (Optional[Callable[[AsyncConsumer], None]]): Callback on close
@@ -56,7 +57,7 @@ class AsyncConsumer:
56
57
  conn: BlockingConnection,
57
58
  addr: str,
58
59
  handler: Optional[AMQPMessagingHandler] = None,
59
- stream_options: Optional[ConsumerOptions] = None,
60
+ stream_options: Optional[AbcConsumerOptions] = None,
60
61
  credit: Optional[int] = None,
61
62
  *,
62
63
  connection_lock: asyncio.Lock,
@@ -69,7 +70,7 @@ class AsyncConsumer:
69
70
  conn (BlockingConnection): The blocking connection to use
70
71
  addr (str): The address to consume from
71
72
  handler (Optional[AMQPMessagingHandler]): Optional message handler for processing received messages
72
- stream_options (Optional[ConsumerOptions]): Optional configuration for stream-based consumption
73
+ stream_options (Optional[AbcConsumerOptions]): Optional configuration for stream-based consumption
73
74
  credit (Optional[int]): Optional credit value for flow control
74
75
  connection_lock (asyncio.Lock): Lock for coordinating access to the shared connection.
75
76
  Must be created by the caller (AsyncConnection).
@@ -124,6 +125,11 @@ class AsyncConsumer:
124
125
  """
125
126
  Consume a message from the queue.
126
127
 
128
+ .. deprecated::
129
+ Use the ``message_handler`` parameter when creating the consumer via
130
+ :meth:`AsyncConnection.consumer` instead. The message handler processes
131
+ messages as they arrive without polling.
132
+
127
133
  Args:
128
134
  timeout: The time to wait for a message.
129
135
  None: Defaults to 60s
@@ -139,6 +145,12 @@ class AsyncConsumer:
139
145
  The return type might be None if no message is available and timeout occurs,
140
146
  but this is handled by the cast to Message.
141
147
  """
148
+ warnings.warn(
149
+ "consume() is deprecated; pass message_handler when creating the consumer "
150
+ "via connection.consumer() instead.",
151
+ DeprecationWarning,
152
+ stacklevel=2,
153
+ )
142
154
  if not self._opened or self._consumer is None:
143
155
  raise RuntimeError(
144
156
  "Consumer is not opened. Call open() or use async context manager."
@@ -16,7 +16,7 @@ from packaging import version
16
16
  from .address_helper import validate_address
17
17
  from .consumer import Consumer
18
18
  from .entities import (
19
- ConsumerOptions,
19
+ AbcConsumerOptions,
20
20
  OAuth2Options,
21
21
  RecoveryConfiguration,
22
22
  )
@@ -381,7 +381,7 @@ class Connection:
381
381
  self,
382
382
  destination: Optional[str] = None,
383
383
  message_handler: Optional[MessagingHandler] = None,
384
- consumer_options: Optional[ConsumerOptions] = None,
384
+ consumer_options: Optional[AbcConsumerOptions] = None,
385
385
  credit: Optional[int] = None,
386
386
  ) -> Consumer:
387
387
  """
@@ -1,13 +1,16 @@
1
1
  import logging
2
+ import warnings
2
3
  from typing import Literal, Optional, Union, cast
3
4
 
4
5
  from .amqp_consumer_handler import AMQPMessagingHandler
5
6
  from .entities import (
7
+ AbcConsumerOptions,
6
8
  ConsumerOptions,
7
- DirectReplyToConsumerOptions,
8
9
  StreamConsumerOptions,
9
10
  )
10
11
  from .options import (
12
+ DynamicReceiverOption,
13
+ ReceiverOptionPreSettled,
11
14
  ReceiverOptionUnsettled,
12
15
  ReceiverOptionUnsettledWithFilters,
13
16
  )
@@ -42,7 +45,7 @@ class Consumer:
42
45
  conn: BlockingConnection,
43
46
  addr: Optional[str] = None,
44
47
  handler: Optional[AMQPMessagingHandler] = None,
45
- consumer_options: Optional[ConsumerOptions] = None,
48
+ consumer_options: Optional[AbcConsumerOptions] = None,
46
49
  credit: Optional[int] = None,
47
50
  ):
48
51
  """
@@ -73,16 +76,22 @@ class Consumer:
73
76
  addr = ""
74
77
  if self._addr is not None:
75
78
  addr = self._addr
79
+ credit = 10
80
+ if self._credit is not None:
81
+ credit = self._credit
76
82
 
77
83
  self._conn = conn
84
+ self._receiver = self._create_receiver(addr)
78
85
  if self._consumer_options is None:
79
86
  logger.debug("creating new receiver without stream")
87
+
80
88
  self._receiver = self._conn.create_receiver(
81
89
  addr,
82
90
  options=ReceiverOptionUnsettled(addr),
83
91
  handler=self._handler,
92
+ credit=self._credit,
84
93
  )
85
- else:
94
+ elif isinstance(self._consumer_options, StreamConsumerOptions):
86
95
  logger.debug("creating new stream receiver")
87
96
  self._consumer_options.offset(self._handler.offset - 1) # type: ignore
88
97
  self._receiver = self._conn.create_receiver(
@@ -92,6 +101,10 @@ class Consumer:
92
101
  ),
93
102
  handler=self._handler,
94
103
  )
104
+ elif isinstance(self._consumer_options, ConsumerOptions):
105
+ self._receiver = self._create_receiver_from_cq_consumer_options(
106
+ addr, credit
107
+ )
95
108
 
96
109
  def _set_consumers_list(self, consumers: []) -> None: # type: ignore
97
110
  self._consumers = consumers
@@ -100,6 +113,11 @@ class Consumer:
100
113
  """
101
114
  Consume a message from the queue.
102
115
 
116
+ .. deprecated::
117
+ Use the ``message_handler`` parameter when creating the consumer via
118
+ :meth:`Connection.consumer` instead. The message handler processes
119
+ messages as they arrive without polling.
120
+
103
121
  Args:
104
122
  timeout: The time to wait for a message.
105
123
  None: Defaults to 60s
@@ -112,9 +130,17 @@ class Consumer:
112
130
  The return type might be None if no message is available and timeout occurs,
113
131
  but this is handled by the cast to Message.
114
132
  """
133
+ warnings.warn(
134
+ "consume() is deprecated; pass message_handler when creating the consumer "
135
+ "via connection.consumer() instead.",
136
+ DeprecationWarning,
137
+ stacklevel=2,
138
+ )
115
139
  if self._receiver is not None:
116
- message = self._receiver.receive(timeout=timeout)
117
- return cast(Message, message)
140
+ message_original = self._receiver.receive(timeout=timeout)
141
+ message = cast(Message, message_original)
142
+ return message
143
+ raise Exception("Receiver is not initialized")
118
144
 
119
145
  def close(self) -> None:
120
146
  """
@@ -163,6 +189,8 @@ class Consumer:
163
189
  else:
164
190
  logger.debug("Creating the receiver, without options")
165
191
 
192
+ # we assume the default is for quorum and classic queues with
193
+ # Unsettled mode
166
194
  if self._consumer_options is None:
167
195
  return self._conn.create_receiver(
168
196
  addr,
@@ -171,14 +199,6 @@ class Consumer:
171
199
  credit=credit,
172
200
  )
173
201
 
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
181
-
182
202
  if isinstance(self._consumer_options, StreamConsumerOptions):
183
203
  return self._conn.create_receiver(
184
204
  addr,
@@ -188,10 +208,48 @@ class Consumer:
188
208
  handler=self._handler,
189
209
  )
190
210
 
211
+ if isinstance(self._consumer_options, ConsumerOptions):
212
+ return self._create_receiver_from_cq_consumer_options(addr, credit)
213
+
191
214
  raise Exception(
192
215
  "Receiver is not initialized. No valid consumer options provided."
193
216
  )
194
217
 
218
+ def _create_receiver_from_cq_consumer_options(
219
+ self, addr: Optional[str], credit: int
220
+ ) -> BlockingReceiver:
221
+
222
+ if self._consumer_options is None:
223
+ raise Exception(
224
+ "Consumer options must be provided for CQ receiver creation."
225
+ )
226
+
227
+ if self._consumer_options.pre_settled():
228
+ logger.debug("Creating receiver with pre-settled deliveries")
229
+ return self._conn.create_receiver(
230
+ addr,
231
+ options=ReceiverOptionPreSettled(addr),
232
+ handler=self._handler,
233
+ credit=credit,
234
+ )
235
+
236
+ if self._consumer_options.direct_reply_to():
237
+ logger.debug("Creating receiver for direct-reply-to queue")
238
+ return self._conn.create_receiver(
239
+ addr,
240
+ options=DynamicReceiverOption(),
241
+ handler=self._handler,
242
+ credit=credit,
243
+ )
244
+
245
+ logger.debug("Creating receiver for CQ queue with default unsettled deliveries")
246
+ return self._conn.create_receiver(
247
+ addr,
248
+ options=ReceiverOptionUnsettled(addr),
249
+ handler=self._handler,
250
+ credit=credit,
251
+ )
252
+
195
253
  @property
196
254
  def address(self) -> Optional[str]:
197
255
  if self._receiver is not None:
@@ -0,0 +1,219 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Dict
3
+
4
+ from .exceptions import (
5
+ ArgumentOutOfRangeException,
6
+ InvalidOperationException,
7
+ )
8
+ from .qpid.proton._data import PythonAMQPData
9
+ from .qpid.proton._delivery import Delivery
10
+ from .qpid.proton._events import Event
11
+ from .utils import validate_annotations
12
+
13
+ """
14
+ DeliveryContext is a class that is used to accept, reject, requeue or requeue with annotations a message.
15
+ It is an helper to set the default values needed for manually accepting and settling messages.
16
+ """
17
+
18
+
19
+ class AbcDeliveryContext(ABC):
20
+ """
21
+ Abstract interface for delivery context operations.
22
+ Provides methods to accept, reject, requeue or requeue with annotations a message.
23
+ """
24
+
25
+ @abstractmethod
26
+ def accept(self, event: Event) -> None:
27
+ """
28
+ Accept the message (AMQP 1.0 <code>accepted</code> outcome).
29
+
30
+ This means the message has been processed and the broker can delete it.
31
+ """
32
+ pass
33
+
34
+ @abstractmethod
35
+ def discard(self, event: Event) -> None:
36
+ """
37
+ Reject the message (AMQP 1.0 <code>rejected</code> outcome).
38
+ This means the message cannot be processed because it is invalid, the broker can drop it
39
+ or dead-letter it if it is configured.
40
+ """
41
+ pass
42
+
43
+ @abstractmethod
44
+ def discard_with_annotations(
45
+ self, event: Event, annotations: Dict[str, "PythonAMQPData"]
46
+ ) -> None:
47
+ """
48
+ Discard the message with annotations to combine with the existing message annotations.
49
+ This means the message cannot be processed because it is invalid, the broker can drop it
50
+ or dead-letter it if it is configured.
51
+ Application-specific annotation keys must start with the <code>x-opt-</code> prefix.
52
+ Annotation keys the broker understands starts with <code>x-</code>, but not with <code>x-opt-
53
+ This maps to the AMQP 1.0
54
+ modified{delivery-failed = true, undeliverable-here = true}</code> outcome.
55
+ <param name="annotations"> annotations message annotations to combine with existing ones </param>
56
+ <a
57
+ href="https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-modified">AMQP
58
+ 1.0 <code>modified</code> outcome</a>
59
+ The annotations can be used only with Quorum queues, see https://www.rabbitmq.com/docs/amqp#modified-outcome
60
+ """
61
+ pass
62
+
63
+ @abstractmethod
64
+ def requeue(self, event: Event) -> None:
65
+ """
66
+ Requeue the message (AMQP 1.0 <code>released</code> outcome).
67
+ This means the message has not been processed and the broker can requeue it and deliver it
68
+ to the same or a different consumer.
69
+ """
70
+ pass
71
+
72
+ @abstractmethod
73
+ def requeue_with_annotations(
74
+ self, event: Event, annotations: Dict[str, "PythonAMQPData"]
75
+ ) -> None:
76
+ """
77
+ Requeue the message with annotations to combine with the existing message annotations.
78
+ This means the message has not been processed and the broker can requeue it and deliver it
79
+ to the same or a different consumer.
80
+ Application-specific annotation keys must start with the <code>x-opt-</code> prefix.
81
+ Annotation keys the broker understands starts with <code>x-</code>, but not with <code>x-opt-
82
+ </code>.
83
+ This maps to the AMQP 1.0 <code>
84
+ modified{delivery-failed = false, undeliverable-here = false}</code> outcome.
85
+ <param name="annotations"> annotations message annotations to combine with existing ones </param>
86
+ <a
87
+ href="https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-modified">AMQP
88
+ 1.0 <code>modified</code> outcome</a>
89
+ The annotations can be used only with Quorum queues, see https://www.rabbitmq.com/docs/amqp#modified-outcome
90
+ """
91
+ pass
92
+
93
+
94
+ class DeliveryContext(AbcDeliveryContext):
95
+ """
96
+ Default implementation of IDeliveryContext.
97
+ Used to accept, reject, requeue or requeue with annotations a message.
98
+ It is an helper to set the default values needed for manually accepting and settling messages.
99
+ """
100
+
101
+ def accept(self, event: Event) -> None:
102
+ """
103
+ Accept the message (AMQP 1.0 <code>accepted</code> outcome).
104
+
105
+ This means the message has been processed and the broker can delete it.
106
+ """
107
+ dlv = event.delivery
108
+ if dlv.settled:
109
+ # like the other clients, it has to raise an exception if trying to accept an already settled delivery
110
+ raise InvalidOperationException(
111
+ "auto-settle on, message is already disposed"
112
+ )
113
+ dlv.update(Delivery.ACCEPTED)
114
+ dlv.settle()
115
+
116
+ def discard(self, event: Event) -> None:
117
+ """
118
+ Reject the message (AMQP 1.0 <code>rejected</code> outcome).
119
+ This means the message cannot be processed because it is invalid, the broker can drop it
120
+ or dead-letter it if it is configured.
121
+ """
122
+ dlv = event.delivery
123
+ if dlv.settled:
124
+ # like the other clients, it has to raise an exception if trying to discard an already settled delivery
125
+ raise InvalidOperationException(
126
+ "auto-settle on, message is already disposed"
127
+ )
128
+ dlv.update(Delivery.REJECTED)
129
+ dlv.settle()
130
+
131
+ def discard_with_annotations(
132
+ self, event: Event, annotations: Dict[str, "PythonAMQPData"]
133
+ ) -> None:
134
+ """
135
+ Discard the message with annotations to combine with the existing message annotations.
136
+ This means the message cannot be processed because it is invalid, the broker can drop it
137
+ or dead-letter it if it is configured.
138
+ Application-specific annotation keys must start with the <code>x-opt-</code> prefix.
139
+ Annotation keys the broker understands starts with <code>x-</code>, but not with <code>x-opt-
140
+ This maps to the AMQP 1.0
141
+ modified{delivery-failed = true, undeliverable-here = true}</code> outcome.
142
+ <param name="annotations"> annotations message annotations to combine with existing ones </param>
143
+ <a
144
+ href="https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-modified">AMQP
145
+ 1.0 <code>modified</code> outcome</a>
146
+ The annotations can be used only with Quorum queues, see https://www.rabbitmq.com/docs/amqp#modified-outcome
147
+ """
148
+ dlv = event.delivery
149
+ if dlv.settled:
150
+ # like the other clients, it has to raise an exception if trying to discard an already settled delivery
151
+ raise InvalidOperationException(
152
+ "auto-settle on, message is already disposed"
153
+ )
154
+ dlv.local.annotations = annotations
155
+ dlv.local.failed = True
156
+ dlv.local.undeliverable = True
157
+
158
+ validated = validate_annotations(annotations.keys())
159
+
160
+ if validated is False:
161
+ raise ArgumentOutOfRangeException(
162
+ "Message annotation key must start with 'x-'"
163
+ )
164
+
165
+ dlv.update(Delivery.MODIFIED)
166
+ dlv.settle()
167
+
168
+ def requeue(self, event: Event) -> None:
169
+ """
170
+ Requeue the message (AMQP 1.0 <code>released</code> outcome).
171
+ This means the message has not been processed and the broker can requeue it and deliver it
172
+ to the same or a different consumer.
173
+ """
174
+ dlv = event.delivery
175
+ if dlv.settled:
176
+ # like the other clients, it has to raise an exception if trying to requeue an already settled delivery
177
+ raise InvalidOperationException(
178
+ "auto-settle on, message is already disposed"
179
+ )
180
+ dlv.update(Delivery.RELEASED)
181
+ dlv.settle()
182
+
183
+ def requeue_with_annotations(
184
+ self, event: Event, annotations: Dict[str, "PythonAMQPData"]
185
+ ) -> None:
186
+ """
187
+ Requeue the message with annotations to combine with the existing message annotations.
188
+ This means the message has not been processed and the broker can requeue it and deliver it
189
+ to the same or a different consumer.
190
+ Application-specific annotation keys must start with the <code>x-opt-</code> prefix.
191
+ Annotation keys the broker understands starts with <code>x-</code>, but not with <code>x-opt-
192
+ </code>.
193
+ This maps to the AMQP 1.0 <code>
194
+ modified{delivery-failed = false, undeliverable-here = false}</code> outcome.
195
+ <param name="annotations"> annotations message annotations to combine with existing ones </param>
196
+ <a
197
+ href="https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-modified">AMQP
198
+ 1.0 <code>modified</code> outcome</a>
199
+ The annotations can be used only with Quorum queues, see https://www.rabbitmq.com/docs/amqp#modified-outcome
200
+ """
201
+ dlv = event.delivery
202
+ if dlv.settled:
203
+ # like the other clients, it has to raise an exception if trying to requeue an already settled delivery
204
+ raise InvalidOperationException(
205
+ "auto-settle on, message is already disposed"
206
+ )
207
+ dlv.local.annotations = annotations
208
+ dlv.local.failed = False
209
+ dlv.local.undeliverable = False
210
+
211
+ validated = validate_annotations(annotations.keys())
212
+
213
+ if validated is False:
214
+ raise ArgumentOutOfRangeException(
215
+ "Message annotation key must start with 'x-'"
216
+ )
217
+
218
+ dlv.update(Delivery.MODIFIED)
219
+ dlv.settle()
@@ -1,11 +1,12 @@
1
+ from abc import ABC, abstractmethod
1
2
  from dataclasses import dataclass, field
2
3
  from datetime import datetime, timedelta
3
- from enum import Enum
4
+ from enum import Enum, auto
4
5
  from typing import Any, Dict, Optional, Union
5
6
 
6
7
  from .common import ExchangeType, QueueType
7
8
  from .exceptions import ValidationCodeException
8
- from .qpid.proton._data import Described, symbol
9
+ from .qpid.proton._data import Described, symbol, timestamp
9
10
 
10
11
  SQL_FILTER = "sql-filter"
11
12
  AMQP_SQL_FILTER = "amqp:sql-filter"
@@ -109,11 +110,13 @@ class OffsetSpecification(Enum):
109
110
  first: Start from the first message in the stream
110
111
  next: Start from the next message to arrive
111
112
  last: Start from the last message in the stream
113
+ timestamp: Start from a specific timestamp
112
114
  """
113
115
 
114
116
  first = ("first",)
115
117
  next = ("next",)
116
118
  last = ("last",)
119
+ timestamp = ("timestamp",)
117
120
 
118
121
 
119
122
  @dataclass
@@ -153,16 +156,27 @@ class ExchangeToExchangeBindingSpecification:
153
156
  binding_key: Optional[str] = None
154
157
 
155
158
 
156
- class ConsumerOptions:
159
+ class AbcConsumerOptions(ABC):
160
+ """
161
+ Abstract base class for consumer configuration options.
162
+ This class defines the interface for consumer options, including validation
163
+ and filter set retrieval.
164
+ """
165
+
166
+ @abstractmethod
157
167
  def validate(self, versions: Dict[str, bool]) -> None:
158
168
  raise NotImplementedError("Subclasses should implement this method")
159
169
 
170
+ @abstractmethod
160
171
  def filter_set(self) -> Dict[symbol, Described]:
161
172
  raise NotImplementedError("Subclasses should implement this method")
162
173
 
163
174
  def direct_reply_to(self) -> bool:
164
175
  return False
165
176
 
177
+ def pre_settled(self) -> bool:
178
+ return False
179
+
166
180
 
167
181
  @dataclass
168
182
  class MessageProperties:
@@ -198,13 +212,12 @@ class MessageProperties:
198
212
  reply_to_group_id: Optional[str] = None
199
213
 
200
214
 
201
- """
202
- StreamFilterOptions defines the filtering options for a stream consumer.
203
- for values and match_unfiltered see: https://www.rabbitmq.com/blog/2023/10/16/stream-filtering
204
- """
205
-
206
-
207
215
  class StreamFilterOptions:
216
+ """
217
+ StreamFilterOptions defines the filtering options for a stream consumer.
218
+ for values and match_unfiltered see: https://www.rabbitmq.com/blog/2023/10/16/stream-filtering
219
+ """
220
+
208
221
  values: Optional[list[str]] = None
209
222
  match_unfiltered: bool = False
210
223
  application_properties: Optional[dict[str, Any]] = None
@@ -226,7 +239,7 @@ class StreamFilterOptions:
226
239
  self.sql = sql
227
240
 
228
241
 
229
- class StreamConsumerOptions(ConsumerOptions):
242
+ class StreamConsumerOptions(AbcConsumerOptions):
230
243
  """
231
244
  Configuration options for stream queues.
232
245
 
@@ -243,7 +256,9 @@ class StreamConsumerOptions(ConsumerOptions):
243
256
 
244
257
  def __init__(
245
258
  self,
246
- offset_specification: Optional[Union[OffsetSpecification, int]] = None,
259
+ offset_specification: Optional[
260
+ Union[OffsetSpecification, int, datetime]
261
+ ] = None,
247
262
  filter_options: Optional[StreamFilterOptions] = None,
248
263
  ):
249
264
 
@@ -275,7 +290,9 @@ class StreamConsumerOptions(ConsumerOptions):
275
290
  if filter_options.sql is not None and filter_options.sql != "":
276
291
  self._filter_sql(filter_options.sql)
277
292
 
278
- def _offset(self, offset_specification: Union[OffsetSpecification, int]) -> None:
293
+ def _offset(
294
+ self, offset_specification: Union[OffsetSpecification, int, datetime]
295
+ ) -> None:
279
296
  """
280
297
  Set the offset specification for the stream.
281
298
 
@@ -287,11 +304,20 @@ class StreamConsumerOptions(ConsumerOptions):
287
304
  self._filter_set[symbol(STREAM_OFFSET_SPEC)] = Described(
288
305
  symbol(STREAM_OFFSET_SPEC), offset_specification
289
306
  )
290
- else:
307
+ if isinstance(offset_specification, OffsetSpecification):
291
308
  self._filter_set[symbol(STREAM_OFFSET_SPEC)] = Described(
292
309
  symbol(STREAM_OFFSET_SPEC), offset_specification.name
293
310
  )
294
311
 
312
+ if isinstance(offset_specification, datetime):
313
+ # convert datetime to milliseconds since epoch
314
+ milliseconds_since_epoch = int(offset_specification.timestamp() * 1000)
315
+ # Create a timestamp instance
316
+ ts = timestamp(milliseconds_since_epoch)
317
+ self._filter_set[symbol(STREAM_OFFSET_SPEC)] = Described(
318
+ symbol(STREAM_OFFSET_SPEC), ts
319
+ )
320
+
295
321
  def _filter_values(self, filters: list[str]) -> None:
296
322
  """
297
323
  Set the filter values for the stream.
@@ -403,19 +429,92 @@ class StreamConsumerOptions(ConsumerOptions):
403
429
  )
404
430
 
405
431
 
406
- class DirectReplyToConsumerOptions(ConsumerOptions):
432
+ class ConsumerSettleStrategy(Enum):
433
+ """
434
+ ConsumerSettleStrategy defines the settle strategy for Classic and Quorum queue consumers.
435
+ Uniform with the AMQP 1.0 clients interface (e.g. .NET client SettleStrategy).
436
+
437
+ Attributes:
438
+ ExplicitSettle: Consumer uses explicit settle (default). Message settle mode
439
+ is the default one (explicit settle).
440
+ DirectReplyTo: Enables Direct Reply-To consumer behavior.
441
+ Feature in RabbitMQ, allowing for simplified request-reply messaging patterns.
442
+ Docs: https://www.rabbitmq.com/docs/direct-reply-to#usage-amqp
443
+ PreSettled: Deliveries are pre-settled (at-most-once semantics).
444
+ When enabled, messages are automatically settled when received,
445
+ meaning they cannot be redelivered if processing fails.
446
+ """
447
+
448
+ ExplicitSettle = auto()
449
+ DirectReplyTo = auto()
450
+ PreSettled = auto()
451
+
452
+
453
+ @dataclass
454
+ class ConsumerOptions(AbcConsumerOptions):
455
+ """
456
+ Configuration options for FIFO (Classic and Quorum) queue consumers.
457
+ Not valid for Stream queues.
458
+
459
+ This class provides options for consuming from FIFO queues, including
460
+ support for pre-settled deliveries. Uses a single settle_strategy to
461
+ align with the uniform AMQP 1.0 clients interface.
462
+
463
+ Attributes:
464
+ see ConsumerSettleStrategy enum for available strategies.
465
+ """
466
+
467
+ def __init__(
468
+ self,
469
+ settle_strategy: ConsumerSettleStrategy = ConsumerSettleStrategy.ExplicitSettle,
470
+ ):
471
+ super().__init__()
472
+ self._settle_strategy = settle_strategy
407
473
 
408
474
  def validate(self, versions: Dict[str, bool]) -> None:
409
- if not versions.get("4.2.0", False):
410
- raise ValidationCodeException(
411
- "Direct Reply-To requires RabbitMQ 4.2.0 or higher"
412
- )
475
+ """
476
+ Validates Classic/Quorum consumer options against supported RabbitMQ server versions.
477
+
478
+ Args:
479
+ versions: Dictionary mapping version strings to boolean indicating support.
480
+
481
+ Raises:
482
+ ValidationCodeException: If a feature requires a higher RabbitMQ version.
483
+ """
484
+ # Classic/Quorum queues and pre-settled deliveries are supported in RabbitMQ 4.x
485
+ # No specific version validation needed at this time
486
+ if self._settle_strategy == ConsumerSettleStrategy.DirectReplyTo:
487
+ if not versions.get("4.2.0", False):
488
+ raise ValidationCodeException(
489
+ "Direct Reply-To requires RabbitMQ 4.2.0 or higher"
490
+ )
413
491
 
414
492
  def filter_set(self) -> Dict[symbol, Described]:
493
+ """
494
+ Get the filter set configuration (empty for FIFO consumers).
495
+
496
+ Returns:
497
+ Dict[symbol, Described]: Empty dictionary as FIFO consumers don't use filters.
498
+ """
415
499
  return {}
416
500
 
417
501
  def direct_reply_to(self) -> bool:
418
- return True
502
+ """
503
+ Indicates if this is a direct reply-to consumer.
504
+
505
+ Returns:
506
+ bool: True if DirectReplyTo settle strategy is used, False otherwise.
507
+ """
508
+ return self._settle_strategy is ConsumerSettleStrategy.DirectReplyTo
509
+
510
+ def pre_settled(self) -> bool:
511
+ """
512
+ Indicates if deliveries are pre-settled.
513
+
514
+ Returns:
515
+ bool: True if deliveries are pre-settled, False otherwise.
516
+ """
517
+ return self._settle_strategy is ConsumerSettleStrategy.PreSettled
419
518
 
420
519
 
421
520
  @dataclass
@@ -14,3 +14,12 @@ class ArgumentOutOfRangeException(BaseException):
14
14
 
15
15
  def __str__(self) -> str:
16
16
  return repr(self.msg)
17
+
18
+
19
+ class InvalidOperationException(Exception):
20
+ # Constructor or Initializer
21
+ def __init__(self, msg: str):
22
+ self.msg = msg
23
+
24
+ def __str__(self) -> str:
25
+ return repr(self.msg)
@@ -1,6 +1,6 @@
1
1
  from typing import Optional
2
2
 
3
- from .entities import ConsumerOptions
3
+ from .entities import AbcConsumerOptions
4
4
  from .qpid.proton._data import ( # noqa: E402
5
5
  Data,
6
6
  PropertyDict,
@@ -89,7 +89,7 @@ class ReceiverOptionUnsettled(LinkOption): # type: ignore
89
89
 
90
90
 
91
91
  class ReceiverOptionUnsettledWithFilters(Filter): # type: ignore
92
- def __init__(self, addr: Optional[str], consumer_options: ConsumerOptions):
92
+ def __init__(self, addr: Optional[str], consumer_options: AbcConsumerOptions):
93
93
  super().__init__(consumer_options.filter_set())
94
94
  self._addr = addr
95
95
 
@@ -103,3 +103,26 @@ class ReceiverOptionUnsettledWithFilters(Filter): # type: ignore
103
103
 
104
104
  def test(self, link: Link) -> bool:
105
105
  return bool(link.is_receiver)
106
+
107
+
108
+ class ReceiverOptionPreSettled(LinkOption): # type: ignore
109
+ """
110
+ Receiver option for pre-settled deliveries (at-most-once semantics).
111
+
112
+ When pre-settled is enabled, deliveries are automatically settled when received,
113
+ meaning they cannot be redelivered if processing fails. This uses RCV_FIRST
114
+ which causes the receiver to settle deliveries immediately upon receiving them.
115
+ """
116
+
117
+ def __init__(self, addr: Optional[str]):
118
+ self._addr = addr
119
+
120
+ def apply(self, link: Link) -> None:
121
+ link.target.address = self._addr
122
+ link.snd_settle_mode = Link.SND_SETTLED
123
+ link.rcv_settle_mode = Link.RCV_FIRST
124
+ link.properties = PropertyDict({symbol("paired"): True})
125
+ link.source.dynamic = False
126
+
127
+ def test(self, link: Link) -> bool:
128
+ return bool(link.is_receiver)
@@ -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,
@@ -1039,7 +1039,7 @@ class Backoff(object):
1039
1039
 
1040
1040
 
1041
1041
  def make_backoff_wrapper(
1042
- backoff: Optional[Union[List[Union[float, int]], bool, Backoff]]
1042
+ backoff: Optional[Union[List[Union[float, int]], bool, Backoff]],
1043
1043
  ) -> Optional[Union[List[Union[float, int]], bool, Backoff]]:
1044
1044
  """
1045
1045
  Make a wrapper for a backoff object:
@@ -1,114 +0,0 @@
1
- from typing import Dict
2
-
3
- from .exceptions import ArgumentOutOfRangeException
4
- from .qpid.proton._data import PythonAMQPData
5
- from .qpid.proton._delivery import Delivery
6
- from .qpid.proton._events import Event
7
- from .utils import validate_annotations
8
-
9
- """
10
- DeliveryContext is a class that is used to accept, reject, requeue or requeue with annotations a message.
11
- It is an helper to set the default values needed for manually accepting and settling messages.
12
- """
13
-
14
-
15
- class DeliveryContext:
16
- """
17
- Accept the message (AMQP 1.0 <code>accepted</code> outcome).
18
-
19
- This means the message has been processed and the broker can delete it.
20
- """
21
-
22
- def accept(self, event: Event) -> None:
23
- dlv = event.delivery
24
- dlv.update(Delivery.ACCEPTED)
25
- dlv.settle()
26
-
27
- """
28
- Reject the message (AMQP 1.0 <code>rejected</code> outcome).
29
- This means the message cannot be processed because it is invalid, the broker can drop it
30
- or dead-letter it if it is configured.
31
- """
32
-
33
- def discard(self, event: Event) -> None:
34
- dlv = event.delivery
35
- dlv.update(Delivery.REJECTED)
36
- dlv.settle()
37
-
38
- """
39
- Discard the message with annotations to combine with the existing message annotations.
40
- This means the message cannot be processed because it is invalid, the broker can drop it
41
- or dead-letter it if it is configured.
42
- Application-specific annotation keys must start with the <code>x-opt-</code> prefix.
43
- Annotation keys the broker understands starts with <code>x-</code>, but not with <code>x-opt-
44
- This maps to the AMQP 1.0
45
- modified{delivery-failed = true, undeliverable-here = true}</code> outcome.
46
- <param name="annotations"> annotations message annotations to combine with existing ones </param>
47
- <a
48
- href="https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-modified">AMQP
49
- 1.0 <code>modified</code> outcome</a>
50
- The annotations can be used only with Quorum queues, see https://www.rabbitmq.com/docs/amqp#modified-outcome
51
- """
52
-
53
- def discard_with_annotations(
54
- self, event: Event, annotations: Dict[str, "PythonAMQPData"]
55
- ) -> None:
56
- dlv = event.delivery
57
- dlv.local.annotations = annotations
58
- dlv.local.failed = True
59
- dlv.local.undeliverable = True
60
-
61
- validated = validate_annotations(annotations.keys())
62
-
63
- if validated is False:
64
- raise ArgumentOutOfRangeException(
65
- "Message annotation key must start with 'x-'"
66
- )
67
-
68
- dlv.update(Delivery.MODIFIED)
69
- dlv.settle()
70
-
71
- """
72
- Requeue the message (AMQP 1.0 <code>released</code> outcome).
73
- This means the message has not been processed and the broker can requeue it and deliver it
74
- to the same or a different consumer.
75
- """
76
-
77
- def requeue(self, event: Event) -> None:
78
- dlv = event.delivery
79
- dlv.update(Delivery.RELEASED)
80
- dlv.settle()
81
-
82
- """
83
- Requeue the message with annotations to combine with the existing message annotations.
84
- This means the message has not been processed and the broker can requeue it and deliver it
85
- to the same or a different consumer.
86
- Application-specific annotation keys must start with the <code>x-opt-</code> prefix.
87
- Annotation keys the broker understands starts with <code>x-</code>, but not with <code>x-opt-
88
- </code>.
89
- This maps to the AMQP 1.0 <code>
90
- modified{delivery-failed = false, undeliverable-here = false}</code> outcome.
91
- <param name="annotations"> annotations message annotations to combine with existing ones </param>
92
- <a
93
- href="https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-modified">AMQP
94
- 1.0 <code>modified</code> outcome</a>
95
- The annotations can be used only with Quorum queues, see https://www.rabbitmq.com/docs/amqp#modified-outcome
96
- """
97
-
98
- def requeue_with_annotations(
99
- self, event: Event, annotations: Dict[str, "PythonAMQPData"]
100
- ) -> None:
101
- dlv = event.delivery
102
- dlv.local.annotations = annotations
103
- dlv.local.failed = False
104
- dlv.local.undeliverable = False
105
-
106
- validated = validate_annotations(annotations.keys())
107
-
108
- if validated is False:
109
- raise ArgumentOutOfRangeException(
110
- "Message annotation key must start with 'x-'"
111
- )
112
-
113
- dlv.update(Delivery.MODIFIED)
114
- dlv.settle()