diff --git a/sdk/servicebus/azure-servicebus/CHANGELOG.md b/sdk/servicebus/azure-servicebus/CHANGELOG.md index 47341e1e3426..5a7bb24c0bb0 100644 --- a/sdk/servicebus/azure-servicebus/CHANGELOG.md +++ b/sdk/servicebus/azure-servicebus/CHANGELOG.md @@ -6,6 +6,7 @@ * Added method `get_topic_sender` in `ServiceBusClient` to get a `ServiceBusSender` for a topic. * Added method `get_subscription_receiver` in `ServiceBusClient` to get a `ServiceBusReceiver` for a subscription under specific topic. +* `ServiceBusSender.send()` can now send a list of messages in one call, if they fit into a single batch. If they do not fit a `ValueError` is thrown. **BugFixes** diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/client_mixins.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/client_mixins.py index 8bc0de00b643..f492cc74b69d 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/client_mixins.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/client_mixins.py @@ -75,7 +75,7 @@ def create_queue( :type max_delivery_count: int :param enable_batched_operations: :type: enable_batched_operations: bool - :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. + :raises: ~azure.servicebus.exceptions.ServiceBusConnectionError if the namespace is not found. :raises: ~azure.common.AzureConflictHttpError if a queue of the same name already exists. """ queue_properties = Queue( @@ -102,8 +102,8 @@ def delete_queue(self, queue_name, fail_not_exist=False): found. If set to True, a ServiceBusResourceNotFound will be raised. Default value is False. :type fail_not_exist: bool - :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namesapce is not found. - :raises: ~azure.servicebus.common.errors.ServiceBusResourceNotFound if the queue is not found + :raises: ~azure.servicebus.exceptions.ServiceBusConnectionError if the namesapce is not found. + :raises: ~azure.servicebus.exceptions.ServiceBusResourceNotFound if the queue is not found and `fail_not_exist` is set to True. """ try: @@ -137,7 +137,7 @@ def create_topic( :type duplicate_detection_history_time_window: ~datetime.timedelta :param enable_batched_operations: :type: enable_batched_operations: bool - :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. + :raises: ~azure.servicebus.exceptions.ServiceBusConnectionError if the namespace is not found. :raises: ~azure.common.AzureConflictHttpError if a topic of the same name already exists. """ topic_properties = Topic( @@ -160,8 +160,8 @@ def delete_topic(self, topic_name, fail_not_exist=False): found. If set to True, a ServiceBusResourceNotFound will be raised. Default value is False. :type fail_not_exist: bool - :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namesapce is not found. - :raises: ~azure.servicebus.common.errors.ServiceBusResourceNotFound if the topic is not found + :raises: ~azure.servicebus.exceptions.ServiceBusConnectionError if the namesapce is not found. + :raises: ~azure.servicebus.exceptions.ServiceBusResourceNotFound if the topic is not found and `fail_not_exist` is set to True. """ try: @@ -203,7 +203,7 @@ def create_subscription( :type max_delivery_count: int :param enable_batched_operations: :type: enable_batched_operations: bool - :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. + :raises: ~azure.servicebus.exceptions.ServiceBusConnectionError if the namespace is not found. :raises: ~azure.common.AzureConflictHttpError if a queue of the same name already exists. """ sub_properties = Subscription( @@ -232,8 +232,8 @@ def delete_subscription(self, topic_name, subscription_name, fail_not_exist=Fals topic is not found. If set to True, a ServiceBusResourceNotFound will be raised. Default value is False. :type fail_not_exist: bool - :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namesapce is not found. - :raises: ~azure.servicebus.common.errors.ServiceBusResourceNotFound if the entity is not found + :raises: ~azure.servicebus.exceptions.ServiceBusConnectionError if the namesapce is not found. + :raises: ~azure.servicebus.exceptions.ServiceBusResourceNotFound if the entity is not found and `fail_not_exist` is set to True. """ try: @@ -326,8 +326,8 @@ def get_properties(self): :returns: The properties of the entity as a dictionary. :rtype: dict[str, Any] - :raises: ~azure.servicebus.common.errors.ServiceBusResourceNotFound if the entity does not exist. - :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the endpoint cannot be reached. + :raises: ~azure.servicebus.exceptions.ServiceBusResourceNotFound if the entity does not exist. + :raises: ~azure.servicebus.exceptions.ServiceBusConnectionError if the endpoint cannot be reached. :raises: ~azure.common.AzureHTTPError if the credentials are invalid. """ try: diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/message.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/message.py index 926142970537..9435182bdde5 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_common/message.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_common/message.py @@ -301,6 +301,12 @@ def __repr__(self): def __len__(self): return self._count + def _from_list(self, messages): + for each in messages: + if not isinstance(each, Message): + raise ValueError("Populating a message batch only supports iterables containing Message Objects. Received instead: {}".format(each.__class__.__name__)) + self.add(each) + @property def size_in_bytes(self): # type: () -> int @@ -599,10 +605,10 @@ def complete(self): This removes the message from the queue. :rtype: None - :raises: ~azure.servicebus.common.errors.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.common.errors.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.common.errors.SessionLockExpired if session lock has already expired. - :raises: ~azure.servicebus.common.errors.MessageSettleFailed if message settle operation fails. + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.SessionLockExpired if session lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. """ # pylint: disable=protected-access self._is_live(MESSAGE_COMPLETE) @@ -620,10 +626,10 @@ def dead_letter(self, reason=None, description=None): :param str reason: The reason for dead-lettering the message. :param str description: The detailed description for dead-lettering the message. :rtype: None - :raises: ~azure.servicebus.common.errors.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.common.errors.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.common.errors.SessionLockExpired if session lock has already expired. - :raises: ~azure.servicebus.common.errors.MessageSettleFailed if message settle operation fails. + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.SessionLockExpired if session lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. """ # pylint: disable=protected-access self._is_live(MESSAGE_DEAD_LETTER) @@ -642,10 +648,10 @@ def abandon(self): This message will be returned to the queue to be reprocessed. :rtype: None - :raises: ~azure.servicebus.common.errors.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.common.errors.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.common.errors.SessionLockExpired if session lock has already expired. - :raises: ~azure.servicebus.common.errors.MessageSettleFailed if message settle operation fails. + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.SessionLockExpired if session lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. """ # pylint: disable=protected-access self._is_live(MESSAGE_ABANDON) @@ -660,10 +666,10 @@ def defer(self): specifically by its sequence number in order to be processed. :rtype: None - :raises: ~azure.servicebus.common.errors.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.common.errors.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.common.errors.SessionLockExpired if session lock has already expired. - :raises: ~azure.servicebus.common.errors.MessageSettleFailed if message settle operation fails. + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.SessionLockExpired if session lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. """ self._is_live(MESSAGE_DEFER) self._settle_message(MESSAGE_DEFER) @@ -682,8 +688,8 @@ def renew_lock(self): :rtype: None :raises: TypeError if the message is sessionful. - :raises: ~azure.servicebus.common.errors.MessageLockExpired is message lock has already expired. - :raises: ~azure.servicebus.common.errors.MessageAlreadySettled is message has already been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired is message lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled is message has already been settled. """ try: if self._receiver.session: diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/_servicebus_sender.py b/sdk/servicebus/azure-servicebus/azure/servicebus/_servicebus_sender.py index 396fa6b3eced..fa1c1d117b7d 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/_servicebus_sender.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/_servicebus_sender.py @@ -291,14 +291,19 @@ def from_connection_string( return cls(**constructor_args) def send(self, message): - # type: (Union[Message, BatchMessage]) -> None + # type: (Union[Message, BatchMessage, List[Message]]) -> None """Sends message and blocks until acknowledgement is received or operation times out. + If a list of messages was provided, attempts to send them as a single batch, throwing a + `ValueError` if they cannot fit in a single batch. + :param message: The ServiceBus message to be sent. - :type message: ~azure.servicebus.Message or ~azure.servicebus.BatchMessage + :type message: ~azure.servicebus.Message or ~azure.servicebus.BatchMessage or list[~azure.servicebus.Message] :rtype: None - :raises: ~azure.servicebus.common.errors.MessageSendFailed if the message fails to - send or ~azure.servicebus.common.errors.OperationTimeoutError if sending times out. + :raises: :class: ~azure.servicebus.exceptions.MessageSendFailed if the message fails to + send + :class: ~azure.servicebus.exceptions.OperationTimeoutError if sending times out. + :class: `ValueError` if list of messages is provided and cannot fit in a batch. .. admonition:: Example: @@ -310,6 +315,13 @@ def send(self, message): :caption: Send message. """ + try: + batch = self.create_batch() + batch._from_list(message) + message = batch + except TypeError: # Message was not a list or generator. + pass + self._do_retryable_operation( self._send, message=message, diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_async_message.py b/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_async_message.py index 9182b9457802..147d89160a57 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_async_message.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_async_message.py @@ -62,10 +62,10 @@ async def complete(self): This removes the message from the queue. :rtype: None - :raises: ~azure.servicebus.common.errors.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.common.errors.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.common.errors.SessionLockExpired if session lock has already expired. - :raises: ~azure.servicebus.common.errors.MessageSettleFailed if message settle operation fails. + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.SessionLockExpired if session lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. """ # pylint: disable=protected-access self._is_live(MESSAGE_COMPLETE) @@ -83,9 +83,9 @@ async def dead_letter(self, reason=None, description=None): :param str reason: The reason for dead-lettering the message. :param str description: The detailed description for dead-lettering the message. :rtype: None - :raises: ~azure.servicebus.common.errors.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.common.errors.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.common.errors.MessageSettleFailed if message settle operation fails. + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. """ # pylint: disable=protected-access self._is_live(MESSAGE_DEAD_LETTER) @@ -97,9 +97,9 @@ async def abandon(self): """Abandon the message. This message will be returned to the queue to be reprocessed. :rtype: None - :raises: ~azure.servicebus.common.errors.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.common.errors.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.common.errors.MessageSettleFailed if message settle operation fails. + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. """ # pylint: disable=protected-access self._is_live(MESSAGE_ABANDON) @@ -111,9 +111,9 @@ async def defer(self): """Abandon the message. This message will be returned to the queue to be reprocessed. :rtype: None - :raises: ~azure.servicebus.common.errors.MessageAlreadySettled if the message has been settled. - :raises: ~azure.servicebus.common.errors.MessageLockExpired if message lock has already expired. - :raises: ~azure.servicebus.common.errors.MessageSettleFailed if message settle operation fails. + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled if the message has been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired if message lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageSettleFailed if message settle operation fails. """ # pylint: disable=protected-access self._is_live(MESSAGE_DEFER) @@ -133,9 +133,9 @@ async def renew_lock(self): :rtype: None :raises: TypeError if the message is sessionful. - :raises: ~azure.servicebus.common.errors.MessageLockExpired is message lock has already expired. - :raises: ~azure.servicebus.common.errors.SessionLockExpired if session lock has already expired. - :raises: ~azure.servicebus.common.errors.MessageAlreadySettled is message has already been settled. + :raises: ~azure.servicebus.exceptions.MessageLockExpired is message lock has already expired. + :raises: ~azure.servicebus.exceptions.SessionLockExpired if session lock has already expired. + :raises: ~azure.servicebus.exceptions.MessageAlreadySettled is message has already been settled. """ try: if self._receiver.session: # pylint: disable=protected-access diff --git a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_sender_async.py b/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_sender_async.py index 18ef43ef588b..e8bd80fe4387 100644 --- a/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_sender_async.py +++ b/sdk/servicebus/azure-servicebus/azure/servicebus/aio/_servicebus_sender_async.py @@ -239,14 +239,19 @@ def from_connection_string( return cls(**constructor_args) async def send(self, message): - # type: (Union[Message, BatchMessage]) -> None + # type: (Union[Message, BatchMessage, List[Message]]) -> None """Sends message and blocks until acknowledgement is received or operation times out. + If a list of messages was provided, attempts to send them as a single batch, throwing a + `ValueError` if they cannot fit in a single batch. + :param message: The ServiceBus message to be sent. - :type message: ~azure.servicebus.Message or ~azure.servicebus.BatchMessage + :type message: ~azure.servicebus.Message or ~azure.servicebus.BatchMessage or list[~azure.servicebus.Message] :rtype: None - :raises: ~azure.servicebus.common.errors.MessageSendFailed if the message fails to - send or ~azure.servicebus.common.errors.OperationTimeoutError if sending times out. + :raises: :class: ~azure.servicebus.exceptions.MessageSendFailed if the message fails to + send + :class: ~azure.servicebus.exceptions.OperationTimeoutError if sending times out. + :class: `ValueError` if list of messages is provided and cannot fit in a batch. .. admonition:: Example: @@ -258,6 +263,13 @@ async def send(self, message): :caption: Send message. """ + try: + batch = await self.create_batch() + batch._from_list(message) + message = batch + except TypeError: # Message was not a list or generator. + pass + await self._do_retryable_operation( self._send, message=message, diff --git a/sdk/servicebus/azure-servicebus/tests/async_tests/test_queues_async.py b/sdk/servicebus/azure-servicebus/tests/async_tests/test_queues_async.py index df28865e22c4..771ad134b53a 100644 --- a/sdk/servicebus/azure-servicebus/tests/async_tests/test_queues_async.py +++ b/sdk/servicebus/azure-servicebus/tests/async_tests/test_queues_async.py @@ -66,6 +66,33 @@ async def test_async_queue_by_queue_client_conn_str_receive_handler_peeklock(sel assert count == 10 + + @pytest.mark.liveTest + @pytest.mark.live_test_only + @CachedResourceGroupPreparer(name_prefix='servicebustest') + @CachedServiceBusNamespacePreparer(name_prefix='servicebustest') + @ServiceBusQueuePreparer(name_prefix='servicebustest', dead_lettering_on_message_expiration=True) + async def test_async_queue_by_queue_client_send_multiple_messages(self, servicebus_namespace_connection_string, servicebus_queue, **kwargs): + async with ServiceBusClient.from_connection_string( + servicebus_namespace_connection_string, logging_enable=False) as sb_client: + + async with sb_client.get_queue_sender(servicebus_queue.name) as sender: + messages = [] + for i in range(10): + message = Message("Handler message no. {}".format(i)) + messages.append(message) + await sender.send(messages) + + async with sb_client.get_queue_receiver(servicebus_queue.name, idle_timeout=5) as receiver: + count = 0 + async for message in receiver: + print_message(_logger, message) + count += 1 + await message.complete() + + assert count == 10 + + @pytest.mark.liveTest @pytest.mark.live_test_only @CachedResourceGroupPreparer() @@ -688,7 +715,6 @@ async def test_async_queue_by_queue_client_conn_str_receive_handler_with_autoloc await renewer.shutdown() assert len(messages) == 11 - @pytest.mark.skip(reason='requires queuing messages') @pytest.mark.liveTest @pytest.mark.live_test_only @CachedResourceGroupPreparer(name_prefix='servicebustest') @@ -698,18 +724,16 @@ async def test_async_queue_by_servicebus_client_fail_send_messages(self, service async with ServiceBusClient.from_connection_string( servicebus_namespace_connection_string, logging_enable=False) as sb_client: - too_large = "A" * 1024 * 512 + too_large = "A" * 1024 * 256 async with sb_client.get_queue_sender(servicebus_queue.name) as sender: with pytest.raises(MessageSendFailed): await sender.send(Message(too_large)) + + half_too_large = "A" * int((1024 * 256) / 2) + with pytest.raises(ValueError): + await sender.send([Message(half_too_large), Message(half_too_large)]) - async with sb_client.get_queue_sender(servicebus_queue.name) as sender: - sender.queue_message(Message(too_large)) - results = await sender.send_pending_messages() - assert len(results) == 1 - assert not results[0][0] - assert isinstance(results[0][1], MessageSendFailed) @pytest.mark.liveTest @pytest.mark.live_test_only diff --git a/sdk/servicebus/azure-servicebus/tests/test_queues.py b/sdk/servicebus/azure-servicebus/tests/test_queues.py index 125c2d662b88..9f157d24bf52 100644 --- a/sdk/servicebus/azure-servicebus/tests/test_queues.py +++ b/sdk/servicebus/azure-servicebus/tests/test_queues.py @@ -114,6 +114,30 @@ def test_queue_by_queue_client_conn_str_receive_handler_peeklock(self, servicebu assert count == 10 + @pytest.mark.liveTest + @pytest.mark.live_test_only + @CachedResourceGroupPreparer(name_prefix='servicebustest') + @CachedServiceBusNamespacePreparer(name_prefix='servicebustest') + @ServiceBusQueuePreparer(name_prefix='servicebustest', dead_lettering_on_message_expiration=True) + def test_queue_by_queue_client_send_multiple_messages(self, servicebus_namespace_connection_string, servicebus_queue, **kwargs): + with ServiceBusClient.from_connection_string( + servicebus_namespace_connection_string, logging_enable=False) as sb_client: + + with sb_client.get_queue_sender(servicebus_queue.name) as sender: + messages = [] + for i in range(10): + message = Message("Handler message no. {}".format(i)) + messages.append(message) + sender.send(messages) + + with sb_client.get_queue_receiver(servicebus_queue.name, idle_timeout=5) as receiver: + count = 0 + for message in receiver: + print_message(_logger, message) + count += 1 + message.complete() + + assert count == 10 @pytest.mark.liveTest @pytest.mark.live_test_only @@ -673,34 +697,25 @@ def test_queue_by_servicebus_client_browse_empty_messages(self, servicebus_names assert len(messages) == 0 - @pytest.mark.skip(reason="Pending queue message") @pytest.mark.liveTest @pytest.mark.live_test_only @CachedResourceGroupPreparer(name_prefix='servicebustest') @CachedServiceBusNamespacePreparer(name_prefix='servicebustest') @CachedServiceBusQueuePreparer(name_prefix='servicebustest', dead_lettering_on_message_expiration=True) - def test_queue_by_servicebus_client_fail_send_messages(self, servicebus_namespace, servicebus_namespace_key_name, servicebus_namespace_primary_key, servicebus_queue, **kwargs): + def test_queue_by_servicebus_client_fail_send_messages(self, servicebus_namespace_connection_string, servicebus_queue, **kwargs): with ServiceBusClient.from_connection_string( servicebus_namespace_connection_string, logging_enable=False) as sb_client: - too_large = "A" * 1024 * 512 - with sb_client.get_queue_sender(servicebus_queue.name) as sender: - try: - results = sender.send(Message(too_large)) - except MessageSendFailed: - pytest.skip("Open issue for uAMQP on OSX") + too_large = "A" * 256 * 1024 with sb_client.get_queue_sender(servicebus_queue.name) as sender: with pytest.raises(MessageSendFailed): sender.send(Message(too_large)) - - with sb_client.get_queue_sender(servicebus_queue.name) as sender: - sender.queue_message(Message(too_large)) - results = sender.send_pending_messages() - assert len(results) == 1 - assert not results[0][0] - assert isinstance(results[0][1], MessageSendFailed) + + half_too_large = "A" * int((1024 * 256) / 2) + with pytest.raises(ValueError): + sender.send([Message(half_too_large), Message(half_too_large)]) @pytest.mark.liveTest diff --git a/sdk/servicebus/azure-servicebus/tests/test_sb_client.py b/sdk/servicebus/azure-servicebus/tests/test_sb_client.py index 6d898579b304..09892d6b7376 100644 --- a/sdk/servicebus/azure-servicebus/tests/test_sb_client.py +++ b/sdk/servicebus/azure-servicebus/tests/test_sb_client.py @@ -108,7 +108,7 @@ def test_sb_client_writeonly_credentials(self, servicebus_authorization_rule_con with client.get_queue_sender(servicebus_queue.name) as sender: sender.send(Message("test")) - with pytest.raises(ServiceBusError): + with pytest.raises(ValueError): sender.send("cat") @pytest.mark.liveTest