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] self._sys_properties = None # type: Optional[Dict[bytes, 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 """ 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[bytes, Any] """Metadata set by the Event Hubs Service associated with the event. An EventData could have some or all of the following meta data depending on the source of the event data. - b"x-opt-sequence-number" (int) - b"x-opt-offset" (bytes) - b"x-opt-partition-key" (bytes) - b"x-opt-enqueued-time" (int) - b"message-id" (bytes) - b"user-id" (bytes) - b"to" (bytes) - b"subject" (bytes) - b"reply-to" (bytes) - b"correlation-id" (bytes) - b"content-type" (bytes) - b"content-encoding" (bytes) - b"absolute-expiry-time" (int) - b"creation-time" (int) - b"group-id" (bytes) - b"group-sequence" (bytes) - b"reply-to-group-id" (bytes) :rtype: dict """ if self._sys_properties is None: self._sys_properties = {} if self.message.properties: for key, prop_name in _SYS_PROP_KEYS_TO_MSG_PROPERTIES: value = getattr(self.message.properties, prop_name, None) if value: self._sys_properties[key] = value self._sys_properties.update(self.message.annotations) return self._sys_properties @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 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): 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.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 _encode_message(self): return self.message.encode_message() @property def sequence_number(self): """The sequence number of the event. :rtype: int or long """ return self.message.annotations.get(PROP_SEQ_NUMBER, None) @property def offset(self): """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): """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): """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): """Application-defined properties on the event. :rtype: dict """ return self.message.application_properties @properties.setter def properties(self, value): """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): """Metadata set by the Event Hubs Service associated with the event :rtype: dict """ return self.message.annotations @property def body(self): """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'): """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 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 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)) @property def application_properties(self): # TODO: This method is for the purpose of livetest, because uamqp v.1.2.4 hasn't been released # The gather() in uamqp.message of v1.2.3 depends on application_properties attribute, # the livetest would all break if removing this property. # It should be removed after uamqp v.1.2.4 is released return self.properties def encode_message(self): # TODO: This method is for the purpose of livetest, because uamqp v.1.2.4 hasn't been released # The gather() in uamqp.message of v1.2.3 depends on encode_message method, # the livetest would all break if removing this method. # It should be removed after uamqp v.1.2.4 is released return self._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 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.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] """ 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 """ data = self.body try: return "".join(b.decode(encoding) for b in data) except TypeError: return str(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 ) if self.application_properties else dict() 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: enqueued_time_stamp = \ self.message.delivery_annotations.get(EventData.PROP_LAST_ENQUEUED_TIME_UTC, None) retrieval_time_stamp = \ self.message.delivery_annotations.get(EventData.PROP_RUNTIME_INFO_RETRIEVAL_TIME_UTC, None) 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": datetime.datetime.utcfromtimestamp( float(enqueued_time_stamp) / 1000) if enqueued_time_stamp else None, "retrieval_time": datetime.datetime.utcfromtimestamp( float(retrieval_time_stamp) / 1000) if retrieval_time_stamp else 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()
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.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 properties(self): """ Application defined properties on the message. :rtype: dict """ return self.message.application_properties @properties.setter def 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 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, 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 """ self._partition_key = types.AMQPSymbol(EventData.PROP_PARTITION_KEY) self._annotations = {} self._properties = {} self._header = MessageHeader() self._header.durable = True if batch: self.message = BatchMessage(data=batch, multi_messages=True) elif message: self.message = message self._annotations = message.annotations self._properties = message.application_properties self._header = message.header else: if isinstance(body, list) and body: 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) @property def sequence_number(self): """ The sequence number of the event data object. :returns: int """ return self._annotations.get(EventData.PROP_SEQ_NUMBER, None) @property def offset(self): """ The offset of the event data object. :returns: 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. :returns: 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. :returns: bytes """ return self._annotations.get(EventData.PROP_DEVICE_ID, None) @property def partition_key(self): """ The partition key of the event data object. :returns: 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 self.message.annotations = annotations self.message.header = self._header self._annotations = annotations @property def properties(self): """ Application defined properties on the message. :returns: dict """ return self._properties @properties.setter def properties(self, value): """ Application defined properties on the message. :param value: The application properties for the EventData. :type value: dict """ self._properties = value properties = dict(self._properties) self.message.application_properties = properties @property def body(self): """ The body of the event data object. :returns: bytes or generator[bytes] """ return self.message.get_data()