feat: speed up to processing bluez passive data (#221)

This commit is contained in:
J. Nick Koston 2023-08-09 00:44:33 -10:00 committed by GitHub
parent 71e6fdfc97
commit 8e7432d31b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 88 additions and 23 deletions

View File

@ -27,6 +27,8 @@ cdef object LITTLE_ENDIAN
cdef object PROTOCOL_VERSION cdef object PROTOCOL_VERSION
cdef object MESSAGE_FLAG cdef object MESSAGE_FLAG
cdef object MESSAGE_FLAG_NONE
cdef object MESSAGE_TYPE_METHOD_CALL
cdef get_signature_tree cdef get_signature_tree

View File

@ -30,6 +30,9 @@ HEADER_UNIX_FDS = HeaderField.UNIX_FDS.value
MESSAGE_FLAG = MessageFlag MESSAGE_FLAG = MessageFlag
MESSAGE_FLAG_NONE = MessageFlag.NONE
MESSAGE_TYPE_METHOD_CALL = MessageType.METHOD_CALL
class Message: class Message:
"""A class for sending and receiving messages through the """A class for sending and receiving messages through the
@ -101,8 +104,8 @@ class Message:
path: Optional[str] = None, path: Optional[str] = None,
interface: Optional[str] = None, interface: Optional[str] = None,
member: Optional[str] = None, member: Optional[str] = None,
message_type: MessageType = MessageType.METHOD_CALL, message_type: MessageType = MESSAGE_TYPE_METHOD_CALL,
flags: Union[MessageFlag, int] = MessageFlag.NONE, flags: Union[MessageFlag, int] = MESSAGE_FLAG_NONE,
error_name: Optional[Union[str, ErrorType]] = None, error_name: Optional[Union[str, ErrorType]] = None,
reply_serial: int = 0, reply_serial: int = 0,
sender: Optional[str] = None, sender: Optional[str] = None,
@ -153,6 +156,22 @@ class Message:
if not getattr(self, field): if not getattr(self, field):
raise InvalidMessageError(f"missing required field: {field}") raise InvalidMessageError(f"missing required field: {field}")
def __repr__(self) -> str:
"""Return a string representation of this message."""
return (
f"<Message {self.message_type.name} "
f"serial={self.serial} "
f"reply_serial={self.reply_serial} "
f"sender={self.sender} "
f"destination={self.destination} "
f"path={self.path} "
f"interface={self.interface} "
f"member={self.member} "
f"error_name={self.error_name} "
f"signature={self.signature} "
f"body={self.body}>"
)
@staticmethod @staticmethod
def new_error( def new_error(
msg: "Message", error_name: Union[str, ErrorType], error_text: str msg: "Message", error_name: Union[str, ErrorType], error_text: str

View File

@ -10,12 +10,14 @@ cdef object MessageFlag
cdef object MESSAGE_TYPE_CALL cdef object MESSAGE_TYPE_CALL
cdef object MESSAGE_TYPE_SIGNAL cdef object MESSAGE_TYPE_SIGNAL
cdef object NO_REPLY_EXPECTED_VALUE cdef object NO_REPLY_EXPECTED
cdef object BLOCK_UNEXPECTED_REPLY
cdef object assert_object_path_valid cdef object assert_object_path_valid
cdef object assert_bus_name_valid cdef object assert_bus_name_valid
cdef _expects_reply(Message msg) cdef _expects_reply(Message msg)
cdef class SendReply: cdef class SendReply:
cdef object _bus cdef object _bus
@ -28,12 +30,12 @@ cdef class BaseMessageBus:
cdef public object _user_disconnect cdef public object _user_disconnect
cdef public object _method_return_handlers cdef public object _method_return_handlers
cdef public object _serial cdef public object _serial
cdef public object _path_exports cdef public cython.dict _path_exports
cdef public cython.list _user_message_handlers cdef public cython.list _user_message_handlers
cdef public object _name_owners cdef public cython.dict _name_owners
cdef public object _bus_address cdef public object _bus_address
cdef public object _name_owner_match_rule cdef public object _name_owner_match_rule
cdef public object _match_rules cdef public cython.dict _match_rules
cdef public object _high_level_client_initialized cdef public object _high_level_client_initialized
cdef public object _ProxyObject cdef public object _ProxyObject
cdef public object _machine_id cdef public object _machine_id
@ -45,7 +47,9 @@ cdef class BaseMessageBus:
cpdef _process_message(self, Message msg) cpdef _process_message(self, Message msg)
@cython.locals( @cython.locals(
methods=cython.list,
method=_Method, method=_Method,
interface=ServiceInterface interface=ServiceInterface,
interfaces=cython.list,
) )
cdef _find_message_handler(self, Message msg) cdef _find_message_handler(self, Message msg)

View File

@ -27,13 +27,34 @@ from .validators import assert_bus_name_valid, assert_object_path_valid
MESSAGE_TYPE_CALL = MessageType.METHOD_CALL MESSAGE_TYPE_CALL = MessageType.METHOD_CALL
MESSAGE_TYPE_SIGNAL = MessageType.SIGNAL MESSAGE_TYPE_SIGNAL = MessageType.SIGNAL
NO_REPLY_EXPECTED_VALUE = MessageFlag.NO_REPLY_EXPECTED.value NO_REPLY_EXPECTED = MessageFlag.NO_REPLY_EXPECTED
_LOGGER = logging.getLogger(__name__)
_Message = Message _Message = Message
def _expects_reply(msg: _Message) -> bool: def _expects_reply(msg: _Message) -> bool:
return not (msg.flags.value & NO_REPLY_EXPECTED_VALUE) """Whether a message expects a reply."""
return not (msg.flags & NO_REPLY_EXPECTED)
def _block_unexpected_reply(reply: _Message) -> None:
"""Block a reply if it's not expected.
Previously we silently ignored replies that were not expected, but this
lead to implementation errors that were hard to debug. Now we log a
debug message instead.
"""
_LOGGER.debug(
"Blocked attempt to send a reply from handler "
"that received a message with flag "
"MessageFlag.NO_REPLY_EXPECTED: %s",
reply,
)
BLOCK_UNEXPECTED_REPLY = _block_unexpected_reply
class SendReply: class SendReply:
@ -50,8 +71,7 @@ class SendReply:
return self return self
def __call__(self, reply: Message) -> None: def __call__(self, reply: Message) -> None:
if _expects_reply(self._msg): self._bus.send(reply)
self._bus.send(reply)
def _exit( def _exit(
self, self,
@ -855,9 +875,19 @@ class BaseMessageBus:
if msg.message_type is MESSAGE_TYPE_CALL: if msg.message_type is MESSAGE_TYPE_CALL:
if not handled: if not handled:
handler = self._find_message_handler(msg) handler = self._find_message_handler(msg)
if not _expects_reply(msg):
if handler:
handler(msg, BLOCK_UNEXPECTED_REPLY)
else:
_LOGGER.error(
'"%s.%s" with signature "%s" could not be found',
msg.interface,
msg.member,
msg.signature,
)
return
send_reply = SendReply(self, msg) send_reply = SendReply(self, msg)
with send_reply: with send_reply:
if handler: if handler:
handler(msg, send_reply) handler(msg, send_reply)
@ -892,8 +922,11 @@ class BaseMessageBus:
def _callback_method_handler( def _callback_method_handler(
msg: Message, send_reply: Callable[[Message], None] msg: Message, send_reply: Callable[[Message], None]
) -> None: ) -> None:
result = method_fn(interface, *msg_body_to_args(msg))
if not _expects_reply(msg):
return
body, fds = fn_result_to_body( body, fds = fn_result_to_body(
method_fn(interface, *msg_body_to_args(msg)), result,
signature_tree=out_signature_tree, signature_tree=out_signature_tree,
replace_fds=negotiate_unix_fd, replace_fds=negotiate_unix_fd,
) )
@ -911,8 +944,8 @@ class BaseMessageBus:
return _callback_method_handler return _callback_method_handler
def _find_message_handler( def _find_message_handler(
self, msg self, msg: _Message
) -> Optional[Callable[[Message, Callable], None]]: ) -> Optional[Callable[[Message, Callable[[Message], None]], None]]:
if ( if (
msg.interface == "org.freedesktop.DBus.Introspectable" msg.interface == "org.freedesktop.DBus.Introspectable"
and msg.member == "Introspect" and msg.member == "Introspect"
@ -937,8 +970,12 @@ class BaseMessageBus:
msg_path = msg.path msg_path = msg.path
if msg_path: if msg_path:
for interface in self._path_exports.get(msg_path, []): interfaces = self._path_exports.get(msg_path)
for method in ServiceInterface._get_methods(interface): if not interfaces:
return None
for interface in interfaces:
methods = ServiceInterface._get_methods(interface)
for method in methods:
if method.disabled: if method.disabled:
continue continue

View File

@ -2,17 +2,19 @@
import cython import cython
from .signature cimport SignatureTree
cdef class _Method: cdef class _Method:
cdef public object name cdef public str name
cdef public object fn cdef public object fn
cdef public object disabled cdef public object disabled
cdef public object introspection cdef public object introspection
cdef public object in_signature cdef public str in_signature
cdef public object out_signature cdef public str out_signature
cdef public object in_signature_tree cdef public SignatureTree in_signature_tree
cdef public object out_signature_tree cdef public SignatureTree out_signature_tree
cdef class ServiceInterface: cdef class ServiceInterface:

View File

@ -36,7 +36,7 @@ if TYPE_CHECKING:
class _Method: class _Method:
def __init__(self, fn, name, disabled=False): def __init__(self, fn, name: str, disabled=False):
in_signature = "" in_signature = ""
out_signature = "" out_signature = ""

View File

@ -602,5 +602,6 @@ def test_unmarshall_bluez_passive_message():
unmarshaller = Unmarshaller(stream) unmarshaller = Unmarshaller(stream)
unmarshaller.unmarshall() unmarshaller.unmarshall()
message = unmarshaller.message message = unmarshaller.message
assert "/org/bluez/hci0/dev_58_D3_49_E6_02_6E" in str(message)
unpacked = unpack_variants(message.body) unpacked = unpack_variants(message.body)
assert unpacked == ["/org/bluez/hci0/dev_58_D3_49_E6_02_6E"] assert unpacked == ["/org/bluez/hci0/dev_58_D3_49_E6_02_6E"]