Added option for methods to inject the caller bus name into a keyword argument

This commit is contained in:
Ezri Brimhall 2025-05-28 14:10:29 -06:00
parent 44b6d309d8
commit d7dcb6c952
Signed by: ezri
GPG Key ID: 058A78E5680C6F24
2 changed files with 31 additions and 4 deletions

View File

@ -461,7 +461,12 @@ class MessageBus(BaseMessageBus):
) -> None:
"""A coroutine method handler."""
args = msg_body_to_args(msg) if msg.unix_fds else msg.body
fut: asyncio.Future = asyncio.ensure_future(method.fn(interface, *args))
kwargs = {}
if method.caller_argument is not None:
kwargs = {method.caller_argument: msg.sender}
fut: asyncio.Future = asyncio.ensure_future(
method.fn(interface, *args, **kwargs)
)
# Hold a strong reference to the future to ensure
# it is not garbage collected before it is done.
self._pending_futures.add(fut)

View File

@ -38,7 +38,11 @@ class _MethodCallbackProtocol(Protocol):
class _Method:
def __init__(
self, fn: _MethodCallbackProtocol, name: str, disabled: bool = False
self,
fn: _MethodCallbackProtocol,
name: str,
disabled: bool = False,
caller_arg: str | None = None,
) -> None:
in_signature = ""
out_signature = ""
@ -50,6 +54,9 @@ class _Method:
if i == 0:
# first is self
continue
if param.name == caller_arg:
# this is an internal argument and is not exposed to D-Bus
continue
annotation = parse_annotation(param.annotation)
if not annotation:
raise ValueError(
@ -72,9 +79,12 @@ class _Method:
self.out_signature = out_signature
self.in_signature_tree = get_signature_tree(in_signature)
self.out_signature_tree = get_signature_tree(out_signature)
self.caller_argument = caller_arg
def method(name: str | None = None, disabled: bool = False) -> Callable:
def method(
name: str | None = None, disabled: bool = False, inject_caller: bool | str = False
) -> Callable:
"""A decorator to mark a class method of a :class:`ServiceInterface` to be a DBus service method.
The parameters and return value must each be annotated with a signature
@ -94,6 +104,8 @@ def method(name: str | None = None, disabled: bool = False) -> Callable:
:type name: str
:param disabled: If set to true, the method will not be visible to clients.
:type disabled: bool
:param inject_caller: If set to true, will inject the caller bus name into the `caller` kwarg. If set to a string, will instead use that value as the kwarg.
:type inject_caller: str | bool
:example:
@ -111,6 +123,14 @@ def method(name: str | None = None, disabled: bool = False) -> Callable:
raise TypeError("name must be a string")
if type(disabled) is not bool:
raise TypeError("disabled must be a bool")
if type(inject_caller) is not bool or type(caller_arg) is not str:
raise TypeError("inject_caller must be a string or bool")
caller_arg = None
if inject_caller is True:
caller_arg = "caller"
elif type(inject_caller) is str:
caller_arg = inject_caller
def decorator(fn: Callable) -> Callable:
@wraps(fn)
@ -118,7 +138,9 @@ def method(name: str | None = None, disabled: bool = False) -> Callable:
fn(*args, **kwargs)
fn_name = name if name else fn.__name__
wrapped.__dict__["__DBUS_METHOD"] = _Method(fn, fn_name, disabled=disabled)
wrapped.__dict__["__DBUS_METHOD"] = _Method(
fn, fn_name, disabled=disabled, caller_arg=caller_arg
)
return wrapped