From 26e34edc8a7490e309d2dddbc5d0b0f6108895ee Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Tue, 13 Sep 2022 19:38:49 -0400 Subject: [PATCH 1/8] feat: add remove signature option --- src/dbus_fast/aio/proxy_object.py | 19 ++++++---- src/dbus_fast/constants.py | 1 + src/dbus_fast/glib/proxy_object.py | 32 +++++++++++------ src/dbus_fast/proxy_object.py | 53 ++++++++++++++++++++++----- tests/client/test_methods.py | 18 ++++++++++ tests/client/test_properties.py | 18 ++++++++++ tests/client/test_signals.py | 58 ++++++++++++++++++++++++++++-- tests/test_signature_removal.py | 57 +++++++++++++++++++++++++++++ 8 files changed, 229 insertions(+), 27 deletions(-) create mode 100644 tests/test_signature_removal.py diff --git a/src/dbus_fast/aio/proxy_object.py b/src/dbus_fast/aio/proxy_object.py index 43f1e837..adeb439f 100644 --- a/src/dbus_fast/aio/proxy_object.py +++ b/src/dbus_fast/aio/proxy_object.py @@ -87,7 +87,7 @@ async def method_fn(*args, flags=MessageFlag.NONE): member=intr_method.name, signature=intr_method.in_signature, body=input_body, - flags=flags, + flags=flags ^ MessageFlag.REMOVE_SIGNATURE, unix_fds=unix_fds, ) ) @@ -103,15 +103,18 @@ async def method_fn(*args, flags=MessageFlag.NONE): if not out_len: return None - elif out_len == 1: + + if flags & MessageFlag.REMOVE_SIGNATURE: + body = BaseProxyInterface.remove_signature(body) + + if out_len == 1: return body[0] - else: - return body + return body method_name = f"call_{BaseProxyInterface._to_snake_case(intr_method.name)}" setattr(self, method_name, method_fn) - def _add_property(self, intr_property): + def _add_property(self, intr_property, *, flags=MessageFlag.NONE): async def property_getter(): msg = await self.bus.call( Message( @@ -133,7 +136,11 @@ async def property_getter(): msg, ) - return replace_idx_with_fds("v", msg.body, msg.unix_fds)[0].value + body = replace_idx_with_fds("v", msg.body, msg.unix_fds)[0].value + + if flags & MessageFlag.REMOVE_SIGNATURE: + return BaseProxyInterface.remove_signature(body) + return body async def property_setter(val): variant = Variant(intr_property.signature, val) diff --git a/src/dbus_fast/constants.py b/src/dbus_fast/constants.py index 9aa62fdc..81aa63f2 100644 --- a/src/dbus_fast/constants.py +++ b/src/dbus_fast/constants.py @@ -29,6 +29,7 @@ class MessageFlag(IntFlag): NO_REPLY_EXPECTED = 1 #: The method call does not expect a method return. NO_AUTOSTART = 2 ALLOW_INTERACTIVE_AUTHORIZATION = 4 + REMOVE_SIGNATURE = 8 MESSAGE_FLAG_MAP = {field.value: field for field in MessageFlag} diff --git a/src/dbus_fast/glib/proxy_object.py b/src/dbus_fast/glib/proxy_object.py index f7946642..1a0078f3 100644 --- a/src/dbus_fast/glib/proxy_object.py +++ b/src/dbus_fast/glib/proxy_object.py @@ -2,7 +2,7 @@ from typing import List, Union from .. import introspection as intr -from ..constants import ErrorType +from ..constants import ErrorType, MessageFlag from ..errors import DBusError from ..message import Message from ..message_bus import BaseMessageBus @@ -113,7 +113,7 @@ def _add_method(self, intr_method): in_len = len(intr_method.in_args) out_len = len(intr_method.out_args) - def method_fn(*args): + def method_fn(*args, flags=MessageFlag.NONE): if len(args) != in_len + 1: raise TypeError( f"method {intr_method.name} expects {in_len} arguments and a callback (got {len(args)} args)" @@ -136,7 +136,10 @@ def call_notify(msg, err): except DBusError as e: err = e - callback(msg.body, err) + if flags & MessageFlag.REMOVE_SIGNATURE: + callback(BaseProxyInterface.remove_signature(msg.body), err) + else: + callback(msg.body, err) self.bus.call( Message( @@ -150,7 +153,7 @@ def call_notify(msg, err): call_notify, ) - def method_fn_sync(*args): + def method_fn_sync(*args, flags=MessageFlag.NONE): main = GLib.MainLoop() call_error = None call_body = None @@ -171,10 +174,13 @@ def callback(body, err): if not out_len: return None - elif out_len == 1: + + if flags & MessageFlag.REMOVE_SIGNATURE: + call_body = BaseProxyInterface.remove_signature(call_body) + + if out_len == 1: return call_body[0] - else: - return call_body + return call_body method_name = f"call_{BaseProxyInterface._to_snake_case(intr_method.name)}" method_name_sync = f"{method_name}_sync" @@ -183,7 +189,7 @@ def callback(body, err): setattr(self, method_name_sync, method_fn_sync) def _add_property(self, intr_property): - def property_getter(callback): + def property_getter(callback, *, flags=MessageFlag.NONE): def call_notify(msg, err): if err: callback(None, err) @@ -204,8 +210,10 @@ def call_notify(msg, err): ) callback(None, err) return - - callback(variant.value, None) + if flags & MessageFlag.REMOVE_SIGNATURE: + callback(BaseProxyInterface.remove_signature(variant.value), None) + else: + callback(variant.value, None) self.bus.call( Message( @@ -219,7 +227,7 @@ def call_notify(msg, err): call_notify, ) - def property_getter_sync(): + def property_getter_sync(*, flags=MessageFlag.NONE): property_value = None reply_error = None @@ -236,6 +244,8 @@ def callback(value, err): main.run() if reply_error: raise reply_error + if flags & MessageFlag.REMOVE_SIGNATURE: + return BaseProxyInterface.remove_signature(property_value) return property_value def property_setter(value, callback): diff --git a/src/dbus_fast/proxy_object.py b/src/dbus_fast/proxy_object.py index 4ec6b7d6..1998b6cc 100644 --- a/src/dbus_fast/proxy_object.py +++ b/src/dbus_fast/proxy_object.py @@ -3,17 +3,28 @@ import logging import re import xml.etree.ElementTree as ET -from typing import Coroutine, List, Type, Union +from copy import deepcopy +from dataclasses import dataclass +from typing import Any, Callable, Coroutine, List, Type, Union from . import introspection as intr from . import message_bus from ._private.util import replace_idx_with_fds -from .constants import ErrorType, MessageType +from .constants import ErrorType, MessageFlag, MessageType from .errors import DBusError, InterfaceNotFoundError from .message import Message +from .signature import Variant from .validators import assert_bus_name_valid, assert_object_path_valid +@dataclass +class SignalHandler: + """Signal handler.""" + + fn: Callable + flags: int = MessageFlag.NONE + + class BaseProxyInterface: """An abstract class representing a proxy to an interface exported on the bus by another client. @@ -46,7 +57,7 @@ def __init__(self, bus_name, path, introspection, bus): self.path = path self.introspection = introspection self.bus = bus - self._signal_handlers = {} + self._signal_handlers: dict[str, list[SignalHandler]] = {} self._signal_match_rule = f"type='signal',sender={bus_name},interface={introspection.name},path={path}" _underscorer1 = re.compile(r"(.)([A-Z][a-z]+)") @@ -72,6 +83,22 @@ def _check_method_return(msg, signature=None): msg, ) + @staticmethod + def remove_signature(data: Any): + """Remove signature info.""" + if isinstance(data, Variant): + return BaseProxyInterface.remove_signature(data.value) + if isinstance(data, dict): + for k in data: + data[k] = BaseProxyInterface.remove_signature(data[k]) + return data + if isinstance(data, list): + new_list = [] + for item in data: + new_list.append(BaseProxyInterface.remove_signature(item)) + return new_list + return data + def _add_method(self, intr_method): raise NotImplementedError("this must be implemented in the inheriting class") @@ -110,13 +137,21 @@ def _message_handler(self, msg): return body = replace_idx_with_fds(msg.signature, msg.body, msg.unix_fds) + no_sig = None for handler in self._signal_handlers[msg.member]: - cb_result = handler(*body) + if handler.flags & MessageFlag.REMOVE_SIGNATURE: + data = no_sig or ( + no_sig := BaseProxyInterface.remove_signature(deepcopy(body)) + ) + else: + data = body + + cb_result = handler.fn(*data) if isinstance(cb_result, Coroutine): asyncio.create_task(cb_result) def _add_signal(self, intr_signal, interface): - def on_signal_fn(fn): + def on_signal_fn(fn, *, flags=MessageFlag.NONE): fn_signature = inspect.signature(fn) if len(fn_signature.parameters) != len(intr_signal.args) and ( inspect.Parameter.VAR_POSITIONAL @@ -134,11 +169,13 @@ def on_signal_fn(fn): if intr_signal.name not in self._signal_handlers: self._signal_handlers[intr_signal.name] = [] - self._signal_handlers[intr_signal.name].append(fn) + self._signal_handlers[intr_signal.name].append(SignalHandler(fn, flags)) - def off_signal_fn(fn): + def off_signal_fn(fn, *, flags=MessageFlag.NONE): try: - i = self._signal_handlers[intr_signal.name].index(fn) + i = self._signal_handlers[intr_signal.name].index( + SignalHandler(fn, flags) + ) del self._signal_handlers[intr_signal.name][i] if not self._signal_handlers[intr_signal.name]: del self._signal_handlers[intr_signal.name] diff --git a/tests/client/test_methods.py b/tests/client/test_methods.py index d4c73cf5..659be036 100644 --- a/tests/client/test_methods.py +++ b/tests/client/test_methods.py @@ -4,6 +4,7 @@ from dbus_fast import DBusError, aio, glib from dbus_fast.message import MessageFlag from dbus_fast.service import ServiceInterface, method +from dbus_fast.signature import Variant from tests.util import check_gi_repository, skip_reason_no_gi has_gi = check_gi_repository() @@ -33,6 +34,11 @@ def ConcatStrings(self, what1: "s", what2: "s") -> "s": def EchoThree(self, what1: "s", what2: "s", what3: "s") -> "sss": return [what1, what2, what3] + @method() + def GetComplex(self) -> "a{sv}": + """Return complex output.""" + return {"hello": Variant("s", "world")} + @method() def ThrowsError(self): raise DBusError("test.error", "something went wrong") @@ -81,6 +87,12 @@ async def test_aio_proxy_object(): ) assert result is None + result = await interface.call_get_complex() + assert result == {"hello": Variant("s", "world")} + + result = await interface.call_get_complex(flags=MessageFlag.REMOVE_SIGNATURE) + assert result == {"hello": "world"} + with pytest.raises(DBusError): try: await interface.call_throws_error() @@ -120,6 +132,12 @@ def test_glib_proxy_object(): result = interface.call_echo_three_sync("hello", "there", "world") assert result == ["hello", "there", "world"] + result = interface.call_get_complex_sync() + assert result == {"hello": Variant("s", "world")} + + result = interface.call_get_complex_sync(flags=MessageFlag.REMOVE_SIGNATURE) + assert result == {"hello": "world"} + with pytest.raises(DBusError): try: result = interface.call_throws_error_sync() diff --git a/tests/client/test_properties.py b/tests/client/test_properties.py index 2f983f4b..caf2f170 100644 --- a/tests/client/test_properties.py +++ b/tests/client/test_properties.py @@ -2,6 +2,7 @@ from dbus_fast import DBusError, Message, aio, glib from dbus_fast.service import PropertyAccess, ServiceInterface, dbus_property +from dbus_fast.signature import Variant from tests.util import check_gi_repository, skip_reason_no_gi has_gi = check_gi_repository() @@ -27,6 +28,11 @@ def SomeProperty(self, val: "s"): def Int64Property(self) -> "x": return self._int64_property + @dbus_property(access=PropertyAccess.READ) + def ComplexProperty(self) -> "a{sv}": + """Return complex output.""" + return {"hello": Variant("s", "world")} + @dbus_property() def ErrorThrowingProperty(self) -> "s": raise DBusError(self.error_name, self.error_text) @@ -59,6 +65,12 @@ async def test_aio_properties(): await interface.set_some_property("different") assert service_interface._some_property == "different" + prop = await interface.get_complex_property() + assert prop == {"hello": Variant("s", "world")} + + prop = await interface.get_complex_property() + assert prop == {"hello": "world"} + with pytest.raises(DBusError): try: prop = await interface.get_error_throwing_property() @@ -102,6 +114,12 @@ def test_glib_properties(): interface.set_some_property_sync("different") assert service_interface._some_property == "different" + prop = interface.get_complex_property_sync() + assert prop == {"hello": Variant("s", "world")} + + prop = interface.get_complex_property_sync() + assert prop == {"hello": "world"} + with pytest.raises(DBusError): try: prop = interface.get_error_throwing_property_sync() diff --git a/tests/client/test_signals.py b/tests/client/test_signals.py index 57c7559e..31656a21 100644 --- a/tests/client/test_signals.py +++ b/tests/client/test_signals.py @@ -2,10 +2,10 @@ from dbus_fast import Message from dbus_fast.aio import MessageBus -from dbus_fast.aio.proxy_object import ProxyInterface -from dbus_fast.constants import RequestNameReply +from dbus_fast.constants import MessageFlag, RequestNameReply from dbus_fast.introspection import Node from dbus_fast.service import ServiceInterface, signal +from dbus_fast.signature import Variant class ExampleInterface(ServiceInterface): @@ -20,6 +20,11 @@ def SomeSignal(self) -> "s": def SignalMultiple(self) -> "ss": return ["hello", "world"] + @signal() + def SignalComplex(self) -> "a{sv}": + """Broadcast a complex signal.""" + return {"hello": Variant("s", "world")} + @pytest.mark.asyncio async def test_signals(): @@ -159,6 +164,55 @@ def dummy_signal_handler(what): bus3.disconnect() +@pytest.mark.asyncio +async def test_complex_signals(): + """Test complex signals with and without signature removal.""" + bus1 = await MessageBus().connect() + bus2 = await MessageBus().connect() + + await bus1.request_name("test.signals.name") + service_interface = ExampleInterface() + bus1.export("/test/path", service_interface) + + obj = bus2.get_proxy_object( + "test.signals.name", "/test/path", bus1._introspect_export_path("/test/path") + ) + interface = obj.get_interface(service_interface.name) + + async def ping(): + await bus2.call( + Message( + destination=bus1.unique_name, + interface="org.freedesktop.DBus.Peer", + path="/test/path", + member="Ping", + ) + ) + + sig_handler_counter = 0 + no_sig_handler_counter = 0 + + def complex_handler_with_sig(value): + nonlocal sig_handler_counter + assert value == {"hello": Variant("s", "world")} + sig_handler_counter += 1 + + def complex_handler_no_sig(value): + nonlocal no_sig_handler_counter + assert value == {"hello": "world"} + no_sig_handler_counter += 1 + + interface.on_signal_complex(complex_handler_with_sig) + interface.on_signal_complex( + complex_handler_no_sig, flags=MessageFlag.REMOVE_SIGNATURE + ) + + service_interface.SignalComplex() + await ping() + assert sig_handler_counter == 1 + assert no_sig_handler_counter == 1 + + @pytest.mark.asyncio async def test_varargs_callback(): """Test varargs callback for signal.""" diff --git a/tests/test_signature_removal.py b/tests/test_signature_removal.py new file mode 100644 index 00000000..b1007a77 --- /dev/null +++ b/tests/test_signature_removal.py @@ -0,0 +1,57 @@ +"""Test signature removal.""" +import pytest + +from dbus_fast.proxy_object import BaseProxyInterface +from dbus_fast.signature import Variant + + +@pytest.mark.asyncio +async def test_dictionary(): + """Test signature stripped from dictionary.""" + assert BaseProxyInterface.remove_signature( + { + "string": Variant("s", "test"), + "boolean": Variant("b", True), + "int": Variant("u", 1), + "object": Variant("o", "/test/path"), + "array": Variant("as", ["test", "value"]), + "tuple": Variant("(su)", ["test", 1]), + "bytes": Variant("ay", b"\0x62\0x75\0x66"), + } + ) == { + "string": "test", + "boolean": True, + "int": 1, + "object": "/test/path", + "array": ["test", "value"], + "tuple": ["test", 1], + "bytes": b"\0x62\0x75\0x66", + } + + +@pytest.mark.asyncio +async def test_output_list(): + """Test signature stripping handles multiple outputs.""" + assert BaseProxyInterface.remove_signature( + [{"hello": Variant("s", "world")}, {"boolean": Variant("b", True)}, 1] + ) == [{"hello": "world"}, {"boolean": True}, 1] + + +@pytest.mark.asyncio +async def test_nested_variants(): + """Test signature stripping handles nesting.""" + assert BaseProxyInterface.remove_signature( + { + "dict": Variant("a{sv}", {"hello": Variant("s", "world")}), + "array": Variant( + "aa{sv}", + [ + {"hello": Variant("s", "world")}, + {"bytes": Variant("ay", b"\0x62\0x75\0x66")}, + ], + ), + } + ) == { + "dict": {"hello": "world"}, + "array": [{"hello": "world"}, {"bytes": b"\0x62\0x75\0x66"}], + } From 1e75291f263110a98d3fe1feb88c8cb2e33aa567 Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Tue, 13 Sep 2022 21:12:48 -0400 Subject: [PATCH 2/8] fix: 3.7 compliance --- src/dbus_fast/proxy_object.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dbus_fast/proxy_object.py b/src/dbus_fast/proxy_object.py index 1998b6cc..054b8d06 100644 --- a/src/dbus_fast/proxy_object.py +++ b/src/dbus_fast/proxy_object.py @@ -140,9 +140,9 @@ def _message_handler(self, msg): no_sig = None for handler in self._signal_handlers[msg.member]: if handler.flags & MessageFlag.REMOVE_SIGNATURE: - data = no_sig or ( - no_sig := BaseProxyInterface.remove_signature(deepcopy(body)) - ) + if not no_sig: + no_sig = BaseProxyInterface.remove_signature(deepcopy(body)) + data = no_sig else: data = body From eba7490d9545baebfdb2666475eee88879c840db Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Wed, 14 Sep 2022 02:22:14 -0400 Subject: [PATCH 3/8] fix: fix xor and verbose pytest --- .github/workflows/ci.yml | 2 +- src/dbus_fast/aio/proxy_object.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de794382..7f416f70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: - name: Install Dependencies run: poetry install - name: Test with Pytest - run: export $(dbus-launch); poetry run pytest --cov-report=xml + run: export $(dbus-launch); poetry run pytest -vvv --cov-report=xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: diff --git a/src/dbus_fast/aio/proxy_object.py b/src/dbus_fast/aio/proxy_object.py index adeb439f..4fc97dad 100644 --- a/src/dbus_fast/aio/proxy_object.py +++ b/src/dbus_fast/aio/proxy_object.py @@ -87,7 +87,11 @@ async def method_fn(*args, flags=MessageFlag.NONE): member=intr_method.name, signature=intr_method.in_signature, body=input_body, - flags=flags ^ MessageFlag.REMOVE_SIGNATURE, + flags=( + flags ^ MessageFlag.REMOVE_SIGNATURE + if flags & MessageFlag.REMOVE_SIGNATURE + else flags + ), unix_fds=unix_fds, ) ) From 3d5d09145550a9a0d2c1ed1e0b2cc6d47dc6f5bd Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Wed, 14 Sep 2022 11:33:11 -0400 Subject: [PATCH 4/8] fix: flags arg for properties and signal tests --- src/dbus_fast/aio/proxy_object.py | 7 +++++-- tests/client/test_properties.py | 3 ++- tests/client/test_signals.py | 24 ++++++++++++++++++++---- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/dbus_fast/aio/proxy_object.py b/src/dbus_fast/aio/proxy_object.py index 4fc97dad..4cef4f5b 100644 --- a/src/dbus_fast/aio/proxy_object.py +++ b/src/dbus_fast/aio/proxy_object.py @@ -118,8 +118,11 @@ async def method_fn(*args, flags=MessageFlag.NONE): method_name = f"call_{BaseProxyInterface._to_snake_case(intr_method.name)}" setattr(self, method_name, method_fn) - def _add_property(self, intr_property, *, flags=MessageFlag.NONE): - async def property_getter(): + def _add_property( + self, + intr_property, + ): + async def property_getter(*, flags=MessageFlag.NONE): msg = await self.bus.call( Message( destination=self.bus_name, diff --git a/tests/client/test_properties.py b/tests/client/test_properties.py index caf2f170..0e6a132e 100644 --- a/tests/client/test_properties.py +++ b/tests/client/test_properties.py @@ -1,6 +1,7 @@ import pytest from dbus_fast import DBusError, Message, aio, glib +from dbus_fast.constants import MessageFlag from dbus_fast.service import PropertyAccess, ServiceInterface, dbus_property from dbus_fast.signature import Variant from tests.util import check_gi_repository, skip_reason_no_gi @@ -68,7 +69,7 @@ async def test_aio_properties(): prop = await interface.get_complex_property() assert prop == {"hello": Variant("s", "world")} - prop = await interface.get_complex_property() + prop = await interface.get_complex_property(flags=MessageFlag.REMOVE_SIGNATURE) assert prop == {"hello": "world"} with pytest.raises(DBusError): diff --git a/tests/client/test_signals.py b/tests/client/test_signals.py index 31656a21..6ea0070e 100644 --- a/tests/client/test_signals.py +++ b/tests/client/test_signals.py @@ -190,28 +190,44 @@ async def ping(): ) sig_handler_counter = 0 + sig_handler_err = None no_sig_handler_counter = 0 + no_sig_handler_err = None def complex_handler_with_sig(value): nonlocal sig_handler_counter - assert value == {"hello": Variant("s", "world")} - sig_handler_counter += 1 + nonlocal sig_handler_err + try: + assert value == {"hello": Variant("s", "world")} + sig_handler_counter += 1 + except AssertionError as ex: + sig_handler_err = ex def complex_handler_no_sig(value): nonlocal no_sig_handler_counter - assert value == {"hello": "world"} - no_sig_handler_counter += 1 + nonlocal no_sig_handler_err + try: + assert value == {"hello": "world"} + no_sig_handler_counter += 1 + except AssertionError as ex: + no_sig_handler_err = ex interface.on_signal_complex(complex_handler_with_sig) interface.on_signal_complex( complex_handler_no_sig, flags=MessageFlag.REMOVE_SIGNATURE ) + await ping() service_interface.SignalComplex() await ping() + assert sig_handler_err is None assert sig_handler_counter == 1 + assert no_sig_handler_err is None assert no_sig_handler_counter == 1 + bus1.disconnect() + bus2.disconnect() + @pytest.mark.asyncio async def test_varargs_callback(): From 94c8350330afc6b4a0ee786054c994c4144e2749 Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Wed, 14 Sep 2022 12:50:19 -0400 Subject: [PATCH 5/8] fix: rename to unpack variants and make copy --- .github/workflows/ci.yml | 2 +- src/dbus_fast/aio/proxy_object.py | 16 +++++------ src/dbus_fast/constants.py | 2 +- src/dbus_fast/glib/proxy_object.py | 18 ++++++------- src/dbus_fast/proxy_object.py | 27 ++++--------------- src/dbus_fast/signature.py | 11 ++++++++ tests/client/test_methods.py | 4 +-- tests/client/test_properties.py | 4 +-- tests/client/test_signals.py | 2 +- ...ure_removal.py => test_unpack_variants.py} | 17 ++++++------ 10 files changed, 46 insertions(+), 57 deletions(-) rename tests/{test_signature_removal.py => test_unpack_variants.py} (75%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f416f70..de794382 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: - name: Install Dependencies run: poetry install - name: Test with Pytest - run: export $(dbus-launch); poetry run pytest -vvv --cov-report=xml + run: export $(dbus-launch); poetry run pytest --cov-report=xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: diff --git a/src/dbus_fast/aio/proxy_object.py b/src/dbus_fast/aio/proxy_object.py index 4cef4f5b..35f60179 100644 --- a/src/dbus_fast/aio/proxy_object.py +++ b/src/dbus_fast/aio/proxy_object.py @@ -8,7 +8,7 @@ from ..message import Message, MessageFlag from ..message_bus import BaseMessageBus from ..proxy_object import BaseProxyInterface, BaseProxyObject -from ..signature import Variant +from ..signature import Variant, unpack_variants class ProxyInterface(BaseProxyInterface): @@ -87,11 +87,7 @@ async def method_fn(*args, flags=MessageFlag.NONE): member=intr_method.name, signature=intr_method.in_signature, body=input_body, - flags=( - flags ^ MessageFlag.REMOVE_SIGNATURE - if flags & MessageFlag.REMOVE_SIGNATURE - else flags - ), + flags=flags & ~MessageFlag.UNPACK_VARIANTS, unix_fds=unix_fds, ) ) @@ -108,8 +104,8 @@ async def method_fn(*args, flags=MessageFlag.NONE): if not out_len: return None - if flags & MessageFlag.REMOVE_SIGNATURE: - body = BaseProxyInterface.remove_signature(body) + if flags & MessageFlag.UNPACK_VARIANTS: + body = unpack_variants(body) if out_len == 1: return body[0] @@ -145,8 +141,8 @@ async def property_getter(*, flags=MessageFlag.NONE): body = replace_idx_with_fds("v", msg.body, msg.unix_fds)[0].value - if flags & MessageFlag.REMOVE_SIGNATURE: - return BaseProxyInterface.remove_signature(body) + if flags & MessageFlag.UNPACK_VARIANTS: + return unpack_variants(body) return body async def property_setter(val): diff --git a/src/dbus_fast/constants.py b/src/dbus_fast/constants.py index 81aa63f2..70387122 100644 --- a/src/dbus_fast/constants.py +++ b/src/dbus_fast/constants.py @@ -29,7 +29,7 @@ class MessageFlag(IntFlag): NO_REPLY_EXPECTED = 1 #: The method call does not expect a method return. NO_AUTOSTART = 2 ALLOW_INTERACTIVE_AUTHORIZATION = 4 - REMOVE_SIGNATURE = 8 + UNPACK_VARIANTS = 8 MESSAGE_FLAG_MAP = {field.value: field for field in MessageFlag} diff --git a/src/dbus_fast/glib/proxy_object.py b/src/dbus_fast/glib/proxy_object.py index 1a0078f3..29f22c9f 100644 --- a/src/dbus_fast/glib/proxy_object.py +++ b/src/dbus_fast/glib/proxy_object.py @@ -7,7 +7,7 @@ from ..message import Message from ..message_bus import BaseMessageBus from ..proxy_object import BaseProxyInterface, BaseProxyObject -from ..signature import Variant +from ..signature import Variant, unpack_variants # glib is optional try: @@ -136,8 +136,8 @@ def call_notify(msg, err): except DBusError as e: err = e - if flags & MessageFlag.REMOVE_SIGNATURE: - callback(BaseProxyInterface.remove_signature(msg.body), err) + if flags & MessageFlag.UNPACK_VARIANTS: + callback(unpack_variants(msg.body), err) else: callback(msg.body, err) @@ -175,8 +175,8 @@ def callback(body, err): if not out_len: return None - if flags & MessageFlag.REMOVE_SIGNATURE: - call_body = BaseProxyInterface.remove_signature(call_body) + if flags & MessageFlag.UNPACK_VARIANTS: + call_body = unpack_variants(call_body) if out_len == 1: return call_body[0] @@ -210,8 +210,8 @@ def call_notify(msg, err): ) callback(None, err) return - if flags & MessageFlag.REMOVE_SIGNATURE: - callback(BaseProxyInterface.remove_signature(variant.value), None) + if flags & MessageFlag.UNPACK_VARIANTS: + callback(unpack_variants(variant.value), None) else: callback(variant.value, None) @@ -244,8 +244,8 @@ def callback(value, err): main.run() if reply_error: raise reply_error - if flags & MessageFlag.REMOVE_SIGNATURE: - return BaseProxyInterface.remove_signature(property_value) + if flags & MessageFlag.UNPACK_VARIANTS: + return unpack_variants(property_value) return property_value def property_setter(value, callback): diff --git a/src/dbus_fast/proxy_object.py b/src/dbus_fast/proxy_object.py index 054b8d06..059e7a3a 100644 --- a/src/dbus_fast/proxy_object.py +++ b/src/dbus_fast/proxy_object.py @@ -3,9 +3,8 @@ import logging import re import xml.etree.ElementTree as ET -from copy import deepcopy from dataclasses import dataclass -from typing import Any, Callable, Coroutine, List, Type, Union +from typing import Callable, Coroutine, Dict, List, Type, Union from . import introspection as intr from . import message_bus @@ -13,7 +12,7 @@ from .constants import ErrorType, MessageFlag, MessageType from .errors import DBusError, InterfaceNotFoundError from .message import Message -from .signature import Variant +from .signature import unpack_variants from .validators import assert_bus_name_valid, assert_object_path_valid @@ -57,7 +56,7 @@ def __init__(self, bus_name, path, introspection, bus): self.path = path self.introspection = introspection self.bus = bus - self._signal_handlers: dict[str, list[SignalHandler]] = {} + self._signal_handlers: Dict[str, List[SignalHandler]] = {} self._signal_match_rule = f"type='signal',sender={bus_name},interface={introspection.name},path={path}" _underscorer1 = re.compile(r"(.)([A-Z][a-z]+)") @@ -83,22 +82,6 @@ def _check_method_return(msg, signature=None): msg, ) - @staticmethod - def remove_signature(data: Any): - """Remove signature info.""" - if isinstance(data, Variant): - return BaseProxyInterface.remove_signature(data.value) - if isinstance(data, dict): - for k in data: - data[k] = BaseProxyInterface.remove_signature(data[k]) - return data - if isinstance(data, list): - new_list = [] - for item in data: - new_list.append(BaseProxyInterface.remove_signature(item)) - return new_list - return data - def _add_method(self, intr_method): raise NotImplementedError("this must be implemented in the inheriting class") @@ -139,9 +122,9 @@ def _message_handler(self, msg): body = replace_idx_with_fds(msg.signature, msg.body, msg.unix_fds) no_sig = None for handler in self._signal_handlers[msg.member]: - if handler.flags & MessageFlag.REMOVE_SIGNATURE: + if handler.flags & MessageFlag.UNPACK_VARIANTS: if not no_sig: - no_sig = BaseProxyInterface.remove_signature(deepcopy(body)) + no_sig = unpack_variants(body) data = no_sig else: data = body diff --git a/src/dbus_fast/signature.py b/src/dbus_fast/signature.py index da8ff715..424ad03e 100644 --- a/src/dbus_fast/signature.py +++ b/src/dbus_fast/signature.py @@ -5,6 +5,17 @@ from .validators import is_object_path_valid +def unpack_variants(data: Any): + """Unpack variants and remove signature info.""" + if isinstance(data, Variant): + return unpack_variants(data.value) + if isinstance(data, dict): + return {k: unpack_variants(v) for k, v in data.values()} + if isinstance(data, list): + return [unpack_variants(item) for item in data] + return data + + class SignatureType: """A class that represents a single complete type within a signature. diff --git a/tests/client/test_methods.py b/tests/client/test_methods.py index 659be036..35ea4231 100644 --- a/tests/client/test_methods.py +++ b/tests/client/test_methods.py @@ -90,7 +90,7 @@ async def test_aio_proxy_object(): result = await interface.call_get_complex() assert result == {"hello": Variant("s", "world")} - result = await interface.call_get_complex(flags=MessageFlag.REMOVE_SIGNATURE) + result = await interface.call_get_complex(flags=MessageFlag.UNPACK_VARIANTS) assert result == {"hello": "world"} with pytest.raises(DBusError): @@ -135,7 +135,7 @@ def test_glib_proxy_object(): result = interface.call_get_complex_sync() assert result == {"hello": Variant("s", "world")} - result = interface.call_get_complex_sync(flags=MessageFlag.REMOVE_SIGNATURE) + result = interface.call_get_complex_sync(flags=MessageFlag.UNPACK_VARIANTS) assert result == {"hello": "world"} with pytest.raises(DBusError): diff --git a/tests/client/test_properties.py b/tests/client/test_properties.py index 0e6a132e..344b0adc 100644 --- a/tests/client/test_properties.py +++ b/tests/client/test_properties.py @@ -69,7 +69,7 @@ async def test_aio_properties(): prop = await interface.get_complex_property() assert prop == {"hello": Variant("s", "world")} - prop = await interface.get_complex_property(flags=MessageFlag.REMOVE_SIGNATURE) + prop = await interface.get_complex_property(flags=MessageFlag.UNPACK_VARIANTS) assert prop == {"hello": "world"} with pytest.raises(DBusError): @@ -118,7 +118,7 @@ def test_glib_properties(): prop = interface.get_complex_property_sync() assert prop == {"hello": Variant("s", "world")} - prop = interface.get_complex_property_sync() + prop = interface.get_complex_property_sync(flags=MessageFlag.UNPACK_VARIANTS) assert prop == {"hello": "world"} with pytest.raises(DBusError): diff --git a/tests/client/test_signals.py b/tests/client/test_signals.py index 6ea0070e..652cbc87 100644 --- a/tests/client/test_signals.py +++ b/tests/client/test_signals.py @@ -214,7 +214,7 @@ def complex_handler_no_sig(value): interface.on_signal_complex(complex_handler_with_sig) interface.on_signal_complex( - complex_handler_no_sig, flags=MessageFlag.REMOVE_SIGNATURE + complex_handler_no_sig, flags=MessageFlag.UNPACK_VARIANTS ) await ping() diff --git a/tests/test_signature_removal.py b/tests/test_unpack_variants.py similarity index 75% rename from tests/test_signature_removal.py rename to tests/test_unpack_variants.py index b1007a77..0c832990 100644 --- a/tests/test_signature_removal.py +++ b/tests/test_unpack_variants.py @@ -1,14 +1,13 @@ -"""Test signature removal.""" +"""Test unpack variants.""" import pytest -from dbus_fast.proxy_object import BaseProxyInterface -from dbus_fast.signature import Variant +from dbus_fast.signature import Variant, unpack_variants @pytest.mark.asyncio async def test_dictionary(): - """Test signature stripped from dictionary.""" - assert BaseProxyInterface.remove_signature( + """Test variants unpacked from dictionary.""" + assert unpack_variants( { "string": Variant("s", "test"), "boolean": Variant("b", True), @@ -31,16 +30,16 @@ async def test_dictionary(): @pytest.mark.asyncio async def test_output_list(): - """Test signature stripping handles multiple outputs.""" - assert BaseProxyInterface.remove_signature( + """Test variants unpacked from multiple outputs.""" + assert unpack_variants( [{"hello": Variant("s", "world")}, {"boolean": Variant("b", True)}, 1] ) == [{"hello": "world"}, {"boolean": True}, 1] @pytest.mark.asyncio async def test_nested_variants(): - """Test signature stripping handles nesting.""" - assert BaseProxyInterface.remove_signature( + """Test unpack variants handles nesting.""" + assert unpack_variants( { "dict": Variant("a{sv}", {"hello": Variant("s", "world")}), "array": Variant( From 9ad365c1962cdb4ca1d2301e1cc2e869b0c64309 Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Wed, 14 Sep 2022 12:56:19 -0400 Subject: [PATCH 6/8] fix: items not values --- src/dbus_fast/signature.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dbus_fast/signature.py b/src/dbus_fast/signature.py index 424ad03e..16bd9e9b 100644 --- a/src/dbus_fast/signature.py +++ b/src/dbus_fast/signature.py @@ -10,7 +10,7 @@ def unpack_variants(data: Any): if isinstance(data, Variant): return unpack_variants(data.value) if isinstance(data, dict): - return {k: unpack_variants(v) for k, v in data.values()} + return {k: unpack_variants(v) for k, v in data.items()} if isinstance(data, list): return [unpack_variants(item) for item in data] return data From 36c375a7fa488e5c8963f9e76dd756f5ac7d5fe1 Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Thu, 15 Sep 2022 13:51:17 -0400 Subject: [PATCH 7/8] refactor: use kwarg instead of flag --- src/dbus_fast/aio/proxy_object.py | 21 +++++++++++++-------- src/dbus_fast/constants.py | 1 - src/dbus_fast/glib/proxy_object.py | 29 +++++++++++++++-------------- src/dbus_fast/proxy_object.py | 20 +++++++++++--------- tests/client/test_methods.py | 4 ++-- tests/client/test_properties.py | 4 ++-- tests/client/test_signals.py | 4 +--- 7 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/dbus_fast/aio/proxy_object.py b/src/dbus_fast/aio/proxy_object.py index 35f60179..d1542978 100644 --- a/src/dbus_fast/aio/proxy_object.py +++ b/src/dbus_fast/aio/proxy_object.py @@ -8,7 +8,8 @@ from ..message import Message, MessageFlag from ..message_bus import BaseMessageBus from ..proxy_object import BaseProxyInterface, BaseProxyObject -from ..signature import Variant, unpack_variants +from ..signature import Variant +from ..signature import unpack_variants as unpack class ProxyInterface(BaseProxyInterface): @@ -74,7 +75,9 @@ class ProxyInterface(BaseProxyInterface): """ def _add_method(self, intr_method): - async def method_fn(*args, flags=MessageFlag.NONE): + async def method_fn( + *args, flags=MessageFlag.NONE, unpack_variants: bool = False + ): input_body, unix_fds = replace_fds_with_idx( intr_method.in_signature, list(args) ) @@ -87,7 +90,7 @@ async def method_fn(*args, flags=MessageFlag.NONE): member=intr_method.name, signature=intr_method.in_signature, body=input_body, - flags=flags & ~MessageFlag.UNPACK_VARIANTS, + flags=flags, unix_fds=unix_fds, ) ) @@ -104,8 +107,8 @@ async def method_fn(*args, flags=MessageFlag.NONE): if not out_len: return None - if flags & MessageFlag.UNPACK_VARIANTS: - body = unpack_variants(body) + if unpack_variants: + body = unpack(body) if out_len == 1: return body[0] @@ -118,7 +121,9 @@ def _add_property( self, intr_property, ): - async def property_getter(*, flags=MessageFlag.NONE): + async def property_getter( + *, flags=MessageFlag.NONE, unpack_variants: bool = False + ): msg = await self.bus.call( Message( destination=self.bus_name, @@ -141,8 +146,8 @@ async def property_getter(*, flags=MessageFlag.NONE): body = replace_idx_with_fds("v", msg.body, msg.unix_fds)[0].value - if flags & MessageFlag.UNPACK_VARIANTS: - return unpack_variants(body) + if unpack_variants: + return unpack(body) return body async def property_setter(val): diff --git a/src/dbus_fast/constants.py b/src/dbus_fast/constants.py index 70387122..9aa62fdc 100644 --- a/src/dbus_fast/constants.py +++ b/src/dbus_fast/constants.py @@ -29,7 +29,6 @@ class MessageFlag(IntFlag): NO_REPLY_EXPECTED = 1 #: The method call does not expect a method return. NO_AUTOSTART = 2 ALLOW_INTERACTIVE_AUTHORIZATION = 4 - UNPACK_VARIANTS = 8 MESSAGE_FLAG_MAP = {field.value: field for field in MessageFlag} diff --git a/src/dbus_fast/glib/proxy_object.py b/src/dbus_fast/glib/proxy_object.py index 29f22c9f..a5a020a2 100644 --- a/src/dbus_fast/glib/proxy_object.py +++ b/src/dbus_fast/glib/proxy_object.py @@ -2,12 +2,13 @@ from typing import List, Union from .. import introspection as intr -from ..constants import ErrorType, MessageFlag +from ..constants import ErrorType from ..errors import DBusError from ..message import Message from ..message_bus import BaseMessageBus from ..proxy_object import BaseProxyInterface, BaseProxyObject -from ..signature import Variant, unpack_variants +from ..signature import Variant +from ..signature import unpack_variants as unpack # glib is optional try: @@ -113,7 +114,7 @@ def _add_method(self, intr_method): in_len = len(intr_method.in_args) out_len = len(intr_method.out_args) - def method_fn(*args, flags=MessageFlag.NONE): + def method_fn(*args, unpack_variants: bool = False): if len(args) != in_len + 1: raise TypeError( f"method {intr_method.name} expects {in_len} arguments and a callback (got {len(args)} args)" @@ -136,8 +137,8 @@ def call_notify(msg, err): except DBusError as e: err = e - if flags & MessageFlag.UNPACK_VARIANTS: - callback(unpack_variants(msg.body), err) + if unpack_variants: + callback(unpack(msg.body), err) else: callback(msg.body, err) @@ -153,7 +154,7 @@ def call_notify(msg, err): call_notify, ) - def method_fn_sync(*args, flags=MessageFlag.NONE): + def method_fn_sync(*args, unpack_variants: bool = False): main = GLib.MainLoop() call_error = None call_body = None @@ -175,8 +176,8 @@ def callback(body, err): if not out_len: return None - if flags & MessageFlag.UNPACK_VARIANTS: - call_body = unpack_variants(call_body) + if unpack_variants: + call_body = unpack(call_body) if out_len == 1: return call_body[0] @@ -189,7 +190,7 @@ def callback(body, err): setattr(self, method_name_sync, method_fn_sync) def _add_property(self, intr_property): - def property_getter(callback, *, flags=MessageFlag.NONE): + def property_getter(callback, *, unpack_variants: bool = False): def call_notify(msg, err): if err: callback(None, err) @@ -210,8 +211,8 @@ def call_notify(msg, err): ) callback(None, err) return - if flags & MessageFlag.UNPACK_VARIANTS: - callback(unpack_variants(variant.value), None) + if unpack_variants: + callback(unpack(variant.value), None) else: callback(variant.value, None) @@ -227,7 +228,7 @@ def call_notify(msg, err): call_notify, ) - def property_getter_sync(*, flags=MessageFlag.NONE): + def property_getter_sync(*, unpack_variants: bool = False): property_value = None reply_error = None @@ -244,8 +245,8 @@ def callback(value, err): main.run() if reply_error: raise reply_error - if flags & MessageFlag.UNPACK_VARIANTS: - return unpack_variants(property_value) + if unpack_variants: + return unpack(property_value) return property_value def property_setter(value, callback): diff --git a/src/dbus_fast/proxy_object.py b/src/dbus_fast/proxy_object.py index 059e7a3a..626d94da 100644 --- a/src/dbus_fast/proxy_object.py +++ b/src/dbus_fast/proxy_object.py @@ -9,10 +9,10 @@ from . import introspection as intr from . import message_bus from ._private.util import replace_idx_with_fds -from .constants import ErrorType, MessageFlag, MessageType +from .constants import ErrorType, MessageType from .errors import DBusError, InterfaceNotFoundError from .message import Message -from .signature import unpack_variants +from .signature import unpack_variants as unpack from .validators import assert_bus_name_valid, assert_object_path_valid @@ -21,7 +21,7 @@ class SignalHandler: """Signal handler.""" fn: Callable - flags: int = MessageFlag.NONE + unpack_variants: bool class BaseProxyInterface: @@ -122,9 +122,9 @@ def _message_handler(self, msg): body = replace_idx_with_fds(msg.signature, msg.body, msg.unix_fds) no_sig = None for handler in self._signal_handlers[msg.member]: - if handler.flags & MessageFlag.UNPACK_VARIANTS: + if handler.unpack_variants: if not no_sig: - no_sig = unpack_variants(body) + no_sig = unpack(body) data = no_sig else: data = body @@ -134,7 +134,7 @@ def _message_handler(self, msg): asyncio.create_task(cb_result) def _add_signal(self, intr_signal, interface): - def on_signal_fn(fn, *, flags=MessageFlag.NONE): + def on_signal_fn(fn, *, unpack_variants: bool = False): fn_signature = inspect.signature(fn) if len(fn_signature.parameters) != len(intr_signal.args) and ( inspect.Parameter.VAR_POSITIONAL @@ -152,12 +152,14 @@ def on_signal_fn(fn, *, flags=MessageFlag.NONE): if intr_signal.name not in self._signal_handlers: self._signal_handlers[intr_signal.name] = [] - self._signal_handlers[intr_signal.name].append(SignalHandler(fn, flags)) + self._signal_handlers[intr_signal.name].append( + SignalHandler(fn, unpack_variants) + ) - def off_signal_fn(fn, *, flags=MessageFlag.NONE): + def off_signal_fn(fn, *, unpack_variants: bool = False): try: i = self._signal_handlers[intr_signal.name].index( - SignalHandler(fn, flags) + SignalHandler(fn, unpack_variants) ) del self._signal_handlers[intr_signal.name][i] if not self._signal_handlers[intr_signal.name]: diff --git a/tests/client/test_methods.py b/tests/client/test_methods.py index 35ea4231..b2c30e66 100644 --- a/tests/client/test_methods.py +++ b/tests/client/test_methods.py @@ -90,7 +90,7 @@ async def test_aio_proxy_object(): result = await interface.call_get_complex() assert result == {"hello": Variant("s", "world")} - result = await interface.call_get_complex(flags=MessageFlag.UNPACK_VARIANTS) + result = await interface.call_get_complex(unpack_variants=True) assert result == {"hello": "world"} with pytest.raises(DBusError): @@ -135,7 +135,7 @@ def test_glib_proxy_object(): result = interface.call_get_complex_sync() assert result == {"hello": Variant("s", "world")} - result = interface.call_get_complex_sync(flags=MessageFlag.UNPACK_VARIANTS) + result = interface.call_get_complex_sync(unpack_variants=True) assert result == {"hello": "world"} with pytest.raises(DBusError): diff --git a/tests/client/test_properties.py b/tests/client/test_properties.py index 344b0adc..feaabe01 100644 --- a/tests/client/test_properties.py +++ b/tests/client/test_properties.py @@ -69,7 +69,7 @@ async def test_aio_properties(): prop = await interface.get_complex_property() assert prop == {"hello": Variant("s", "world")} - prop = await interface.get_complex_property(flags=MessageFlag.UNPACK_VARIANTS) + prop = await interface.get_complex_property(unpack_variants=True) assert prop == {"hello": "world"} with pytest.raises(DBusError): @@ -118,7 +118,7 @@ def test_glib_properties(): prop = interface.get_complex_property_sync() assert prop == {"hello": Variant("s", "world")} - prop = interface.get_complex_property_sync(flags=MessageFlag.UNPACK_VARIANTS) + prop = interface.get_complex_property_sync(unpack_variants=True) assert prop == {"hello": "world"} with pytest.raises(DBusError): diff --git a/tests/client/test_signals.py b/tests/client/test_signals.py index 652cbc87..12acfc6b 100644 --- a/tests/client/test_signals.py +++ b/tests/client/test_signals.py @@ -213,9 +213,7 @@ def complex_handler_no_sig(value): no_sig_handler_err = ex interface.on_signal_complex(complex_handler_with_sig) - interface.on_signal_complex( - complex_handler_no_sig, flags=MessageFlag.UNPACK_VARIANTS - ) + interface.on_signal_complex(complex_handler_no_sig, unpack_variants=True) await ping() service_interface.SignalComplex() From 8bd423d8f1370b42fd377c510104c7a0ce5d04d0 Mon Sep 17 00:00:00 2001 From: Mike Degatano Date: Mon, 19 Sep 2022 20:38:23 -0400 Subject: [PATCH 8/8] fix: remove MessageFlag import --- tests/client/test_properties.py | 1 - tests/client/test_signals.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/client/test_properties.py b/tests/client/test_properties.py index feaabe01..f3422863 100644 --- a/tests/client/test_properties.py +++ b/tests/client/test_properties.py @@ -1,7 +1,6 @@ import pytest from dbus_fast import DBusError, Message, aio, glib -from dbus_fast.constants import MessageFlag from dbus_fast.service import PropertyAccess, ServiceInterface, dbus_property from dbus_fast.signature import Variant from tests.util import check_gi_repository, skip_reason_no_gi diff --git a/tests/client/test_signals.py b/tests/client/test_signals.py index 12acfc6b..490f3d27 100644 --- a/tests/client/test_signals.py +++ b/tests/client/test_signals.py @@ -2,7 +2,7 @@ from dbus_fast import Message from dbus_fast.aio import MessageBus -from dbus_fast.constants import MessageFlag, RequestNameReply +from dbus_fast.constants import RequestNameReply from dbus_fast.introspection import Node from dbus_fast.service import ServiceInterface, signal from dbus_fast.signature import Variant