diff --git a/src/dbus_fast/message_bus.pxd b/src/dbus_fast/message_bus.pxd index 58e883b2..723d701b 100644 --- a/src/dbus_fast/message_bus.pxd +++ b/src/dbus_fast/message_bus.pxd @@ -57,3 +57,11 @@ cdef class BaseMessageBus: cpdef _call(self, Message msg, object callback) cpdef next_serial(self) + + cpdef void _callback_method_handler( + self, + ServiceInterface interface, + _Method method, + Message msg, + object send_reply + ) diff --git a/src/dbus_fast/message_bus.py b/src/dbus_fast/message_bus.py index b13f9bad..55a6fe09 100644 --- a/src/dbus_fast/message_bus.py +++ b/src/dbus_fast/message_bus.py @@ -865,67 +865,64 @@ def _process_message(self, msg: _Message) -> None: return_handler(msg, None) del self._method_return_handlers[msg.reply_serial] + def _callback_method_handler( + self, + interface: ServiceInterface, + method: _Method, + msg: Message, + send_reply: Callable[[Message], None], + ) -> None: + """This is the callback that will be called when a method call is.""" + args = ServiceInterface._c_msg_body_to_args(msg) if msg.unix_fds else msg.body + result = method.fn(interface, *args) + if send_reply is BLOCK_UNEXPECTED_REPLY or _expects_reply(msg) is False: + return + body, fds = ServiceInterface._c_fn_result_to_body( + result, + signature_tree=method.out_signature_tree, + replace_fds=self._negotiate_unix_fd, + ) + send_reply( + Message( + message_type=MessageType.METHOD_RETURN, + reply_serial=msg.serial, + destination=msg.sender, + signature=method.out_signature, + body=body, + unix_fds=fds, + ) + ) + def _make_method_handler( self, interface: ServiceInterface, method: _Method ) -> Callable[[Message, Callable[[Message], None]], None]: - method_fn = method.fn - out_signature_tree = method.out_signature_tree - negotiate_unix_fd = self._negotiate_unix_fd - out_signature = method.out_signature - message_type_method_return = MessageType.METHOD_RETURN - msg_body_to_args = ServiceInterface._msg_body_to_args - fn_result_to_body = ServiceInterface._fn_result_to_body - - def _callback_method_handler( - msg: Message, send_reply: Callable[[Message], None] - ) -> None: - """This is the callback that will be called when a method call is.""" - args = msg_body_to_args(msg) if msg.unix_fds else msg.body - result = method_fn(interface, *args) - if send_reply is BLOCK_UNEXPECTED_REPLY or _expects_reply(msg) is False: - return - body, fds = fn_result_to_body( - result, - signature_tree=out_signature_tree, - replace_fds=negotiate_unix_fd, - ) - send_reply( - Message( - message_type=message_type_method_return, - reply_serial=msg.serial, - destination=msg.sender, - signature=out_signature, - body=body, - unix_fds=fds, - ) - ) - - return _callback_method_handler + return partial(self._callback_method_handler, interface, method) def _find_message_handler( self, msg: _Message ) -> Optional[Callable[[Message, Callable[[Message], None]], None]]: - if ( - msg.interface == "org.freedesktop.DBus.Introspectable" - and msg.member == "Introspect" - and msg.signature == "" - ): - return self._default_introspect_handler + if msg.interface.startswith("org.freedesktop.DBus."): + if ( + msg.interface == "org.freedesktop.DBus.Introspectable" + and msg.member == "Introspect" + and msg.signature == "" + ): + return self._default_introspect_handler - if msg.interface == "org.freedesktop.DBus.Properties": - return self._default_properties_handler + if msg.interface == "org.freedesktop.DBus.Properties": + return self._default_properties_handler - if msg.interface == "org.freedesktop.DBus.Peer": - if msg.member == "Ping" and msg.signature == "": - return self._default_ping_handler - elif msg.member == "GetMachineId" and msg.signature == "": - return self._default_get_machine_id_handler + if msg.interface == "org.freedesktop.DBus.Peer": + if msg.member == "Ping" and msg.signature == "": + return self._default_ping_handler + elif msg.member == "GetMachineId" and msg.signature == "": + return self._default_get_machine_id_handler - if ( - msg.interface == "org.freedesktop.DBus.ObjectManager" - and msg.member == "GetManagedObjects" - ): - return self._default_get_managed_objects_handler + if ( + msg.interface == "org.freedesktop.DBus.ObjectManager" + and msg.member == "GetManagedObjects" + ): + return self._default_get_managed_objects_handler msg_path = msg.path if msg_path: diff --git a/src/dbus_fast/service.pxd b/src/dbus_fast/service.pxd index 8cf1adc9..40a2ad3c 100644 --- a/src/dbus_fast/service.pxd +++ b/src/dbus_fast/service.pxd @@ -2,6 +2,7 @@ import cython +from .message cimport Message from .signature cimport SignatureTree @@ -16,6 +17,14 @@ cdef class _Method: cdef public SignatureTree in_signature_tree cdef public SignatureTree out_signature_tree + + +cdef tuple _real_fn_result_to_body( + object result, + SignatureTree signature_tree, + bint replace_fds +) + cdef class ServiceInterface: cdef public str name @@ -30,3 +39,13 @@ cdef class ServiceInterface: @staticmethod cdef object _c_get_handler(ServiceInterface interface, _Method method, object bus) + + @staticmethod + cdef list _c_msg_body_to_args(Message msg) + + @staticmethod + cdef tuple _c_fn_result_to_body( + object result, + SignatureTree signature_tree, + bint replace_fds, + ) diff --git a/src/dbus_fast/service.py b/src/dbus_fast/service.py index 70281904..25fe1028 100644 --- a/src/dbus_fast/service.py +++ b/src/dbus_fast/service.py @@ -326,6 +326,35 @@ def decorator(fn): return decorator +def _real_fn_result_to_body( + result: Optional[Any], + signature_tree: SignatureTree, + replace_fds: bool, +) -> Tuple[List[Any], List[int]]: + out_len = len(signature_tree.types) + if result is None: + final_result = [] + else: + if out_len == 1: + final_result = [result] + else: + result_type = type(result) + if result_type is not list and result_type is not tuple: + raise SignatureBodyMismatchError( + "Expected signal to return a list or tuple of arguments" + ) + final_result = result + + if out_len != len(final_result): + raise SignatureBodyMismatchError( + f"Signature and function return mismatch, expected {len(signature_tree.types)} arguments but got {len(result)}" + ) + + if not replace_fds: + return final_result, [] + return replace_fds_with_idx(signature_tree, final_result) + + class ServiceInterface: """An abstract class that can be extended by the user to define DBus services. @@ -505,6 +534,11 @@ def _remove_bus(interface: "ServiceInterface", bus: "BaseMessageBus") -> None: @staticmethod def _msg_body_to_args(msg: Message) -> List[Any]: + return ServiceInterface._c_msg_body_to_args(msg) + + @staticmethod + def _c_msg_body_to_args(msg: Message) -> List[Any]: + # https://github.com/cython/cython/issues/3327 if not signature_contains_type(msg.signature_tree, msg.body, "h"): return msg.body @@ -520,31 +554,20 @@ def _fn_result_to_body( result: Optional[Any], signature_tree: SignatureTree, replace_fds: bool = True, + ) -> Tuple[List[Any], List[int]]: + return _real_fn_result_to_body(result, signature_tree, replace_fds) + + @staticmethod + def _c_fn_result_to_body( + result: Optional[Any], + signature_tree: SignatureTree, + replace_fds: bool, ) -> Tuple[List[Any], List[int]]: """The high level interfaces may return single values which may be wrapped in a list to be a message body. Also they may return fds directly for type 'h' which need to be put into an external list.""" - out_len = len(signature_tree.types) - if result is None: - result = [] - else: - if out_len == 1: - result = [result] - else: - result_type = type(result) - if result_type is not list and result_type is not tuple: - raise SignatureBodyMismatchError( - "Expected signal to return a list or tuple of arguments" - ) - - if out_len != len(result): - raise SignatureBodyMismatchError( - f"Signature and function return mismatch, expected {len(signature_tree.types)} arguments but got {len(result)}" - ) - - if not replace_fds: - return result, [] - return replace_fds_with_idx(signature_tree, result) + # https://github.com/cython/cython/issues/3327 + return _real_fn_result_to_body(result, signature_tree, replace_fds) @staticmethod def _handle_signal(