def close(self, exception=None): """ Close down the handler. If the handler has already closed, this will be a no op. An optional exception can be passed in to indicate that the handler was shutdown due to error. :param exception: An optional exception if the handler is closing due to an error. :type exception: Exception Example: .. literalinclude:: ../examples/test_examples_eventhub.py :start-after: [START eventhub_client_receiver_close] :end-before: [END eventhub_client_receiver_close] :language: python :dedent: 4 :caption: Close down the handler. """ self.running = False if self.error: return if isinstance(exception, errors.LinkRedirect): self.redirected = exception elif isinstance(exception, EventHubError): self.error = exception elif exception: self.error = EventHubError(str(exception)) else: self.error = EventHubError("This receive handler is now closed.") self._handler.close()
def receive(self, max_batch_size=None, timeout=None): """ Receive events from the EventHub. :param max_batch_size: Receive a batch of events. Batch size will be up to the maximum specified, but will return as soon as service returns no new events. If combined with a timeout and no events are retrieve before the time, the result will be empty. If no batch size is supplied, the prefetch size will be the maximum. :type max_batch_size: int :rtype: list[~azure.eventhub.common.EventData] """ if self.error: raise self.error try: timeout_ms = 1000 * timeout if timeout else 0 message_batch = self._handler.receive_message_batch( max_batch_size=max_batch_size, timeout=timeout_ms) data_batch = [] for message in message_batch: event_data = EventData(message=message) self.offset = event_data.offset data_batch.append(event_data) return data_batch except errors.LinkDetach as detach: error = EventHubError(str(detach)) self.close(exception=error) raise error except Exception as e: error = EventHubError("Receive failed: {}".format(e)) self.close(exception=error) raise error
def wait(self): """ Wait until all transferred events have been sent. """ if self.error: raise self.error if not self.running: raise ValueError("Unable to send until client has been started.") try: self._handler.wait() except (errors.LinkDetach, errors.ConnectionClose) as shutdown: if shutdown.action.retry and self.auto_reconnect: self.reconnect() else: error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except errors.MessageHandlerError as shutdown: if self.auto_reconnect: self.reconnect() else: error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except Exception as e: raise EventHubError("Send failed: {}".format(e))
def send(self, event_data): """ Sends an event data and blocks until acknowledgement is received or operation times out. :param event_data: The event to be sent. :type event_data: ~azure.eventhub.common.EventData :raises: ~azure.eventhub.common.EventHubError if the message fails to send. :return: The outcome of the message send. :rtype: ~uamqp.constants.MessageSendResult """ if self.error: raise self.error if event_data.partition_key and self.partition: raise ValueError( "EventData partition key cannot be used with a partition sender." ) event_data.message.on_send_complete = self._on_outcome try: self._handler.send_message(event_data.message) if self._outcome != constants.MessageSendResult.Ok: raise Sender._error(self._outcome, self._condition) except errors.LinkDetach as detach: error = EventHubError(str(detach)) self.close(exception=error) raise error except Exception as e: error = EventHubError("Send failed: {}".format(e)) self.close(exception=error) raise error else: return self._outcome
def _reconnect(self): # pylint: disable=too-many-statements # pylint: disable=protected-access alt_creds = { "username": self.client._auth_config.get("iot_username"), "password": self.client._auth_config.get("iot_password")} self._handler.close() source = Source(self.source) if self.offset is not None: source.set_filter(self.offset.selector()) self._handler = ReceiveClient( source, auth=self.client.get_auth(**alt_creds), debug=self.client.debug, prefetch=self.prefetch, link_properties=self.properties, timeout=self.timeout, error_policy=self.retry_policy, keep_alive_interval=self.keep_alive, client_name=self.name, properties=self.client.create_properties()) try: self._handler.open() while not self._handler.client_ready(): time.sleep(0.05) return True except errors.TokenExpired as shutdown: log.info("Receiver disconnected due to token expiry. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except (errors.LinkDetach, errors.ConnectionClose) as shutdown: if shutdown.action.retry and self.auto_reconnect: log.info("Receiver detached. Attempting reconnect.") return False log.info("Receiver detached. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except errors.MessageHandlerError as shutdown: if self.auto_reconnect: log.info("Receiver detached. Attempting reconnect.") return False log.info("Receiver detached. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except errors.AMQPConnectionError as shutdown: if str(shutdown).startswith("Unable to open authentication session") and self.auto_reconnect: log.info("Receiver couldn't authenticate. Attempting reconnect.") return False log.info("Receiver connection error (%r). Shutting down.", shutdown) error = EventHubError(str(shutdown)) self.close(exception=error) raise error except Exception as e: log.info("Unexpected error occurred (%r). Shutting down.", e) error = EventHubError("Receiver reconnect failed: {}".format(e)) self.close(exception=error) raise error
def send(self, event_data): """ Sends an event data and blocks until acknowledgement is received or operation times out. :param event_data: The event to be sent. :type event_data: ~azure.eventhub.common.EventData :raises: ~azure.eventhub.common.EventHubError if the message fails to send. :return: The outcome of the message send. :rtype: ~uamqp.constants.MessageSendResult """ if self.error: raise self.error if not self.running: raise ValueError("Unable to send until client has been started.") if event_data.partition_key and self.partition: raise ValueError( "EventData partition key cannot be used with a partition sender." ) event_data.message.on_send_complete = self._on_outcome try: self._handler.send_message(event_data.message) if self._outcome != constants.MessageSendResult.Ok: raise Sender._error(self._outcome, self._condition) except errors.MessageException as failed: error = EventHubError(str(failed), failed) self.close(exception=error) raise error except (errors.TokenExpired, errors.AuthenticationException): log.info( "Sender disconnected due to token error. Attempting reconnect." ) self.reconnect() except (errors.LinkDetach, errors.ConnectionClose) as shutdown: if shutdown.action.retry and self.auto_reconnect: log.info("Sender detached. Attempting reconnect.") self.reconnect() else: log.info("Sender detached. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except errors.MessageHandlerError as shutdown: if self.auto_reconnect: log.info("Sender detached. Attempting reconnect.") self.reconnect() else: log.info("Sender detached. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except Exception as e: log.info("Unexpected error occurred (%r). Shutting down.", e) error = EventHubError("Send failed: {}".format(e)) self.close(exception=error) raise error else: return self._outcome
def _handle_redirect(self, redirects): if len(redirects) != len(self.clients): raise EventHubError("Some clients are attempting to redirect the connection.") if not all(r.hostname == redirects[0].hostname for r in redirects): raise EventHubError("Multiple clients attempting to redirect to different hosts.") self._process_redirect_uri(redirects[0]) for client in self.clients: client.open()
def receive(self, max_batch_size=None, timeout=None): """ Receive events from the EventHub. :param max_batch_size: Receive a batch of events. Batch size will be up to the maximum specified, but will return as soon as service returns no new events. If combined with a timeout and no events are retrieve before the time, the result will be empty. If no batch size is supplied, the prefetch size will be the maximum. :type max_batch_size: int :rtype: list[~azure.eventhub.common.EventData] """ if self.error: raise self.error if not self.running: raise ValueError("Unable to receive until client has been started.") data_batch = [] try: timeout_ms = 1000 * timeout if timeout else 0 message_batch = self._handler.receive_message_batch( max_batch_size=max_batch_size, timeout=timeout_ms) for message in message_batch: event_data = EventData(message=message) self.offset = event_data.offset data_batch.append(event_data) return data_batch except (errors.TokenExpired, errors.AuthenticationException): log.info("Receiver disconnected due to token error. Attempting reconnect.") self.reconnect() return data_batch except (errors.LinkDetach, errors.ConnectionClose) as shutdown: if shutdown.action.retry and self.auto_reconnect: log.info("Receiver detached. Attempting reconnect.") self.reconnect() return data_batch log.info("Receiver detached. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except errors.MessageHandlerError as shutdown: if self.auto_reconnect: log.info("Receiver detached. Attempting reconnect.") self.reconnect() return data_batch log.info("Receiver detached. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except Exception as e: log.info("Unexpected error occurred (%r). Shutting down.", e) error = EventHubError("Receive failed: {}".format(e)) self.close(exception=error) raise error
def _handle_redirect(self, redirects): if len(redirects) != len(self.clients): raise EventHubError( "Some clients are attempting to redirect the connection.") if not all(r.hostname == redirects[0].hostname for r in redirects): raise EventHubError( "Multiple clients attempting to redirect to different hosts.") self.auth = self._create_auth(redirects[0].address.decode('utf-8'), **self._auth_config) self.connection.redirect(redirects[0], self.auth) for client in self.clients: client.open(self.connection)
def _reconnect(self): # pylint: disable=protected-access self._handler.close() unsent_events = self._handler.pending_messages self._handler = SendClient(self.target, auth=self.client.get_auth(), debug=self.client.debug, msg_timeout=self.timeout, error_policy=self.retry_policy, keep_alive_interval=self.keep_alive, client_name=self.name, properties=self.client.create_properties()) try: self._handler.open() self._handler.queue_message(*unsent_events) self._handler.wait() return True except errors.TokenExpired as shutdown: log.info("Sender disconnected due to token expiry. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except (errors.LinkDetach, errors.ConnectionClose) as shutdown: if shutdown.action.retry and self.auto_reconnect: log.info("Sender detached. Attempting reconnect.") return False log.info("Sender reconnect failed. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except errors.MessageHandlerError as shutdown: if self.auto_reconnect: log.info("Sender detached. Attempting reconnect.") return False log.info("Sender reconnect failed. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except errors.AMQPConnectionError as shutdown: if str(shutdown).startswith("Unable to open authentication session" ) and self.auto_reconnect: log.info("Sender couldn't authenticate. Attempting reconnect.") return False log.info("Sender connection error (%r). Shutting down.", shutdown) error = EventHubError(str(shutdown)) self.close(exception=error) raise error except Exception as e: log.info("Unexpected error occurred (%r). Shutting down.", e) error = EventHubError("Sender Reconnect failed: {}".format(e)) self.close(exception=error) raise error
def run(self): """ Run the EventHubClient in blocking mode. Opens the connection and starts running all Sender/Receiver clients. Returns a list of the start up results. For a succcesful client start the result will be `None`, otherwise the exception raised. If all clients failed to start, then run will fail, shut down the connection and raise an exception. If at least one client starts up successfully the run command will succeed. :rtype: list[~azure.eventhub.common.EventHubError] """ log.info("%r: Starting %r clients", self.container_id, len(self.clients)) try: self._start_clients() redirects = [c.redirected for c in self.clients if c.redirected] failed = [c.error for c in self.clients if c.error] if failed and len(failed) == len(self.clients): log.warning("%r: All clients failed to start.", self.container_id) raise failed[0] if failed: log.warning("%r: %r clients failed to start.", self.container_id, len(failed)) elif redirects: self._handle_redirect(redirects) except EventHubError: self.stop() raise except Exception as e: self.stop() raise EventHubError(str(e)) return failed
def reconnect(self): """If the Receiver was disconnected from the service with a retryable error - attempt to reconnect.""" # pylint: disable=protected-access alt_creds = { "username": self.client._auth_config.get("iot_username"), "password": self.client._auth_config.get("iot_password") } self._handler.close() source = Source(self.source) if self.offset is not None: source.set_filter(self.offset.selector()) self._handler = ReceiveClient( source, auth=self.client.get_auth(**alt_creds), debug=self.client.debug, prefetch=self.prefetch, link_properties=self.properties, timeout=self.timeout, error_policy=self.retry_policy, keep_alive_interval=self.keep_alive, client_name=self.name, properties=self.client.create_properties()) try: self._handler.open() while not self.has_started(): self._handler._connection.work() except (errors.LinkDetach, errors.ConnectionClose) as shutdown: if shutdown.action.retry and self.auto_reconnect: self.reconnect() else: error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except errors.MessageHandlerError as shutdown: if self.auto_reconnect: self.reconnect() else: error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except Exception as e: error = EventHubError("Receiver reconnect failed: {}".format(e)) self.close(exception=error) raise error
def wait(self): """ Wait until all transferred events have been sent. Example: .. literalinclude:: ../examples/test_examples_eventhub.py :start-after: [START eventhub_client_transfer] :end-before: [END eventhub_client_transfer] :language: python :dedent: 4 :caption: Wait until all transferred events have been sent. """ if self.error: raise self.error if not self.running: raise ValueError("Unable to send until client has been started.") try: self._handler.wait() except (errors.TokenExpired, errors.AuthenticationException): log.info("Sender disconnected due to token error. Attempting reconnect.") self.reconnect() except (errors.LinkDetach, errors.ConnectionClose) as shutdown: if shutdown.action.retry and self.auto_reconnect: log.info("Sender detached. Attempting reconnect.") self.reconnect() else: log.info("Sender detached. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except errors.MessageHandlerError as shutdown: if self.auto_reconnect: log.info("Sender detached. Attempting reconnect.") self.reconnect() else: log.info("Sender detached. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except Exception as e: log.info("Unexpected error occurred (%r).", e) raise EventHubError("Send failed: {}".format(e))
def wait(self): """ Wait until all transferred events have been sent. """ if self.error: raise self.error try: self._handler.wait() except Exception as e: raise EventHubError("Send failed: {}".format(e))
def close(self, exception=None): """ Close down the handler. If the handler has already closed, this will be a no op. An optional exception can be passed in to indicate that the handler was shutdown due to error. :param exception: An optional exception if the handler is closing due to an error. :type exception: Exception """ if self.error: return elif isinstance(exception, errors.LinkRedirect): self.redirected = exception elif isinstance(exception, EventHubError): self.error = exception elif exception: self.error = EventHubError(str(exception)) else: self.error = EventHubError("This receive handler is now closed.") self._handler.close()
def reconnect(self): """If the Sender was disconnected from the service with a retryable error - attempt to reconnect.""" # pylint: disable=protected-access self._handler.close() unsent_events = self._handler.pending_messages self._handler = SendClient(self.target, auth=self.client.get_auth(), debug=self.client.debug, msg_timeout=self.timeout, error_policy=self.retry_policy, keep_alive_interval=self.keep_alive, client_name=self.name, properties=self.client.create_properties()) try: self._handler.open() self._handler.queue_message(*unsent_events) self._handler.wait() except (errors.LinkDetach, errors.ConnectionClose) as shutdown: if shutdown.action.retry and self.auto_reconnect: self.reconnect() else: error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except errors.MessageHandlerError as shutdown: if self.auto_reconnect: self.reconnect() else: error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except Exception as e: error = EventHubError("Sender Reconnect failed: {}".format(e)) self.close(exception=error) raise error
def wait(self): """ Wait until all transferred events have been sent. """ if self.error: raise self.error if not self.running: raise ValueError("Unable to send until client has been started.") try: self._handler.wait() except (errors.TokenExpired, errors.AuthenticationException): log.info( "Sender disconnected due to token error. Attempting reconnect." ) self.reconnect() except (errors.LinkDetach, errors.ConnectionClose) as shutdown: if shutdown.action.retry and self.auto_reconnect: log.info("Sender detached. Attempting reconnect.") self.reconnect() else: log.info("Sender detached. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except errors.MessageHandlerError as shutdown: if self.auto_reconnect: log.info("Sender detached. Attempting reconnect.") self.reconnect() else: log.info("Sender detached. Shutting down.") error = EventHubError(str(shutdown), shutdown) self.close(exception=error) raise error except Exception as e: log.info("Unexpected error occurred (%r).", e) raise EventHubError("Send failed: {}".format(e))
def has_started(self): """ Whether the handler has completed all start up processes such as establishing the connection, session, link and authentication, and is not ready to process messages. :rtype: bool """ # pylint: disable=protected-access timeout = False auth_in_progress = False if self._handler._connection.cbs: timeout, auth_in_progress = self._handler._auth.handle_token() if timeout: raise EventHubError("Authorization timeout.") if auth_in_progress: return False if not self._handler._client_ready(): return False return True
def _error(outcome, condition): return None if outcome == constants.MessageSendResult.Ok else EventHubError( outcome, condition)