async def send_message_async(self, messages, close_on_done=False): """Send a single message or batched message asynchronously. :param messages: A message to send. This can either be a single instance of ~uamqp.message.Message, or multiple messages wrapped in an instance of ~uamqp.message.BatchMessage. :type message: ~uamqp.message.Message :param close_on_done: Close the client once the message is sent. Default is `False`. :type close_on_done: bool :raises: ~uamqp.errors.MessageException if message fails to send after retry policy is exhausted. """ batch = messages.gather() pending_batch = [] for message in batch: message.idle_time = self._counter.get_current_ms() async with self._pending_messages_lock: self._pending_messages.append(message) pending_batch.append(message) await self.open_async() try: while any([m for m in pending_batch if m.state not in constants.DONE_STATES]): await self.do_work_async() failed = [m for m in pending_batch if m.state == constants.MessageState.SendFailed] if any(failed): details = {"total_messages": len(pending_batch), "number_failed": len(failed)} details['failed_messages'] = {} exception = None for failed_message in failed: exception = failed_message._response # pylint: disable=protected-access details['failed_messages'][failed_message] = exception raise errors.ClientMessageError(exception, info=details) finally: if close_on_done: await self.close_async()
def _on_message_sent(self, message, result, delivery_state=None): """Callback run on a message send operation. If message has a user defined callback, it will be called here. If the result of the operation is failure, the message state will be reverted to 'pending' up to the maximum retry count. :param message: The message that was sent. :type message: ~uamqp.message.Message :param result: The result of the send operation. :type result: int :param error: An Exception if an error ocurred during the send operation. :type error: ~Exception """ # pylint: disable=protected-access exception = delivery_state result = constants.MessageSendResult(result) if result == constants.MessageSendResult.Error: if isinstance(delivery_state, Exception): exception = errors.ClientMessageError(delivery_state, info=delivery_state) exception.action = errors.ErrorAction(retry=True) elif delivery_state: error = errors.ErrorResponse(delivery_state) exception = errors._process_send_error(self._error_policy, error.condition, error.description, error.info) else: exception = errors.MessageSendFailed( constants.ErrorCodes.UnknownError) exception.action = errors.ErrorAction(retry=True) if exception.action.retry == errors.ErrorAction.retry and message.retries < self._error_policy.max_retries: if exception.action.increment_retries: message.retries += 1 self._backoff = exception.action.backoff _logger.debug( "Message error, retrying. Attempts: %r, Error: %r", message.retries, exception) message.state = constants.MessageState.WaitingToBeSent return if exception.action.retry == errors.ErrorAction.retry: _logger.info("Message error, %r retries exhausted. Error: %r", message.retries, exception) else: _logger.info("Message error, not retrying. Error: %r", exception) message.state = constants.MessageState.SendFailed message._response = exception else: _logger.debug("Message sent: %r, %r", result, exception) message.state = constants.MessageState.SendComplete message._response = errors.MessageAlreadySettled() if message.on_send_complete: message.on_send_complete(result, exception)