def __init__(self, body=None, batch=None, to_device=None, message=None): """ Initialize EventData. :param body: The data to send in a single message. :type body: str, bytes or list :param batch: A data generator to send batched messages. :type batch: Generator :param to_device: An IoT device to route to. :type to_device: str :param message: The received message. :type message: ~uamqp.message.Message """ self._partition_key = types.AMQPSymbol(EventData.PROP_PARTITION_KEY) self._annotations = {} self._app_properties = {} self.msg_properties = MessageProperties() if to_device: self.msg_properties.to = '/devices/{}/messages/devicebound'.format(to_device) if batch: self.message = BatchMessage(data=batch, multi_messages=True, properties=self.msg_properties) elif message: self.message = message self.msg_properties = message.properties self._annotations = message.annotations self._app_properties = message.application_properties else: if isinstance(body, list) and body: self.message = Message(body[0], properties=self.msg_properties) for more in body[1:]: self.message._body.append(more) # pylint: disable=protected-access elif body is None: raise ValueError("EventData cannot be None.") else: self.message = Message(body, properties=self.msg_properties)
class EventData(object): """The EventData class is a container for event content. :param body: The data to send in a single message. body can be type of str or bytes. :type body: str or bytes .. admonition:: Example: .. literalinclude:: ../samples/sync_samples/sample_code_eventhub.py :start-after: [START create_event_data] :end-before: [END create_event_data] :language: python :dedent: 4 :caption: Create instances of EventData """ def __init__(self, body=None): # type: (Union[str, bytes, List[AnyStr]]) -> None self._last_enqueued_event_properties = {} # type: Dict[str, Any] if body and isinstance(body, list): self.message = Message(body[0]) for more in body[1:]: self.message._body.append(more) # pylint: disable=protected-access elif body is None: raise ValueError("EventData cannot be None.") else: self.message = Message(body) self.message.annotations = {} self.message.application_properties = {} def __repr__(self): # type: () -> str # pylint: disable=bare-except try: body_str = self.body_as_str() except: body_str = "<read-error>" event_repr = "body='{}'".format(body_str) try: event_repr += ", properties={}".format(self.properties) except: event_repr += ", properties=<read-error>" try: event_repr += ", offset={}".format(self.offset) except: event_repr += ", offset=<read-error>" try: event_repr += ", sequence_number={}".format(self.sequence_number) except: event_repr += ", sequence_number=<read-error>" try: event_repr += ", partition_key={!r}".format(self.partition_key) except: event_repr += ", partition_key=<read-error>" try: event_repr += ", enqueued_time={!r}".format(self.enqueued_time) except: event_repr += ", enqueued_time=<read-error>" return "EventData({})".format(event_repr) def __str__(self): # type: () -> str try: body_str = self.body_as_str() except: # pylint: disable=bare-except body_str = "<read-error>" event_str = "{{ body: '{}'".format(body_str) try: event_str += ", properties: {}".format(self.properties) if self.offset: event_str += ", offset: {}".format(self.offset) if self.sequence_number: event_str += ", sequence_number: {}".format( self.sequence_number) if self.partition_key: event_str += ", partition_key={!r}".format(self.partition_key) if self.enqueued_time: event_str += ", enqueued_time={!r}".format(self.enqueued_time) except: # pylint: disable=bare-except pass event_str += " }" return event_str @classmethod def _from_message(cls, message): # type: (Message) -> EventData """Internal use only. Creates an EventData object from a raw uamqp message. :param ~uamqp.Message message: A received uamqp message. :rtype: ~azure.eventhub.EventData """ event_data = cls(body="") event_data.message = message return event_data def _encode_message(self): # type: () -> bytes return self.message.encode_message() @property def sequence_number(self): # type: () -> Optional[int] """The sequence number of the event. :rtype: int or long """ return self.message.annotations.get(PROP_SEQ_NUMBER, None) @property def offset(self): # type: () -> Optional[str] """The offset of the event. :rtype: str """ try: return self.message.annotations[PROP_OFFSET].decode("UTF-8") except (KeyError, AttributeError): return None @property def enqueued_time(self): # type: () -> Optional[datetime.datetime] """The enqueued timestamp of the event. :rtype: datetime.datetime """ timestamp = self.message.annotations.get(PROP_TIMESTAMP, None) if timestamp: return utc_from_timestamp(float(timestamp) / 1000) return None @property def partition_key(self): # type: () -> Optional[bytes] """The partition key of the event. :rtype: bytes """ try: return self.message.annotations[PROP_PARTITION_KEY_AMQP_SYMBOL] except KeyError: return self.message.annotations.get(PROP_PARTITION_KEY, None) @property def properties(self): # type: () -> Dict[Union[str, bytes], Any] """Application-defined properties on the event. :rtype: dict """ return self.message.application_properties @properties.setter def properties(self, value): # type: (Dict[Union[str, bytes], Any]) -> None """Application-defined properties on the event. :param dict value: The application properties for the EventData. """ properties = None if value is None else dict(value) self.message.application_properties = properties @property def system_properties(self): # type: () -> Dict[Union[str, bytes], Any] """Metadata set by the Event Hubs Service associated with the event :rtype: dict """ return self.message.annotations @property def body(self): # type: () -> Union[bytes, Iterable[bytes]] """The content of the event. :rtype: bytes or Generator[bytes] """ try: return self.message.get_data() except TypeError: raise ValueError("Event content empty.") def body_as_str(self, encoding="UTF-8"): # type: (str) -> str """The content of the event as a string, if the data is of a compatible type. :param encoding: The encoding to use for decoding event data. Default is 'UTF-8' :rtype: str """ data = self.body try: return "".join( b.decode(encoding) for b in cast(Iterable[bytes], data)) except TypeError: return six.text_type(data) except: # pylint: disable=bare-except pass try: return cast(bytes, data).decode(encoding) except Exception as e: raise TypeError( "Message data is not compatible with string type: {}".format( e)) def body_as_json(self, encoding="UTF-8"): # type: (str) -> Dict[str, Any] """The content of the event loaded as a JSON object, if the data is compatible. :param encoding: The encoding to use for decoding event data. Default is 'UTF-8' :rtype: dict """ data_str = self.body_as_str(encoding=encoding) try: return json.loads(data_str) except Exception as e: raise TypeError( "Event data is not compatible with JSON type: {}".format(e))
class EventData(object): """ The EventData class is a holder of event content. :param body: The data to send in a single message. body can be type of str or bytes. :type body: str or bytes .. admonition:: Example: .. literalinclude:: ../samples/sync_samples/sample_code_eventhub.py :start-after: [START create_event_data] :end-before: [END create_event_data] :language: python :dedent: 4 :caption: Create instances of EventData """ def __init__(self, body=None): self._last_enqueued_event_properties = {} if body and isinstance(body, list): self.message = Message(body[0]) for more in body[1:]: self.message._body.append(more) # pylint: disable=protected-access elif body is None: raise ValueError("EventData cannot be None.") else: self.message = Message(body) self.message.annotations = {} self.message.application_properties = {} def __str__(self): try: body = self.body_as_str() except: # pylint: disable=bare-except body = "<read-error>" message_as_dict = { 'body': body, 'application_properties': str(self.application_properties) } try: if self.sequence_number: message_as_dict['sequence_number'] = str(self.sequence_number) if self.offset: message_as_dict['offset'] = str(self.offset) if self.enqueued_time: message_as_dict['enqueued_time'] = str(self.enqueued_time) if self.partition_key: message_as_dict['partition_key'] = str(self.partition_key) except: # pylint: disable=bare-except pass return str(message_as_dict) @classmethod def _from_message(cls, message): """Internal use only. Creates an EventData object from a raw uamqp message. :param ~uamqp.Message message: A received uamqp message. :rtype: ~azure.eventhub.EventData """ event_data = cls(body='') event_data.message = message return event_data def _get_last_enqueued_event_properties(self): """Extracts the last enqueued event in from the received event delivery annotations. :rtype: Dict[str, Any] """ if self._last_enqueued_event_properties: return self._last_enqueued_event_properties if self.message.delivery_annotations: sequence_number = self.message.delivery_annotations.get( PROP_LAST_ENQUEUED_SEQUENCE_NUMBER, None) enqueued_time_stamp = self.message.delivery_annotations.get( PROP_LAST_ENQUEUED_TIME_UTC, None) if enqueued_time_stamp: enqueued_time_stamp = utc_from_timestamp( float(enqueued_time_stamp) / 1000) retrieval_time_stamp = self.message.delivery_annotations.get( PROP_RUNTIME_INFO_RETRIEVAL_TIME_UTC, None) if retrieval_time_stamp: retrieval_time_stamp = utc_from_timestamp( float(retrieval_time_stamp) / 1000) offset_bytes = self.message.delivery_annotations.get( PROP_LAST_ENQUEUED_OFFSET, None) offset = offset_bytes.decode('UTF-8') if offset_bytes else None self._last_enqueued_event_properties = { "sequence_number": sequence_number, "offset": offset, "enqueued_time": enqueued_time_stamp, "retrieval_time": retrieval_time_stamp } return self._last_enqueued_event_properties return None @property def sequence_number(self): """ The sequence number of the event data object. :rtype: int or long """ return self.message.annotations.get(PROP_SEQ_NUMBER, None) @property def offset(self): """ The offset of the event data object. :rtype: str """ try: return self.message.annotations[PROP_OFFSET].decode('UTF-8') except (KeyError, AttributeError): return None @property def enqueued_time(self): """ The enqueued timestamp of the event data object. :rtype: datetime.datetime """ timestamp = self.message.annotations.get(PROP_TIMESTAMP, None) if timestamp: return utc_from_timestamp(float(timestamp) / 1000) return None @property def partition_key(self): """ The partition key of the event data object. :rtype: bytes """ try: return self.message.annotations[PROP_PARTITION_KEY_AMQP_SYMBOL] except KeyError: return self.message.annotations.get(PROP_PARTITION_KEY, None) @property def application_properties(self): """ Application defined properties on the message. :rtype: dict """ return self.message.application_properties @application_properties.setter def application_properties(self, value): """ Application defined properties on the message. :param dict value: The application properties for the EventData. """ properties = None if value is None else dict(value) self.message.application_properties = properties @property def system_properties(self): """ Metadata set by the Event Hubs Service associated with the EventData :rtype: dict """ return self.message.annotations @property def body(self): """ The body of the event data object. :rtype: bytes or Generator[bytes] """ try: return self.message.get_data() except TypeError: raise ValueError("Message data empty.") @property def last_enqueued_event_properties(self): """ The latest enqueued event information. This property will be updated each time an event is received when the receiver is created with `track_last_enqueued_event_properties` being `True`. The dict includes following information of the partition: - `sequence_number` - `offset` - `enqueued_time` - `retrieval_time` :rtype: dict or None """ return self._get_last_enqueued_event_properties() def body_as_str(self, encoding='UTF-8'): """ The body of the event data as a string if the data is of a compatible type. :param encoding: The encoding to use for decoding message data. Default is 'UTF-8' :rtype: str """ data = self.body try: return "".join(b.decode(encoding) for b in data) except TypeError: return six.text_type(data) except: # pylint: disable=bare-except pass try: return data.decode(encoding) except Exception as e: raise TypeError( "Message data is not compatible with string type: {}".format( e)) def body_as_json(self, encoding='UTF-8'): """ The body of the event loaded as a JSON object is the data is compatible. :param encoding: The encoding to use for decoding message data. Default is 'UTF-8' :rtype: dict """ data_str = self.body_as_str(encoding=encoding) try: return json.loads(data_str) except Exception as e: raise TypeError( "Event data is not compatible with JSON type: {}".format(e)) def encode_message(self): return self.message.encode_message()
class EventData(object): """ The EventData class is a holder of event content. Acts as a wrapper to an uamqp.message.Message object. Example: .. literalinclude:: ../examples/test_examples_eventhub.py :start-after: [START create_event_data] :end-before: [END create_event_data] :language: python :dedent: 4 :caption: Create instances of EventData """ PROP_SEQ_NUMBER = b"x-opt-sequence-number" PROP_OFFSET = b"x-opt-offset" PROP_PARTITION_KEY = b"x-opt-partition-key" PROP_TIMESTAMP = b"x-opt-enqueued-time" PROP_DEVICE_ID = b"iothub-connection-device-id" def __init__(self, body=None, batch=None, to_device=None, message=None): """ Initialize EventData. :param body: The data to send in a single message. :type body: str, bytes or list :param batch: A data generator to send batched messages. :type batch: Generator :param to_device: An IoT device to route to. :type to_device: str :param message: The received message. :type message: ~uamqp.message.Message """ self._partition_key = types.AMQPSymbol(EventData.PROP_PARTITION_KEY) self._annotations = {} self._app_properties = {} self.msg_properties = MessageProperties() if to_device: self.msg_properties.to = '/devices/{}/messages/devicebound'.format(to_device) if batch: self.message = BatchMessage(data=batch, multi_messages=True, properties=self.msg_properties) elif message: self.message = message self.msg_properties = message.properties self._annotations = message.annotations self._app_properties = message.application_properties else: if isinstance(body, list) and body: self.message = Message(body[0], properties=self.msg_properties) for more in body[1:]: self.message._body.append(more) # pylint: disable=protected-access elif body is None: raise ValueError("EventData cannot be None.") else: self.message = Message(body, properties=self.msg_properties) @property def sequence_number(self): """ The sequence number of the event data object. :rtype: int or long """ return self._annotations.get(EventData.PROP_SEQ_NUMBER, None) @property def offset(self): """ The offset of the event data object. :rtype: ~azure.eventhub.common.Offset """ try: return Offset(self._annotations[EventData.PROP_OFFSET].decode('UTF-8')) except (KeyError, AttributeError): return None @property def enqueued_time(self): """ The enqueued timestamp of the event data object. :rtype: datetime.datetime """ timestamp = self._annotations.get(EventData.PROP_TIMESTAMP, None) if timestamp: return datetime.datetime.utcfromtimestamp(float(timestamp)/1000) return None @property def device_id(self): """ The device ID of the event data object. This is only used for IoT Hub implementations. :rtype: bytes """ return self._annotations.get(EventData.PROP_DEVICE_ID, None) @property def partition_key(self): """ The partition key of the event data object. :rtype: bytes """ try: return self._annotations[self._partition_key] except KeyError: return self._annotations.get(EventData.PROP_PARTITION_KEY, None) @partition_key.setter def partition_key(self, value): """ Set the partition key of the event data object. :param value: The partition key to set. :type value: str or bytes """ annotations = dict(self._annotations) annotations[self._partition_key] = value header = MessageHeader() header.durable = True self.message.annotations = annotations self.message.header = header self._annotations = annotations @property def application_properties(self): """ Application defined properties on the message. :rtype: dict """ return self._app_properties @application_properties.setter def application_properties(self, value): """ Application defined properties on the message. :param value: The application properties for the EventData. :type value: dict """ self._app_properties = value properties = dict(self._app_properties) self.message.application_properties = properties @property def body(self): """ The body of the event data object. :rtype: bytes or Generator[bytes] """ try: return self.message.get_data() except TypeError: raise ValueError("Message data empty.") def body_as_str(self, encoding='UTF-8'): """ The body of the event data as a string if the data is of a compatible type. :param encoding: The encoding to use for decoding message data. Default is 'UTF-8' :rtype: str or unicode """ data = self.body try: return "".join(b.decode(encoding) for b in data) except TypeError: return six.text_type(data) except: # pylint: disable=bare-except pass try: return data.decode(encoding) except Exception as e: raise TypeError("Message data is not compatible with string type: {}".format(e)) def body_as_json(self, encoding='UTF-8'): """ The body of the event loaded as a JSON object is the data is compatible. :param encoding: The encoding to use for decoding message data. Default is 'UTF-8' :rtype: dict """ data_str = self.body_as_str(encoding=encoding) try: return json.loads(data_str) except Exception as e: raise TypeError("Event data is not compatible with JSON type: {}".format(e))
class EventData(object): """ The EventData class is a holder of event content. Example: .. literalinclude:: ../examples/test_examples_eventhub.py :start-after: [START create_event_data] :end-before: [END create_event_data] :language: python :dedent: 4 :caption: Create instances of EventData """ PROP_SEQ_NUMBER = b"x-opt-sequence-number" PROP_OFFSET = b"x-opt-offset" PROP_PARTITION_KEY = b"x-opt-partition-key" PROP_PARTITION_KEY_AMQP_SYMBOL = types.AMQPSymbol(PROP_PARTITION_KEY) PROP_TIMESTAMP = b"x-opt-enqueued-time" PROP_DEVICE_ID = b"iothub-connection-device-id" def __init__(self, body=None, to_device=None): """ Initialize EventData. :param body: The data to send in a single message. :type body: str, bytes or list :param to_device: An IoT device to route to. :type to_device: str """ self._annotations = {} self._app_properties = {} self._msg_properties = MessageProperties() if to_device: self._msg_properties.to = '/devices/{}/messages/devicebound'.format( to_device) if body and isinstance(body, list): self.message = Message(body[0], properties=self._msg_properties) for more in body[1:]: self.message._body.append(more) # pylint: disable=protected-access elif body is None: raise ValueError("EventData cannot be None.") else: self.message = Message(body, properties=self._msg_properties) def __str__(self): dic = { 'body': self.body_as_str(), 'application_properties': str(self.application_properties) } if self.sequence_number: dic['sequence_number'] = str(self.sequence_number) if self.offset: dic['offset'] = str(self.offset) if self.enqueued_time: dic['enqueued_time'] = str(self.enqueued_time) if self.device_id: dic['device_id'] = str(self.device_id) if self.partition_key: dic['partition_key'] = str(self.partition_key) return str(dic) def _set_partition_key(self, value): """ Set the partition key of the event data object. :param value: The partition key to set. :type value: str or bytes """ annotations = dict(self._annotations) annotations[EventData.PROP_PARTITION_KEY_AMQP_SYMBOL] = value header = MessageHeader() header.durable = True self.message.annotations = annotations self.message.header = header self._annotations = annotations @staticmethod def _from_message(message): event_data = EventData(body='') event_data.message = message event_data._msg_properties = message.properties # pylint:disable=protected-access event_data._annotations = message.annotations # pylint:disable=protected-access event_data._app_properties = message.application_properties # pylint:disable=protected-access return event_data @property def sequence_number(self): """ The sequence number of the event data object. :rtype: int or long """ return self._annotations.get(EventData.PROP_SEQ_NUMBER, None) @property def offset(self): """ The offset of the event data object. :rtype: str """ try: return self._annotations[EventData.PROP_OFFSET].decode('UTF-8') except (KeyError, AttributeError): return None @property def enqueued_time(self): """ The enqueued timestamp of the event data object. :rtype: datetime.datetime """ timestamp = self._annotations.get(EventData.PROP_TIMESTAMP, None) if timestamp: return datetime.datetime.utcfromtimestamp(float(timestamp) / 1000) return None @property def device_id(self): """ The device ID of the event data object. This is only used for IoT Hub implementations. :rtype: bytes """ return self._annotations.get(EventData.PROP_DEVICE_ID, None) @property def partition_key(self): """ The partition key of the event data object. :rtype: bytes """ try: return self._annotations[EventData.PROP_PARTITION_KEY_AMQP_SYMBOL] except KeyError: return self._annotations.get(EventData.PROP_PARTITION_KEY, None) @property def application_properties(self): """ Application defined properties on the message. :rtype: dict """ return self._app_properties @application_properties.setter def application_properties(self, value): """ Application defined properties on the message. :param value: The application properties for the EventData. :type value: dict """ self._app_properties = value properties = None if value is None else dict(self._app_properties) self.message.application_properties = properties @property def system_properties(self): """ Metadata set by the Event Hubs Service associated with the EventData :rtype: dict """ return self._annotations @property def body(self): """ The body of the event data object. :rtype: bytes or Generator[bytes] """ try: return self.message.get_data() except TypeError: raise ValueError("Message data empty.") def body_as_str(self, encoding='UTF-8'): """ The body of the event data as a string if the data is of a compatible type. :param encoding: The encoding to use for decoding message data. Default is 'UTF-8' :rtype: str or unicode """ data = self.body try: return "".join(b.decode(encoding) for b in data) except TypeError: return six.text_type(data) except: # pylint: disable=bare-except pass try: return data.decode(encoding) except Exception as e: raise TypeError( "Message data is not compatible with string type: {}".format( e)) def body_as_json(self, encoding='UTF-8'): """ The body of the event loaded as a JSON object is the data is compatible. :param encoding: The encoding to use for decoding message data. Default is 'UTF-8' :rtype: dict """ data_str = self.body_as_str(encoding=encoding) try: return json.loads(data_str) except Exception as e: raise TypeError( "Event data is not compatible with JSON type: {}".format(e)) def encode_message(self): return self.message.encode_message()
class EventData(object): """ The EventData class is a holder of event content. Acts as a wrapper to an ~uamqp.message.Message object. """ PROP_SEQ_NUMBER = b"x-opt-sequence-number" PROP_OFFSET = b"x-opt-offset" PROP_PARTITION_KEY = b"x-opt-partition-key" PROP_TIMESTAMP = b"x-opt-enqueued-time" PROP_DEVICE_ID = b"iothub-connection-device-id" def __init__(self, body=None, batch=None, to_device=None, message=None): """ Initialize EventData. :param body: The data to send in a single message. :type body: str, bytes or list :param batch: A data generator to send batched messages. :type batch: Generator :param message: The received message. :type message: ~uamqp.message.Message """ self._partition_key = types.AMQPSymbol(EventData.PROP_PARTITION_KEY) self._annotations = {} self._app_properties = {} self.msg_properties = MessageProperties() if to_device: self.msg_properties.to = '/devices/{}/messages/devicebound'.format( to_device) if batch: self.message = BatchMessage(data=batch, multi_messages=True, properties=self.msg_properties) elif message: self.message = message self.msg_properties = message.properties self._annotations = message.annotations self._app_properties = message.application_properties else: if isinstance(body, list) and body: self.message = Message(body[0], properties=self.msg_properties) for more in body[1:]: self.message._body.append(more) # pylint: disable=protected-access elif body is None: raise ValueError("EventData cannot be None.") else: self.message = Message(body, properties=self.msg_properties) @property def sequence_number(self): """ The sequence number of the event data object. :rtype: int """ return self._annotations.get(EventData.PROP_SEQ_NUMBER, None) @property def offset(self): """ The offset of the event data object. :rtype: int """ try: return self._annotations[EventData.PROP_OFFSET].decode('UTF-8') except (KeyError, AttributeError): return None @property def enqueued_time(self): """ The enqueued timestamp of the event data object. :rtype: datetime.datetime """ timestamp = self._annotations.get(EventData.PROP_TIMESTAMP, None) if timestamp: return datetime.datetime.fromtimestamp(float(timestamp) / 1000) return None @property def device_id(self): """ The device ID of the event data object. This is only used for IoT Hub implementations. :rtype: bytes """ return self._annotations.get(EventData.PROP_DEVICE_ID, None) @property def partition_key(self): """ The partition key of the event data object. :rtype: bytes """ try: return self._annotations[self._partition_key] except KeyError: return self._annotations.get(EventData.PROP_PARTITION_KEY, None) @partition_key.setter def partition_key(self, value): """ Set the partition key of the event data object. :param value: The partition key to set. :type value: str or bytes """ annotations = dict(self._annotations) annotations[self._partition_key] = value header = MessageHeader() header.durable = True self.message.annotations = annotations self.message.header = header self._annotations = annotations @property def application_properties(self): """ Application defined properties on the message. :rtype: dict """ return self._app_properties @application_properties.setter def application_properties(self, value): """ Application defined properties on the message. :param value: The application properties for the EventData. :type value: dict """ self._app_properties = value properties = dict(self._app_properties) self.message.application_properties = properties @property def body(self): """ The body of the event data object. :rtype: bytes or generator[bytes] """ return self.message.get_data()
async def _management_request_async(self, mgmt_msg: Message, op_type: bytes) -> Any: retried_times = 0 last_exception = None while retried_times <= self._config.max_retries: mgmt_auth = await self._create_auth_async() mgmt_client = AMQPClientAsync( self._mgmt_target, auth=mgmt_auth, debug=self._config.network_tracing ) try: conn = await self._conn_manager_async.get_connection( self._address.hostname, mgmt_auth ) mgmt_msg.application_properties["security_token"] = mgmt_auth.token await mgmt_client.open_async(connection=conn) response = await mgmt_client.mgmt_request_async( mgmt_msg, constants.READ_OPERATION, op_type=op_type, status_code_field=MGMT_STATUS_CODE, description_fields=MGMT_STATUS_DESC, ) status_code = int(response.application_properties[MGMT_STATUS_CODE]) description = response.application_properties.get( MGMT_STATUS_DESC ) # type: Optional[Union[str, bytes]] if description and isinstance(description, six.binary_type): description = description.decode("utf-8") if status_code < 400: return response if status_code in [401]: raise errors.AuthenticationException( "Management authentication failed. Status code: {}, Description: {!r}".format( status_code, description ) ) if status_code in [404]: raise ConnectError( "Management connection failed. Status code: {}, Description: {!r}".format( status_code, description ) ) raise errors.AMQPConnectionError( "Management request error. Status code: {}, Description: {!r}".format( status_code, description ) ) except asyncio.CancelledError: # pylint: disable=try-except-raise raise except Exception as exception: # pylint:disable=broad-except last_exception = await _handle_exception(exception, self) await self._backoff_async( retried_times=retried_times, last_exception=last_exception ) retried_times += 1 if retried_times > self._config.max_retries: _LOGGER.info( "%r returns an exception %r", self._container_id, last_exception ) raise last_exception finally: await mgmt_client.close_async()
class EventData(object): """ The EventData class is a holder of event content. Acts as a wrapper to an uamqp.message.Message object. Example: .. literalinclude:: ../examples/test_examples_eventhub.py :start-after: [START create_event_data] :end-before: [END create_event_data] :language: python :dedent: 4 :caption: Create instances of EventData """ PROP_SEQ_NUMBER = b"x-opt-sequence-number" PROP_OFFSET = b"x-opt-offset" PROP_PARTITION_KEY = b"x-opt-partition-key" PROP_TIMESTAMP = b"x-opt-enqueued-time" PROP_DEVICE_ID = b"iothub-connection-device-id" def __init__(self, body=None, batch=None, to_device=None, message=None): """ Initialize EventData. :param body: The data to send in a single message. :type body: str, bytes or list :param batch: A data generator to send batched messages. :type batch: Generator :param to_device: An IoT device to route to. :type to_device: str :param message: The received message. :type message: ~uamqp.message.Message """ self._partition_key = types.AMQPSymbol(EventData.PROP_PARTITION_KEY) self._annotations = {} self._app_properties = {} self.msg_properties = MessageProperties() if to_device: self.msg_properties.to = '/devices/{}/messages/devicebound'.format( to_device) if batch: self.message = BatchMessage(data=batch, multi_messages=True, properties=self.msg_properties) elif message: self.message = message self.msg_properties = message.properties self._annotations = message.annotations self._app_properties = message.application_properties else: if isinstance(body, list) and body: self.message = Message(body[0], properties=self.msg_properties) for more in body[1:]: self.message._body.append(more) # pylint: disable=protected-access elif body is None: raise ValueError("EventData cannot be None.") else: self.message = Message(body, properties=self.msg_properties) @property def sequence_number(self): """ The sequence number of the event data object. :rtype: int or long """ return self._annotations.get(EventData.PROP_SEQ_NUMBER, None) @property def offset(self): """ The offset of the event data object. :rtype: ~azure.eventhub.common.Offset """ try: return Offset( self._annotations[EventData.PROP_OFFSET].decode('UTF-8')) except (KeyError, AttributeError): return None @property def enqueued_time(self): """ The enqueued timestamp of the event data object. :rtype: datetime.datetime """ timestamp = self._annotations.get(EventData.PROP_TIMESTAMP, None) if timestamp: return datetime.datetime.utcfromtimestamp(float(timestamp) / 1000) return None @property def device_id(self): """ The device ID of the event data object. This is only used for IoT Hub implementations. :rtype: bytes """ return self._annotations.get(EventData.PROP_DEVICE_ID, None) @property def partition_key(self): """ The partition key of the event data object. :rtype: bytes """ try: return self._annotations[self._partition_key] except KeyError: return self._annotations.get(EventData.PROP_PARTITION_KEY, None) @partition_key.setter def partition_key(self, value): """ Set the partition key of the event data object. :param value: The partition key to set. :type value: str or bytes """ annotations = dict(self._annotations) annotations[self._partition_key] = value header = MessageHeader() header.durable = True self.message.annotations = annotations self.message.header = header self._annotations = annotations @property def application_properties(self): """ Application defined properties on the message. :rtype: dict """ return self._app_properties @application_properties.setter def application_properties(self, value): """ Application defined properties on the message. :param value: The application properties for the EventData. :type value: dict """ self._app_properties = value properties = dict(self._app_properties) self.message.application_properties = properties @property def body(self): """ The body of the event data object. :rtype: bytes or Generator[bytes] """ try: return self.message.get_data() except TypeError: raise ValueError("Message data empty.") def body_as_str(self, encoding='UTF-8'): """ The body of the event data as a string if the data is of a compatible type. :param encoding: The encoding to use for decoding message data. Default is 'UTF-8' :rtype: str or unicode """ data = self.body try: return "".join(b.decode(encoding) for b in data) except TypeError: return six.text_type(data) except: # pylint: disable=bare-except pass try: return data.decode(encoding) except Exception as e: raise TypeError( "Message data is not compatible with string type: {}".format( e)) def body_as_json(self, encoding='UTF-8'): """ The body of the event loaded as a JSON object is the data is compatible. :param encoding: The encoding to use for decoding message data. Default is 'UTF-8' :rtype: dict """ data_str = self.body_as_str(encoding=encoding) try: return json.loads(data_str) except Exception as e: raise TypeError( "Event data is not compatible with JSON type: {}".format(e))
class EventData(object): """ The EventData class is a holder of event content. Example: .. literalinclude:: ../examples/test_examples_eventhub.py :start-after: [START create_event_data] :end-before: [END create_event_data] :language: python :dedent: 4 :caption: Create instances of EventData """ PROP_SEQ_NUMBER = b"x-opt-sequence-number" PROP_OFFSET = b"x-opt-offset" PROP_PARTITION_KEY = b"x-opt-partition-key" PROP_PARTITION_KEY_AMQP_SYMBOL = types.AMQPSymbol(PROP_PARTITION_KEY) PROP_TIMESTAMP = b"x-opt-enqueued-time" PROP_LAST_ENQUEUED_SEQUENCE_NUMBER = b"last_enqueued_sequence_number" PROP_LAST_ENQUEUED_OFFSET = b"last_enqueued_offset" PROP_LAST_ENQUEUED_TIME_UTC = b"last_enqueued_time_utc" PROP_RUNTIME_INFO_RETRIEVAL_TIME_UTC = b"runtime_info_retrieval_time_utc" def __init__(self, body=None): """ Initialize EventData. :param body: The data to send in a single message. :type body: str, bytes or list """ self._last_enqueued_event_properties = {} if body and isinstance(body, list): self.message = Message(body[0]) for more in body[1:]: self.message._body.append(more) # pylint: disable=protected-access elif body is None: raise ValueError("EventData cannot be None.") else: self.message = Message(body) self.message.annotations = {} def __str__(self): dic = { 'body': self.body_as_str(), 'application_properties': str(self.application_properties) } if self.sequence_number: dic['sequence_number'] = str(self.sequence_number) if self.offset: dic['offset'] = str(self.offset) if self.enqueued_time: dic['enqueued_time'] = str(self.enqueued_time) if self.partition_key: dic['partition_key'] = str(self.partition_key) return str(dic) def _set_partition_key(self, value): """ Set the partition key of the event data object. :param value: The partition key to set. :type value: str or bytes """ annotations = dict(self.message.annotations) annotations[EventData.PROP_PARTITION_KEY_AMQP_SYMBOL] = value header = MessageHeader() header.durable = True self.message.annotations = annotations self.message.header = header def _trace_message(self, parent_span=None): """Add tracing information to this message. Will open and close a "Azure.EventHubs.message" span, and add the "DiagnosticId" as app properties of the message. """ span_impl_type = settings.tracing_implementation( ) # type: Type[AbstractSpan] if span_impl_type is not None: current_span = parent_span or span_impl_type( span_impl_type.get_current_span()) message_span = current_span.span(name="Azure.EventHubs.message") message_span.start() app_prop = dict(self.application_properties) app_prop.setdefault( b"Diagnostic-Id", message_span.get_trace_parent().encode('ascii')) self.application_properties = app_prop message_span.finish() def _trace_link_message(self, parent_span=None): """Link the current message to current span. Will extract DiagnosticId if available. """ span_impl_type = settings.tracing_implementation( ) # type: Type[AbstractSpan] if span_impl_type is not None: current_span = parent_span or span_impl_type( span_impl_type.get_current_span()) if current_span and self.application_properties: traceparent = self.application_properties.get( b"Diagnostic-Id", "").decode('ascii') if traceparent: current_span.link(traceparent) def _get_last_enqueued_event_properties(self): if self._last_enqueued_event_properties: return self._last_enqueued_event_properties if self.message.delivery_annotations: self._last_enqueued_event_properties = { "sequence_number": self.message.delivery_annotations.get( EventData.PROP_LAST_ENQUEUED_SEQUENCE_NUMBER, None), "offset": self.message.delivery_annotations.get( EventData.PROP_LAST_ENQUEUED_OFFSET, None), "enqueued_time": self.message.delivery_annotations.get( EventData.PROP_LAST_ENQUEUED_TIME_UTC, None), "retrieval_time": self.message.delivery_annotations.get( EventData.PROP_RUNTIME_INFO_RETRIEVAL_TIME_UTC, None) } return self._last_enqueued_event_properties return None @classmethod def _from_message(cls, message): # pylint:disable=protected-access event_data = cls(body='') event_data.message = message return event_data @property def sequence_number(self): """ The sequence number of the event data object. :rtype: int or long """ return self.message.annotations.get(EventData.PROP_SEQ_NUMBER, None) @property def offset(self): """ The offset of the event data object. :rtype: str """ try: return self.message.annotations[EventData.PROP_OFFSET].decode( 'UTF-8') except (KeyError, AttributeError): return None @property def enqueued_time(self): """ The enqueued timestamp of the event data object. :rtype: datetime.datetime """ timestamp = self.message.annotations.get(EventData.PROP_TIMESTAMP, None) if timestamp: return datetime.datetime.utcfromtimestamp(float(timestamp) / 1000) return None @property def partition_key(self): """ The partition key of the event data object. :rtype: bytes """ try: return self.message.annotations[ EventData.PROP_PARTITION_KEY_AMQP_SYMBOL] except KeyError: return self.message.annotations.get(EventData.PROP_PARTITION_KEY, None) @property def application_properties(self): """ Application defined properties on the message. :rtype: dict """ return self.message.application_properties @application_properties.setter def application_properties(self, value): """ Application defined properties on the message. :param value: The application properties for the EventData. :type value: dict """ properties = None if value is None else dict(value) self.message.application_properties = properties @property def system_properties(self): """ Metadata set by the Event Hubs Service associated with the EventData :rtype: dict """ return self.message.annotations @property def body(self): """ The body of the event data object. :rtype: bytes or Generator[bytes] """ try: return self.message.get_data() except TypeError: raise ValueError("Message data empty.") def body_as_str(self, encoding='UTF-8'): """ The body of the event data as a string if the data is of a compatible type. :param encoding: The encoding to use for decoding message data. Default is 'UTF-8' :rtype: str or unicode """ data = self.body try: return "".join(b.decode(encoding) for b in data) except TypeError: return six.text_type(data) except: # pylint: disable=bare-except pass try: return data.decode(encoding) except Exception as e: raise TypeError( "Message data is not compatible with string type: {}".format( e)) def body_as_json(self, encoding='UTF-8'): """ The body of the event loaded as a JSON object is the data is compatible. :param encoding: The encoding to use for decoding message data. Default is 'UTF-8' :rtype: dict """ data_str = self.body_as_str(encoding=encoding) try: return json.loads(data_str) except Exception as e: raise TypeError( "Event data is not compatible with JSON type: {}".format(e)) def encode_message(self): return self.message.encode_message()