Skip to content

Commit 9926619

Browse files
[ServiceBus] Graceful noops for methods taking empty lists. (#15286)
Make send_messages, schedule_messages, cancel_scheduled_messages, and receive_deferred_messages gracefully no-op when provided an empty list or empty batch (where appropriate). Adds tests and changelog notes for these scenarios. Closes #15211
1 parent 9f4e92b commit 9926619

File tree

7 files changed

+32
-10
lines changed

7 files changed

+32
-10
lines changed

sdk/servicebus/azure-servicebus/CHANGELOG.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@
44

55
**Breaking Changes**
66

7-
* `ServiceBusSender` and `ServiceBusReceiver` are no longer reusable and will raise `ValueError` when trying to operate on a closed handler.
7+
* `ServiceBusSender` and `ServiceBusReceiver` are no more reusable and will raise `ValueError` when trying to operate on a closed handler.
8+
* `send_messages`, `schedule_messages`, `cancel_scheduled_messages` and `receive_deferred_messages` now performs a no-op rather than raising a `ValueError` if provided an empty list of messages or an empty batch.
89

910
**BugFixes**
1011

1112
* FQDNs and Connection strings are now supported even with strippable whitespace or protocol headers (e.g. 'sb://').
12-
13-
**Bug Fixes**
14-
1513
* Using parameter `auto_lock_renewer` on a sessionful receiver alongside `ReceiveMode.ReceiveAndDelete` will no longer fail during receipt due to failure to register the message with the renewer.
1614

1715
## 7.0.0b8 (2020-11-05)

sdk/servicebus/azure-servicebus/azure/servicebus/_servicebus_receiver.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -555,8 +555,8 @@ def receive_deferred_messages(self, sequence_numbers, **kwargs):
555555
raise ValueError("The timeout must be greater than 0.")
556556
if isinstance(sequence_numbers, six.integer_types):
557557
sequence_numbers = [sequence_numbers]
558-
if not sequence_numbers:
559-
raise ValueError("At least one sequence number must be specified.")
558+
if len(sequence_numbers) == 0:
559+
return [] # no-op on empty list.
560560
self._open()
561561
try:
562562
receive_mode = self._receive_mode.value.value

sdk/servicebus/azure-servicebus/azure/servicebus/_servicebus_sender.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ def schedule_messages(self, messages, schedule_time_utc, **kwargs):
259259
if isinstance(messages, ServiceBusMessage):
260260
request_body = self._build_schedule_request(schedule_time_utc, messages)
261261
else:
262+
if len(messages) == 0:
263+
return [] # No-op on empty list.
262264
request_body = self._build_schedule_request(schedule_time_utc, *messages)
263265
return self._mgmt_request_response_with_retry(
264266
REQUEST_RESPONSE_SCHEDULE_MESSAGE_OPERATION,
@@ -296,6 +298,8 @@ def cancel_scheduled_messages(self, sequence_numbers, **kwargs):
296298
numbers = [types.AMQPLong(sequence_numbers)]
297299
else:
298300
numbers = [types.AMQPLong(s) for s in sequence_numbers]
301+
if len(numbers) == 0:
302+
return None # no-op on empty list.
299303
request_body = {MGMT_REQUEST_SEQUENCE_NUMBERS: types.AMQPArray(numbers)}
300304
return self._mgmt_request_response_with_retry(
301305
REQUEST_RESPONSE_CANCEL_SCHEDULED_MESSAGE_OPERATION,
@@ -347,7 +351,7 @@ def send_messages(self, message, **kwargs):
347351
except TypeError: # Message was not a list or generator.
348352
pass
349353
if isinstance(message, ServiceBusMessageBatch) and len(message) == 0: # pylint: disable=len-as-condition
350-
raise ValueError("A ServiceBusMessageBatch or list of Message must have at least one Message")
354+
return # Short circuit noop if an empty list or batch is provided.
351355
if not isinstance(message, ServiceBusMessageBatch) and not isinstance(message, ServiceBusMessage):
352356
raise TypeError(
353357
"Can only send azure.servicebus.<ServiceBusMessageBatch,ServiceBusMessage> or "

sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_receiver_async.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -557,8 +557,8 @@ async def receive_deferred_messages(
557557
raise ValueError("The timeout must be greater than 0.")
558558
if isinstance(sequence_numbers, six.integer_types):
559559
sequence_numbers = [sequence_numbers]
560-
if not sequence_numbers:
561-
raise ValueError("At least one sequence number must be specified.")
560+
if len(sequence_numbers) == 0:
561+
return [] # no-op on empty list.
562562
await self._open()
563563
try:
564564
receive_mode = self._receive_mode.value.value

sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_sender_async.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ async def schedule_messages(
204204
if isinstance(messages, ServiceBusMessage):
205205
request_body = self._build_schedule_request(schedule_time_utc, messages)
206206
else:
207+
if len(messages) == 0:
208+
return [] # No-op on empty list.
207209
request_body = self._build_schedule_request(schedule_time_utc, *messages)
208210
return await self._mgmt_request_response_with_retry(
209211
REQUEST_RESPONSE_SCHEDULE_MESSAGE_OPERATION,
@@ -240,6 +242,8 @@ async def cancel_scheduled_messages(self, sequence_numbers: Union[int, List[int]
240242
numbers = [types.AMQPLong(sequence_numbers)]
241243
else:
242244
numbers = [types.AMQPLong(s) for s in sequence_numbers]
245+
if len(numbers) == 0:
246+
return None # no-op on empty list.
243247
request_body = {MGMT_REQUEST_SEQUENCE_NUMBERS: types.AMQPArray(numbers)}
244248
return await self._mgmt_request_response_with_retry(
245249
REQUEST_RESPONSE_CANCEL_SCHEDULED_MESSAGE_OPERATION,
@@ -294,7 +298,7 @@ async def send_messages(
294298
except TypeError: # Message was not a list or generator.
295299
pass
296300
if isinstance(message, ServiceBusMessageBatch) and len(message) == 0: # pylint: disable=len-as-condition
297-
raise ValueError("A ServiceBusMessageBatch or list of Message must have at least one Message")
301+
return # Short circuit noop if an empty list or batch is provided.
298302
if not isinstance(message, ServiceBusMessageBatch) and not isinstance(message, ServiceBusMessage):
299303
raise TypeError(
300304
"Can only send azure.servicebus.<ServiceBusMessageBatch,ServiceBusMessage>"

sdk/servicebus/azure-servicebus/tests/async_tests/test_queues_async.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ async def test_async_queue_by_queue_client_conn_str_receive_handler_peeklock(sel
6767
message = ServiceBusMessage("Handler message no. {}".format(i))
6868
await sender.send_messages(message, timeout=5)
6969

70+
# Test that noop empty send works properly.
71+
await sender.send_messages([])
72+
await sender.send_messages(ServiceBusMessageBatch())
73+
assert len(await sender.schedule_messages([], utc_now())) == 0
74+
await sender.cancel_scheduled_messages([])
75+
76+
# Then test expected failure modes.
7077
with pytest.raises(ValueError):
7178
async with sender:
7279
raise AssertionError("Should raise ValueError")
@@ -85,6 +92,7 @@ async def test_async_queue_by_queue_client_conn_str_receive_handler_peeklock(sel
8592

8693
receiver = sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=5)
8794
async with receiver:
95+
assert len(await receiver.receive_deferred_messages([])) == 0
8896
with pytest.raises(ValueError):
8997
await receiver.receive_messages(max_wait_time=0)
9098

sdk/servicebus/azure-servicebus/tests/test_queues.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,15 @@ def test_queue_by_queue_client_conn_str_receive_handler_peeklock(self, servicebu
128128
message.to = 'to'
129129
message.reply_to = 'reply_to'
130130
sender.send_messages(message)
131+
132+
# Test that noop empty send works properly.
133+
sender.send_messages([])
134+
sender.send_messages(ServiceBusMessageBatch())
135+
assert len(sender.schedule_messages([], utc_now())) == 0
136+
sender.cancel_scheduled_messages([])
131137
sender.close()
132138

139+
# Then test expected failure modes.
133140
with pytest.raises(ValueError):
134141
with sender:
135142
raise AssertionError("Should raise ValueError")
@@ -145,6 +152,7 @@ def test_queue_by_queue_client_conn_str_receive_handler_peeklock(self, servicebu
145152

146153
receiver = sb_client.get_queue_receiver(servicebus_queue.name, max_wait_time=5)
147154

155+
assert len(receiver.receive_deferred_messages([])) == 0
148156
with pytest.raises(ValueError):
149157
receiver.receive_messages(max_wait_time=0)
150158

0 commit comments

Comments
 (0)