Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: handle kwargs in signal callback #26

Merged
merged 1 commit into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions src/dbus_fast/proxy_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,30 @@ def _message_handler(self, msg):
def _add_signal(self, intr_signal, interface):
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
not in [par.kind for par in fn_signature.parameters.values()]
or len(fn_signature.parameters) - 1 > len(intr_signal.args)
if 0 < len(
[
par
for par in fn_signature.parameters.values()
if par.kind == inspect.Parameter.KEYWORD_ONLY
and par.default == inspect.Parameter.empty
]
):
raise TypeError(
f"reply_notify must be a function with {len(intr_signal.args)} parameters"
"reply_notify cannot have required keyword only parameters"
)

positional_params = [
par.kind
for par in fn_signature.parameters.values()
if par.kind
not in [inspect.Parameter.KEYWORD_ONLY, inspect.Parameter.VAR_KEYWORD]
]
if len(positional_params) != len(intr_signal.args) and (
inspect.Parameter.VAR_POSITIONAL not in positional_params
or len(positional_params) - 1 > len(intr_signal.args)
):
raise TypeError(
f"reply_notify must be a function with {len(intr_signal.args)} positional parameters"
)

if not self._signal_handlers:
Expand Down
69 changes: 69 additions & 0 deletions tests/client/test_signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,75 @@ def varargs_plus_handler(value, *_):
bus2.disconnect()


@pytest.mark.asyncio
async def test_kwargs_callback():
"""Test callback for signal with kwargs."""
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",
)
)

kwargs_handler_counter = 0
kwargs_handler_err = None
kwarg_default_handler_counter = 0
kwarg_default_handler_err = None

def kwargs_handler(value, **_):
nonlocal kwargs_handler_counter
nonlocal kwargs_handler_err
try:
assert value == "hello"
kwargs_handler_counter += 1
except AssertionError as ex:
kwargs_handler_err = ex

def kwarg_default_handler(value, *, _=True):
nonlocal kwarg_default_handler_counter
nonlocal kwarg_default_handler_err
try:
assert value == "hello"
kwarg_default_handler_counter += 1
except AssertionError as ex:
kwarg_default_handler_err = ex

interface.on_some_signal(kwargs_handler)
interface.on_some_signal(kwarg_default_handler)
await ping()

service_interface.SomeSignal()
await ping()
assert kwargs_handler_err is None
assert kwargs_handler_counter == 1
assert kwarg_default_handler_err is None
assert kwarg_default_handler_counter == 1

def kwarg_bad_handler(value, *, bad_kwarg):
pass

with pytest.raises(TypeError):
interface.on_some_signal(kwarg_bad_handler)

bus1.disconnect()
bus2.disconnect()


@pytest.mark.asyncio
async def test_on_signal_type_error():
"""Test on callback raises type errors for invalid callbacks."""
Expand Down