def _populate_message_attributes(self, c_message): if self.properties: c_message.properties = self.properties.get_properties_obj() if self.application_properties: if not isinstance(self.application_properties, dict): raise TypeError("Application properties must be a dictionary.") amqp_props = utils.data_factory(self.application_properties, encoding=self._encoding) c_message.application_properties = amqp_props if self.annotations: if not isinstance(self.annotations, dict): raise TypeError("Message annotations must be a dictionary.") ann_props = c_uamqp.create_message_annotations( utils.data_factory(self.annotations, encoding=self._encoding)) c_message.message_annotations = ann_props if self.delivery_annotations: if not isinstance(self.delivery_annotations, dict): raise TypeError("Delivery annotations must be a dictionary.") delivery_ann_props = c_uamqp.create_delivery_annotations( utils.data_factory(self.delivery_annotations, encoding=self._encoding)) c_message.delivery_annotations = delivery_ann_props if self.header: c_message.header = self.header.get_header_obj() if self.footer: if not isinstance(self.footer, dict): raise TypeError("Footer must be a dictionary.") footer = c_uamqp.create_footer( utils.data_factory(self.footer, encoding=self._encoding)) c_message.footer = footer
def _create_handler(self): source = Source(self._source) if self._offset is not None: source.set_filter(self._offset._selector()) # pylint:disable=protected-access if StrictVersion(uamqp.__version__) < StrictVersion("1.2.3"): # backward compatible until uamqp 1.2.3 released desired_capabilities = {} elif self._track_last_enqueued_event_properties: symbol_array = [types.AMQPSymbol(self._receiver_runtime_metric_symbol)] desired_capabilities = {"desired_capabilities": utils.data_factory(types.AMQPArray(symbol_array))} else: desired_capabilities = {"desired_capabilities": None} self._handler = ReceiveClientAsync( source, auth=self._client._create_auth(), # pylint:disable=protected-access debug=self._client._config.network_tracing, # pylint:disable=protected-access prefetch=self._prefetch, link_properties=self._link_properties, timeout=self._timeout, error_policy=self._retry_policy, keep_alive_interval=self._keep_alive, client_name=self._name, receive_settle_mode=uamqp.constants.ReceiverSettleMode.ReceiveAndDelete, auto_complete=False, properties=self._client._create_properties( # pylint:disable=protected-access self._client._config.user_agent), # pylint:disable=protected-access **desired_capabilities, # pylint:disable=protected-access loop=self._loop) self._messages_iter = None
def __init__(self, failed, undeliverable, annotations=None, encoding='UTF-8'): self.failed = failed self.undeliverable = undeliverable if annotations and not isinstance(annotations, dict): raise TypeError("Disposition annotations must be a dictionary.") self.annotations = utils.data_factory(annotations, encoding=encoding) if annotations else None super(MessageModified, self).__init__()
def footer(self, value): if value and not isinstance(value, dict): raise TypeError("Footer must be a dictionary") footer_props = c_uamqp.create_footer( utils.data_factory(value, encoding=self._encoding)) self._message.footer = footer_props self._footer = value
def set_filter(self, value, name=constants.STRING_FILTER, descriptor=constants.STRING_FILTER): """Set a filter on the endpoint. Only one filter can be applied to an endpoint. :param value: The filter to apply to the endpoint. Set to None for a NULL filter. :type value: bytes or str or None :param name: The name of the filter. This will be encoded as an AMQP Symbol. By default this is set to b'apache.org:selector-filter:string'. :type name: bytes :param descriptor: The descriptor used if the filter is to be encoded as a described value. This will be encoded as an AMQP Symbol. By default this is set to b'apache.org:selector-filter:string'. Set to None if the filter should not be encoded as a described value. :type descriptor: bytes or None """ value = value.encode(self._encoding) if isinstance( value, six.text_type) else value filter_set = c_uamqp.dict_value() filter_key = c_uamqp.symbol_value(name) filter_value = utils.data_factory(value, encoding=self._encoding) if value is not None and descriptor is not None: descriptor = c_uamqp.symbol_value(descriptor) filter_value = c_uamqp.described_value(descriptor, filter_value) filter_set[filter_key] = filter_value self._address.filter_set = filter_set
def _create_handler(self, auth: "JWTTokenAsync") -> None: source = Source(self._source) if self._offset is not None: source.set_filter( event_position_selector(self._offset, self._offset_inclusive)) desired_capabilities = None if self._track_last_enqueued_event_properties: symbol_array = [types.AMQPSymbol(RECEIVER_RUNTIME_METRIC_SYMBOL)] desired_capabilities = utils.data_factory( types.AMQPArray(symbol_array)) properties = create_properties(self._client._config.user_agent # pylint:disable=protected-access ) self._handler = ReceiveClientAsync( source, auth=auth, debug=self._client._config.network_tracing, # pylint:disable=protected-access prefetch=self._prefetch, link_properties=self._link_properties, timeout=self._timeout, idle_timeout=self._idle_timeout, error_policy=self._retry_policy, keep_alive_interval=self._keep_alive, client_name=self._name, receive_settle_mode=uamqp.constants.ReceiverSettleMode. ReceiveAndDelete, auto_complete=False, properties=properties, desired_capabilities=desired_capabilities, loop=self._loop, ) self._handler._streaming_receive = True # pylint:disable=protected-access self._handler._message_received_callback = ( # pylint:disable=protected-access self._message_received)
def test_event_hubs_client_receive_with_runtime_metric_sync(live_eventhub_config): uri = "sb://{}/{}".format(live_eventhub_config['hostname'], live_eventhub_config['event_hub']) sas_auth = authentication.SASTokenAuth.from_shared_access_key( uri, live_eventhub_config['key_name'], live_eventhub_config['access_key']) source = "amqps://{}/{}/ConsumerGroups/{}/Partitions/{}".format( live_eventhub_config['hostname'], live_eventhub_config['event_hub'], live_eventhub_config['consumer_group'], live_eventhub_config['partition']) receiver_runtime_metric_symbol = b'com.microsoft:enable-receiver-runtime-metric' symbol_array = [types.AMQPSymbol(receiver_runtime_metric_symbol)] desired_capabilities = utils.data_factory(types.AMQPArray(symbol_array)) with uamqp.ReceiveClient(source, auth=sas_auth, debug=False, timeout=50, prefetch=50, desired_capabilities=desired_capabilities) as receive_client: log.info("Created client, receiving...") with pytest.raises(ValueError): batch = receive_client.receive_message_batch(max_batch_size=100) batch = receive_client.receive_message_batch(max_batch_size=10) log.info("Got batch: {}".format(len(batch))) assert len(batch) <= 10 for message in batch: annotations = message.annotations delivery_annotations = message.delivery_annotations log.info("Sequence Number: {}".format(annotations.get(b'x-opt-sequence-number'))) assert b'last_enqueued_sequence_number' in delivery_annotations assert b'last_enqueued_offset' in delivery_annotations assert b'last_enqueued_time_utc' in delivery_annotations assert b'runtime_info_retrieval_time_utc' in delivery_annotations log.info("Finished receiving")
async def test_event_hubs_receive_with_runtime_metric_async( live_eventhub_config): uri = "sb://{}/{}".format(live_eventhub_config['hostname'], live_eventhub_config['event_hub']) sas_auth = authentication.SASTokenAsync.from_shared_access_key( uri, live_eventhub_config['key_name'], live_eventhub_config['access_key']) source = "amqps://{}/{}/ConsumerGroups/{}/Partitions/{}".format( live_eventhub_config['hostname'], live_eventhub_config['event_hub'], live_eventhub_config['consumer_group'], live_eventhub_config['partition']) receiver_runtime_metric_symbol = b'com.microsoft:enable-receiver-runtime-metric' symbol_array = [types.AMQPSymbol(receiver_runtime_metric_symbol)] desired_capabilities = utils.data_factory(types.AMQPArray(symbol_array)) async with uamqp.ReceiveClientAsync( source, debug=False, auth=sas_auth, timeout=1000, prefetch=10, desired_capabilities=desired_capabilities) as receive_client: message_batch = await receive_client.receive_message_batch_async(10) log.info("got batch: {}".format(len(message_batch))) for message in message_batch: annotations = message.annotations delivery_annotations = message.delivery_annotations log.info("Sequence Number: {}".format( annotations.get(b'x-opt-sequence-number'))) assert b'last_enqueued_sequence_number' in delivery_annotations assert b'last_enqueued_offset' in delivery_annotations assert b'last_enqueued_time_utc' in delivery_annotations assert b'runtime_info_retrieval_time_utc' in delivery_annotations
def _c_wrapper(self, value_array): value_type = type(value_array[0]) if not all(isinstance(x, value_type) for x in value_array): raise ValueError("All Array values must be the same type.") c_array = c_uamqp.array_value() for value in value_array: c_array.append(utils.data_factory(value)) return c_array
def user_id(self, value): if isinstance(value, six.text_type): value = value.encode(self._encoding) elif value is not None and not isinstance(value, six.binary_type): raise TypeError("user_id must be bytes or str.") # user_id is type of binary according to the spec. # convert byte string into bytearray then wrap the data into c_uamqp.BinaryValue. if value is not None: self._user_id = utils.data_factory(bytearray(value), encoding=self._encoding) else: self._user_id = None
def set(self, value): """Set a value as the message body. This can be any Python data type and it will be automatically encoded into an AMQP type. If a specific AMQP type is required, a `types.AMQPType` can be used. :param data: The data to send in the body. :type data: ~uamqp.types.AMQPType """ value = utils.data_factory(value) self._message.set_body_value(value)
def __init__(self, session, source, target, on_message_received, name=None, receive_settle_mode=constants.ReceiverSettleMode.PeekLock, send_settle_mode=constants.SenderSettleMode.Unsettled, max_message_size=constants.MAX_MESSAGE_LENGTH_BYTES, prefetch=300, properties=None, error_policy=None, debug=False, encoding='UTF-8', desired_capabilities=None): # pylint: disable=protected-access if name: self.name = name.encode(encoding) if isinstance( name, six.text_type) else name else: self.name = str(uuid.uuid4()).encode(encoding) target = target.encode(encoding) if isinstance( target, six.text_type) else target role = constants.Role.Receiver self.source = source._address.value self.target = c_uamqp.Messaging.create_target(target) self.on_message_received = on_message_received self.encoding = encoding self.error_policy = error_policy or errors.ErrorPolicy() self._settle_mode = receive_settle_mode self._conn = session._conn self._session = session self._link = c_uamqp.create_link(session._session, self.name, role.value, self.source, self.target) self._link.subscribe_to_detach_event(self) if prefetch: self._link.set_prefetch_count(prefetch) if properties: self._link.set_attach_properties( utils.data_factory(properties, encoding=encoding)) if receive_settle_mode: self.receive_settle_mode = receive_settle_mode if send_settle_mode: self.send_settle_mode = send_settle_mode if max_message_size: self.max_message_size = max_message_size if desired_capabilities: self._link.set_desired_capabilities(desired_capabilities) self._receiver = c_uamqp.create_message_receiver(self._link, self) self._receiver.set_trace(debug) self._state = constants.MessageReceiverState.Idle self._error = None
def append(self, value): """Append an item to the body. This can be any Python data type and it will be automatically encoded into an AMQP type. If a specific AMQP type is required, a `types.AMQPType` can be used. :param data: The data to append. :type data: ~uamqp.types.AMQPType """ value = utils.data_factory(value, encoding=self._encoding) self._message.add_body_sequence(value)
def append(self, data): """Append a sequence section to the body. The data should be a list of objects. The object in the list can be any Python data type and it will be automatically encoded into an AMQP type. If a specific AMQP type is required, a `types.AMQPType` can be used. :param data: The list of objects to append. :type data: list[~uamqp.types.AMQPType] """ data = utils.data_factory(data) self._message.add_body_sequence(data)
def get_message(self): """Get the underlying C message from this object. :returns: ~uamqp.c_uamqp.cMessage """ if not self._message: return None if self.properties: self._message.properties = self.properties._properties # pylint: disable=protected-access if self.application_properties: if not isinstance(self.application_properties, dict): raise TypeError("Application properties must be a dictionary.") amqp_props = utils.data_factory(self.application_properties, encoding=self._encoding) self._message.application_properties = amqp_props if self.annotations: if not isinstance(self.annotations, dict): raise TypeError("Message annotations must be a dictionary.") ann_props = c_uamqp.create_message_annotations( utils.data_factory(self.annotations, encoding=self._encoding)) self._message.message_annotations = ann_props if self.header: self._message.header = self.header._header # pylint: disable=protected-access return self._message
def test_message_properties(): value = c_uamqp.create_properties() assert not value.user_id value = c_uamqp.create_properties() value.user_id = utils.data_factory(bytearray(b'testuseridlongstring')) assert value.user_id == b'testuseridlongstring' value = c_uamqp.create_properties() value.user_id = utils.data_factory(bytearray(b'')) assert value.user_id == b'' value = c_uamqp.create_properties() value.user_id = utils.data_factory(bytearray(b'short')) assert value.user_id == b'short' value = c_uamqp.create_properties() value.user_id = utils.data_factory(bytearray(b'!@#$%^&*()+_?')) assert value.user_id == b'!@#$%^&*()+_?' value = c_uamqp.create_properties() value.user_id = utils.data_factory(bytearray(b'\nweird\0user\1id\0\t')) assert value.user_id == b'\nweird\0user\1id\0\t'
async def test_event_hubs_idempotent_producer(live_eventhub_config): uri = "sb://{}/{}".format(live_eventhub_config['hostname'], live_eventhub_config['event_hub']) sas_auth = authentication.SASTokenAsync.from_shared_access_key( uri, live_eventhub_config['key_name'], live_eventhub_config['access_key']) target = "amqps://{}/{}/Partitions/0".format( live_eventhub_config['hostname'], live_eventhub_config['event_hub']) symbol_array = [ uamqp_types.AMQPSymbol(b"com.microsoft:idempotent-producer") ] desired_capabilities = utils.data_factory( uamqp_types.AMQPArray(symbol_array)) link_properties = { uamqp_types.AMQPSymbol(b"com.microsoft:timeout"): uamqp_types.AMQPLong(int(60 * 1000)) } def on_attach(attach_source, attach_target, properties, error): if str(attach_target) == target: on_attach.owner_level = properties.get( b"com.microsoft:producer-epoch") on_attach.producer_group_id = properties.get( b"com.microsoft:producer-id") on_attach.starting_sequence_number = properties.get( b"com.microsoft:producer-sequence-number") send_client = uamqp.SendClientAsync( target, auth=sas_auth, desired_capabilities=desired_capabilities, link_properties=link_properties, on_attach=on_attach, debug=True) await send_client.open_async() while not await send_client.client_ready_async(): await asyncio.sleep(0.05) assert on_attach.owner_level is not None assert on_attach.producer_group_id is not None assert on_attach.starting_sequence_number is not None await send_client.close_async()
def __init__(self, session, source, target, on_message_received, name=None, receive_settle_mode=None, max_message_size=None, prefetch=None, properties=None, debug=False, encoding='UTF-8'): # pylint: disable=protected-access if name: self.name = name.encode(encoding) if isinstance(name, str) else name else: self.name = str(uuid.uuid4()).encode(encoding) target = target.encode(encoding) if isinstance(target, str) else target role = constants.Role.Receiver self.source = source._address.value self.target = c_uamqp.Messaging.create_target(target) self.on_message_received = on_message_received self._conn = session._conn self._session = session self._link = c_uamqp.create_link(session._session, self.name, role.value, self.source, self.target) if prefetch: self._link.set_prefetch_count(prefetch) if properties: self._link.set_attach_properties( utils.data_factory(properties, encoding=encoding)) if receive_settle_mode: self.receive_settle_mode = receive_settle_mode if max_message_size: self.max_message_size = max_message_size self._receiver = c_uamqp.create_message_receiver(self._link, self) self._receiver.set_trace(debug) self._state = constants.MessageReceiverState.Idle
def __init__(self, session, source, target, name=None, send_settle_mode=None, max_message_size=None, link_credit=None, properties=None, debug=False, encoding='UTF-8'): # pylint: disable=protected-access if name: self.name = name.encode(encoding) if isinstance(name, str) else name else: self.name = str(uuid.uuid4()).encode(encoding) source = source.encode(encoding) if isinstance(source, str) else source role = constants.Role.Sender self.source = c_uamqp.Messaging.create_source(source) self.target = target._address.value self._conn = session._conn self._session = session self._link = c_uamqp.create_link(session._session, self.name, role.value, self.source, self.target) self._link.max_message_size = max_message_size if link_credit: self._link.set_prefetch_count(link_credit) if properties: self._link.set_attach_properties( utils.data_factory(properties, encoding=encoding)) if send_settle_mode: self.send_settle_mode = send_settle_mode if max_message_size: self.max_message_size = max_message_size self._sender = c_uamqp.create_message_sender(self._link, self) self._sender.set_trace(debug) self._state = constants.MessageSenderState.Idle
def __init__(self, condition=None, description=None, encoding='UTF-8', info=None): if condition: self.error_condition = condition.encode(encoding) if isinstance( condition, six.text_type) else condition else: self.error_condition = b"amqp:internal-error" self.error_description = None if description: self.error_description = description.encode(encoding) if isinstance(description, six.text_type) \ else description else: self.error_description = b"" if info and not isinstance(info, dict): raise TypeError("Disposition error info must be a dictionary.") self.error_info = utils.data_factory( info, encoding=encoding) if info else None super(MessageRejected, self).__init__()
def _c_wrapper(self, descriptor, described): descriptor = utils.data_factory(descriptor) described = utils.data_factory(described) return c_uamqp.described_value(descriptor, described)
def absolute_expiry_time(self, value): value = utils.data_factory(value, encoding=self._encoding) self._properties.absolute_expiry_time = value
def content_encoding(self, value): value = utils.data_factory(value, encoding=self._encoding) self._properties.content_encoding = value
def creation_time(self, value): value = utils.data_factory(value, encoding=self._encoding) self._properties.creation_time = value
def properties(self, value): if not isinstance(value, dict): raise TypeError("Connection properties must be a dictionary.") self._conn.properties = utils.data_factory(value, encoding=self._encoding)
def group_sequence(self, value): value = utils.data_factory(value, encoding=self._encoding) self._properties.group_sequence = value
def reply_to_group_id(self, value): value = utils.data_factory(value, encoding=self._encoding) self._properties.reply_to_group_id = value
def correlation_id(self, value): if value is None: self._correlation_id = None else: self._correlation_id = utils.data_factory(value, encoding=self._encoding)
def reply_to(self, value): if value is None: self._reply_to = None else: self._reply_to = utils.data_factory(value, encoding=self._encoding)
def message_id(self, value): if value is None: self._message_id = None else: self._message_id = utils.data_factory(value, encoding=self._encoding)