async def test_export_alias(): bus = await MessageBus().connect() interface = ExampleInterface('test.interface') export_path = '/test/path' export_path2 = '/test/path/child' bus.export(export_path, interface) bus.export(export_path2, interface) result = await bus.call( Message(destination=bus.unique_name, path=export_path, interface='test.interface', member='some_method')) assert result.message_type is MessageType.METHOD_RETURN, result.body[0] assert interface._method_called interface._method_called = False result = await bus.call( Message(destination=bus.unique_name, path=export_path2, interface='test.interface', member='some_method')) assert result.message_type is MessageType.METHOD_RETURN, result.body[0] assert interface._method_called
def test_unmarshalling_with_table(): for item in table: stream = io.BytesIO(bytes.fromhex(item['data'])) unmarshaller = Unmarshaller(stream) try: unmarshaller.unmarshall() except Exception as e: print('message failed to unmarshall:') print(json_dump(item['message'])) raise e message = Message(**item['message']) body = [] for i, type_ in enumerate(message.signature_tree.types): body.append(replace_variants(type_, message.body[i])) message.body = body for attr in [ 'body', 'signature', 'message_type', 'destination', 'path', 'interface', 'member', 'flags', 'serial' ]: assert getattr(unmarshaller.message, attr) == getattr(message, attr), f'attr doesnt match: {attr}'
async def test_sending_messages_between_buses(): bus1 = await MessageBus().connect() bus2 = await MessageBus().connect() msg = Message(destination=bus1.unique_name, path='/org/test/path', interface='org.test.iface', member='SomeMember', serial=bus2.next_serial()) def message_handler(sent): if sent.sender == bus2.unique_name and sent.serial == msg.serial: assert sent.path == msg.path assert sent.serial == msg.serial assert sent.interface == msg.interface assert sent.member == msg.member bus1.send(Message.new_method_return(sent, 's', ['got it'])) bus1.remove_message_handler(message_handler) return True bus1.add_message_handler(message_handler) reply = await bus2.call(msg) assert reply.message_type == MessageType.METHOD_RETURN assert reply.sender == bus1.unique_name assert reply.signature == 's' assert reply.body == ['got it'] assert reply.reply_serial == msg.serial def message_handler_error(sent): if sent.sender == bus2.unique_name and sent.serial == msg.serial: assert sent.path == msg.path assert sent.serial == msg.serial assert sent.interface == msg.interface assert sent.member == msg.member bus1.send( Message.new_error(sent, 'org.test.Error', 'throwing an error')) bus1.remove_message_handler(message_handler_error) return True bus1.add_message_handler(message_handler_error) msg.serial = bus2.next_serial() reply = await bus2.call(msg) assert reply.message_type == MessageType.ERROR assert reply.sender == bus1.unique_name assert reply.reply_serial == msg.serial assert reply.error_name == 'org.test.Error' assert reply.signature == 's' assert reply.body == ['throwing an error'] msg.serial = bus2.next_serial() msg.flags = MessageFlag.NO_REPLY_EXPECTED reply = await bus2.call(msg) assert reply is None
async def test_object_manager(): expected_reply = { '/test/path/deeper': { 'test.interface2': { 'Bar': Variant('s', 'str'), 'Foo': Variant('y', 42), 'AsyncProp': Variant('s', 'async'), } } } reply_ext = { '/test/path': { 'test.interface1': {}, 'test.interface2': { 'Bar': Variant('s', 'str'), 'Foo': Variant('y', 42), 'AsyncProp': Variant('s', 'async'), } } } bus1 = await MessageBus().connect() bus2 = await MessageBus().connect() interface = ExampleInterface('test.interface1') interface2 = ExampleComplexInterface('test.interface2') export_path = '/test/path' bus1.export(export_path, interface) bus1.export(export_path, interface2) bus1.export(export_path + '/deeper', interface2) reply_root = await bus2.call( Message(destination=bus1.unique_name, path='/', interface='org.freedesktop.DBus.ObjectManager', member='GetManagedObjects')) reply_level1 = await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface='org.freedesktop.DBus.ObjectManager', member='GetManagedObjects')) reply_level2 = await bus2.call( Message(destination=bus1.unique_name, path=export_path + '/deeper', interface='org.freedesktop.DBus.ObjectManager', member='GetManagedObjects')) assert reply_root.signature == 'a{oa{sa{sv}}}' assert reply_level1.signature == 'a{oa{sa{sv}}}' assert reply_level2.signature == 'a{oa{sa{sv}}}' assert reply_level2.body == [{}] assert reply_level1.body == [expected_reply] expected_reply.update(reply_ext) assert reply_root.body == [expected_reply]
async def _send_dbus_message(session_bus, message_type, destination, interface, path, member, signature, body): """ Private method to send messages to dbus via dbus_next. Returns a tuple of the bus object and message response. """ if session_bus: bus_type = BusType.SESSION else: bus_type = BusType.SYSTEM if isinstance(body, str): body = [body] bus = await MessageBus(bus_type=bus_type).connect() msg = await bus.call( Message(message_type=message_type, destination=destination, interface=interface, path=path, member=member, signature=signature, body=body)) return bus, msg
async def call_dbus(self, method: str, *args: list[Any]) -> str: """Call a dbus method.""" method_parts = method.split(".") signature, arg_list = self._prepare_args(*args) _LOGGER.debug("Call %s on %s", method, self.object_path) reply = await self._bus.call( Message( destination=self.bus_name, path=self.object_path, interface=".".join(method_parts[:-1]), member=method_parts[-1], signature=signature, body=arg_list, )) if reply.message_type == MessageType.ERROR: if reply.error_name == "org.freedesktop.DBus.Error.ServiceUnknown": raise DBusInterfaceError(reply.body[0]) if reply.error_name == "org.freedesktop.DBus.Error.UnknownMethod": raise DBusInterfaceMethodError(reply.body[0]) if reply.error_name == "org.freedesktop.DBus.Error.Disconnected": raise DBusNotConnectedError() if reply.body and len(reply.body) > 0: raise DBusFatalError(reply.body[0]) raise DBusFatalError() return _remove_dbus_signature(reply.body)
async def main(): bus = await MessageBus(bus_type=bus_type).connect() message = Message(destination=destination, member=member, interface=interface, path=object_path, signature=signature, body=body) result = await bus.call(message) ret = 0 if result.message_type is MessageType.ERROR: print(f'Error: {result.error_name}', file=sys.stderr) ret = 1 def default(o): if type(o) is Variant: return [o.signature, o.value] else: raise json.JSONDecodeError() print(json.dumps(result.body, indent=2, default=default)) sys.exit(ret)
async def test_tcp_connection_with_forwarding(event_loop): closables = [] host = '127.0.0.1' port = '55556' addr_info = parse_address(os.environ.get('DBUS_SESSION_BUS_ADDRESS')) assert addr_info assert 'abstract' in addr_info[0][1] path = f'\0{addr_info[0][1]["abstract"]}' async def handle_connection(tcp_reader, tcp_writer): unix_reader, unix_writer = await asyncio.open_unix_connection(path) closables.append(tcp_writer) closables.append(unix_writer) async def handle_read(): while True: data = await tcp_reader.read(1) if not data: break unix_writer.write(data) async def handle_write(): while True: data = await unix_reader.read(1) if not data: break tcp_writer.write(data) asyncio.run_coroutine_threadsafe(handle_read(), event_loop) asyncio.run_coroutine_threadsafe(handle_write(), event_loop) server = await asyncio.start_server(handle_connection, host, port) closables.append(server) bus = await MessageBus(bus_address=f'tcp:host={host},port={port}' ).connect() # basic tests to see if it works result = await bus.call( Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus.Peer', member='Ping')) assert result intr = await bus.introspect('org.freedesktop.DBus', '/org/freedesktop/DBus') obj = bus.get_proxy_object('org.freedesktop.DBus', '/org/freedesktop/DBus', intr) iface = obj.get_interface('org.freedesktop.DBus.Peer') await iface.call_ping() assert bus._sock.getpeername()[0] == host assert bus._sock.getsockname()[0] == host assert bus._sock.gettimeout() == 0 assert bus._stream.closed is False for c in closables: c.close()
async def call(member, signature='', body=[]): return await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface=interface.name, member=member, signature=signature, body=body))
async def call_properties(member, signature, body): return await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface='org.freedesktop.DBus.Properties', member=member, signature=signature, body=body))
async def test_standard_interfaces(): bus = await MessageBus().connect() msg = Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus', member='ListNames', serial=bus.next_serial()) reply = await bus.call(msg) assert reply.message_type == MessageType.METHOD_RETURN assert reply.reply_serial == msg.serial assert reply.signature == 'as' assert bus.unique_name in reply.body[0] msg.interface = 'org.freedesktop.DBus.Introspectable' msg.member = 'Introspect' msg.serial = bus.next_serial() reply = await bus.call(msg) assert reply.message_type == MessageType.METHOD_RETURN assert reply.reply_serial == msg.serial assert reply.signature == 's' assert type(reply.body[0]) is str msg.member = 'MemberDoesNotExist' msg.serial = bus.next_serial() reply = await bus.call(msg) assert reply.message_type == MessageType.ERROR assert reply.reply_serial == msg.serial assert reply.error_name assert reply.signature == 's' assert type(reply.body[0]) is str
def message_handler_error(sent): if sent.sender == bus2.unique_name and sent.serial == msg.serial: assert sent.path == msg.path assert sent.serial == msg.serial assert sent.interface == msg.interface assert sent.member == msg.member bus1.send(Message.new_error(sent, 'org.test.Error', 'throwing an error')) bus1.remove_message_handler(message_handler_error) return True
def message_handler(sent): if sent.sender == bus2.unique_name and sent.serial == msg.serial: assert sent.path == msg.path assert sent.serial == msg.serial assert sent.interface == msg.interface assert sent.member == msg.member bus1.send(Message.new_method_return(sent, 's', ['got it'])) bus1.remove_message_handler(message_handler) return True
async def test_standard_interface_properties(): # standard interfaces have no properties, but should still behave correctly # when you try to call the methods anyway (#49) bus1 = await MessageBus().connect() bus2 = await MessageBus().connect() interface = ExampleInterface('test.interface1') export_path = '/test/path' bus1.export(export_path, interface) for iface in [ 'org.freedesktop.DBus.Properties', 'org.freedesktop.DBus.Introspectable', 'org.freedesktop.DBus.Peer', 'org.freedesktop.DBus.ObjectManager' ]: result = await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface='org.freedesktop.DBus.Properties', member='Get', signature='ss', body=[iface, 'anything'])) assert result.message_type is MessageType.ERROR assert result.error_name == ErrorType.UNKNOWN_PROPERTY.value result = await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface='org.freedesktop.DBus.Properties', member='Set', signature='ssv', body=[iface, 'anything', Variant('s', 'new thing')])) assert result.message_type is MessageType.ERROR assert result.error_name == ErrorType.UNKNOWN_PROPERTY.value result = await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface='org.freedesktop.DBus.Properties', member='GetAll', signature='s', body=[iface])) assert result.message_type is MessageType.METHOD_RETURN assert result.body == [{}]
async def test_property_changed_signal(interface_class): bus1 = await MessageBus().connect() bus2 = await MessageBus().connect() await bus2.call( Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus', member='AddMatch', signature='s', body=[f'sender={bus1.unique_name}'])) interface = interface_class('test.interface') export_path = '/test/path' bus1.export(export_path, interface) async def wait_for_message(): # TODO timeout future = asyncio.get_event_loop().create_future() def message_handler(signal): if signal.interface == 'org.freedesktop.DBus.Properties': bus2.remove_message_handler(message_handler) future.set_result(signal) bus2.add_message_handler(message_handler) return await future bus2.send( Message(destination=bus1.unique_name, interface=interface.name, path=export_path, member='do_emit_properties_changed')) signal = await wait_for_message() assert signal.interface == 'org.freedesktop.DBus.Properties' assert signal.member == 'PropertiesChanged' assert signal.signature == 'sa{sv}as' assert signal.body == [ interface.name, { 'string_prop': Variant('s', 'asdf') }, ['container_prop'] ]
async def test_signals(): bus1 = await MessageBus().connect() bus2 = await MessageBus().connect() interface = ExampleInterface('test.interface') export_path = '/test/path' bus1.export(export_path, interface) await bus2.call( Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus', member='AddMatch', signature='s', body=[f'sender={bus1.unique_name}'])) async def wait_for_message(): # TODO timeout future = asyncio.get_event_loop().create_future() def message_handler(signal): if signal.sender == bus1.unique_name and signal.interface == interface.name: bus1.remove_message_handler(message_handler) future.set_result(signal) bus2.add_message_handler(message_handler) return await future def assert_signal_ok(signal, member, signature, body): assert signal.message_type == MessageType.SIGNAL, signal.body[0] assert signal.interface == interface.name assert signal.path == export_path assert signal.sender == bus1.unique_name assert signal.member == member assert signal.signature == signature assert signal.body == body interface.signal_empty() signal = await wait_for_message() assert_signal_ok(signal, 'signal_empty', '', []) interface.original_name() signal = await wait_for_message() assert_signal_ok(signal, 'renamed', '', []) interface.signal_simple() signal = await wait_for_message() assert_signal_ok(signal, 'signal_simple', 's', ['hello']) interface.signal_multiple() signal = await wait_for_message() assert_signal_ok(signal, 'signal_multiple', 'ss', ['hello', 'world']) with pytest.raises(SignalDisabledError): interface.signal_disabled()
async def get_name_owner(name): reply = await bus1.call( Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus', member='GetNameOwner', signature='s', body=[name])) assert reply.message_type == MessageType.METHOD_RETURN return reply.body[0]
async def test_active_player_change(bus_address): queue = Queue() playerctld_proc = await start_playerctld(bus_address) bus = await MessageBus(bus_address=bus_address).connect() reply = await bus.call( Message(destination='org.freedesktop.DBus', interface='org.freedesktop.DBus', path='/org/freedesktop/DBus', member='AddMatch', signature='s', body=["sender='org.mpris.MediaPlayer2.playerctld'"])) assert reply.message_type == MessageType.METHOD_RETURN, reply.body def message_handler(message): if message.member == 'PropertiesChanged' and message.body[ 0] == 'com.github.altdesktop.playerctld' and 'PlayerNames' in message.body[ 1]: queue.put_nowait(message.body[1]['PlayerNames'].value) def player_list(*args): return [f'org.mpris.MediaPlayer2.{name}' for name in args] bus.add_message_handler(message_handler) [mpris1] = await setup_mpris('player1', bus_address=bus_address) assert player_list('player1') == await queue.get() [mpris2] = await setup_mpris('player2', bus_address=bus_address) assert player_list('player2', 'player1') == await queue.get() # changing artist/title should bump the player up await mpris1.set_artist_title('artist1', 'title1', '/1') assert player_list('player1', 'player2') == await queue.get() # if properties are not actually different, it shouldn't update await mpris2.set_artist_title('artist2', 'title2', '/2') assert player_list('player2', 'player1') == await queue.get() await mpris1.set_artist_title('artist1', 'title1', '/1') await mpris1.ping() assert queue.empty() bus.disconnect() await asyncio.gather(mpris1.disconnect(), mpris2.disconnect(), bus.wait_for_disconnect()) playerctld_proc.terminate() await playerctld_proc.wait()
def test_sending_signals_between_buses(): bus1 = MessageBus().connect_sync() bus2 = MessageBus().connect_sync() add_match_msg = Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus', member='AddMatch', signature='s', body=[f'sender={bus2.unique_name}']) bus1.call_sync(add_match_msg) main = GLib.MainLoop() def wait_for_message(): ret = None def message_handler(signal): nonlocal ret if signal.sender == bus2.unique_name: ret = signal bus1.remove_message_handler(message_handler) main.quit() bus1.add_message_handler(message_handler) main.run() return ret bus2.send( Message.new_signal('/org/test/path', 'org.test.interface', 'SomeSignal', 's', ['a signal'])) signal = wait_for_message() assert signal.message_type == MessageType.SIGNAL assert signal.path == '/org/test/path' assert signal.interface == 'org.test.interface' assert signal.member == 'SomeSignal' assert signal.signature == 's' assert signal.body == ['a signal']
async def test_peer_interface(): bus1 = await MessageBus().connect() bus2 = await MessageBus().connect() reply = await bus2.call( Message(destination=bus1.unique_name, path='/path/doesnt/exist', interface='org.freedesktop.DBus.Peer', member='Ping')) assert reply.message_type == MessageType.METHOD_RETURN, reply.body[0] assert reply.signature == '' reply = await bus2.call( Message(destination=bus1.unique_name, path='/path/doesnt/exist', interface='org.freedesktop.DBus.Peer', member='GetMachineId', signature='')) assert reply.message_type == MessageType.METHOD_RETURN, reply.body[0] assert reply.signature == 's'
def message_handler(sent): nonlocal fd_after if sent.sender == bus2.unique_name and sent.serial == msg.serial: assert sent.path == msg.path assert sent.serial == msg.serial assert sent.interface == msg.interface assert sent.member == msg.member assert sent.body == [0] assert len(sent.unix_fds) == 1 fd_after = sent.unix_fds[0] bus1.send(Message.new_method_return(sent, 's', ['got it'])) bus1.remove_message_handler(message_handler) return True
async def main(): bus = await MessageBus().connect() reply = await bus.call( Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus', member='ListNames')) if reply.message_type == MessageType.ERROR: raise Exception(reply.body[0]) print(json.dumps(reply.body[0], indent=2))
async def __aexit__(self, exc_t, exc_v, exc_tb): """Stop collecting signal messages and remove match for signals.""" self._dbus._bus.remove_message_handler(self._message_handler) await self._dbus._bus.call( Message( destination="org.freedesktop.DBus", interface="org.freedesktop.DBus", path="/org/freedesktop/DBus", member="RemoveMatch", signature="s", body=[self._match], ))
def test_marshalling_with_table(): for item in table: message = Message(**item['message']) body = [] for i, type_ in enumerate(message.signature_tree.types): body.append(replace_variants(type_, message.body[i])) message.body = body buf = message._marshall() data = bytes.fromhex(item['data']) if buf != data: print('message:') print(json_dump(item['message'])) print('') print('mine:') print_buf(bytes(buf)) print('') print('theirs:') print_buf(data) assert buf == data
async def test_signals(): bus1 = await MessageBus().connect() bus2 = await MessageBus().connect() interface = ExampleInterface('test.interface') export_path = '/test/path' bus1.export(export_path, interface) await bus2.call( Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus', member='AddMatch', signature='s', body=[f'sender={bus1.unique_name}'])) async with ExpectMessage(bus1, bus2, interface.name) as expected_signal: interface.signal_empty() assert_signal_ok(signal=await expected_signal, export_path=export_path, member='signal_empty', signature='', body=[]) async with ExpectMessage(bus1, bus2, interface.name) as expected_signal: interface.original_name() assert_signal_ok(signal=await expected_signal, export_path=export_path, member='renamed', signature='', body=[]) async with ExpectMessage(bus1, bus2, interface.name) as expected_signal: interface.signal_simple() assert_signal_ok(signal=await expected_signal, export_path=export_path, member='signal_simple', signature='s', body=['hello']) async with ExpectMessage(bus1, bus2, interface.name) as expected_signal: interface.signal_multiple() assert_signal_ok(signal=await expected_signal, export_path=export_path, member='signal_multiple', signature='ss', body=['hello', 'world']) with pytest.raises(SignalDisabledError): interface.signal_disabled()
async def test_introspectable_interface(): bus1 = await MessageBus().connect() bus2 = await MessageBus().connect() interface = ExampleInterface('test.interface') interface2 = ExampleInterface('test.interface2') export_path = '/test/path' bus1.export(export_path, interface) bus1.export(export_path, interface2) reply = await bus2.call( Message(destination=bus1.unique_name, path=export_path, interface='org.freedesktop.DBus.Introspectable', member='Introspect')) assert reply.message_type == MessageType.METHOD_RETURN, reply.body[0] assert reply.signature == 's' node = intr.Node.parse(reply.body[0]) assert len(node.interfaces) == standard_interfaces_count + 2 assert node.interfaces[-1].name == 'test.interface2' assert node.interfaces[-2].name == 'test.interface' assert not node.nodes # introspect works on every path reply = await bus2.call( Message(destination=bus1.unique_name, path='/path/doesnt/exist', interface='org.freedesktop.DBus.Introspectable', member='Introspect')) assert reply.message_type == MessageType.METHOD_RETURN, reply.body[0] assert reply.signature == 's' node = intr.Node.parse(reply.body[0]) assert not node.interfaces assert not node.nodes
async def test_sending_signals_between_buses(event_loop): bus1 = await MessageBus().connect() bus2 = await MessageBus().connect() add_match_msg = Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus', member='AddMatch', signature='s', body=[f'sender={bus2.unique_name}']) await bus1.call(add_match_msg) async def wait_for_message(): future = event_loop.create_future() def message_handler(signal): if signal.sender == bus2.unique_name: bus1.remove_message_handler(message_handler) future.set_result(signal) bus1.add_message_handler(message_handler) return await future bus2.send( Message.new_signal('/org/test/path', 'org.test.interface', 'SomeSignal', 's', ['a signal'])) signal = await wait_for_message() assert signal.message_type == MessageType.SIGNAL assert signal.path == '/org/test/path' assert signal.interface == 'org.test.interface' assert signal.member == 'SomeSignal' assert signal.signature == 's' assert signal.body == ['a signal']
async def __aenter__(self): """Install match for signals and start collecting signal messages.""" _LOGGER.debug("Install match for signal %s.%s", self._interface, self._member) await self._dbus._bus.call( Message( destination="org.freedesktop.DBus", interface="org.freedesktop.DBus", path="/org/freedesktop/DBus", member="AddMatch", signature="s", body=[self._match], )) self._dbus._bus.add_message_handler(self._message_handler) return self
async def test_bus_disconnect_before_reply(event_loop): '''In this test, the bus disconnects before the reply comes in. Make sure the caller receives a reply with the error instead of hanging.''' bus = await MessageBus().connect() ping = bus.call( Message(destination='org.freedesktop.DBus', path='/org/freedesktop/DBus', interface='org.freedesktop.DBus', member='Ping')) event_loop.call_soon(bus.disconnect) with pytest.raises(EOFError): await ping assert bus._disconnected
def test_glib_big_message(): 'this tests that nonblocking reads and writes actually work for glib' bus1 = glib.MessageBus().connect_sync() bus2 = glib.MessageBus().connect_sync() interface = ExampleInterface() bus1.export('/test/path', interface) # two megabytes big_body = [bytes(1000000) * 2] result = bus2.call_sync( Message(destination=bus1.unique_name, path='/test/path', interface=interface.name, member='echo_bytes', signature='ay', body=big_body)) assert result.message_type == MessageType.METHOD_RETURN, result.body[0] assert result.body[0] == big_body[0]