From e23e06c08e5c3c691cdde616b9f539cba16c60b8 Mon Sep 17 00:00:00 2001 From: Jeroen Hofstee Date: Mon, 10 Oct 2022 14:54:43 +0200 Subject: Add support for arg0namespace in add_signal_receiver Match messages whose first argument is a string and start with the value given to arg0namespace. This is primarily intended for watching name owner changes for a group of related bus names, rather than for a single name or all name changes. Signed-off-by: Jeroen Hofstee --- dbus/connection.py | 24 ++++++++++++++++++++++-- dbus/proxies.py | 5 +++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/dbus/connection.py b/dbus/connection.py index 46d74d4..621e54b 100644 --- a/dbus/connection.py +++ b/dbus/connection.py @@ -55,7 +55,9 @@ class SignalMatch(object): '_byte_arrays', '_conn_weakref', '_destination_keyword', '_interface_keyword', '_message_keyword', '_member_keyword', - '_sender_keyword', '_path_keyword', '_int_args_match'] + '_sender_keyword', '_path_keyword', '_int_args_match', + '_arg0namespace' + ] __slots__ = tuple(_slots) @@ -64,7 +66,7 @@ class SignalMatch(object): sender_keyword=None, path_keyword=None, interface_keyword=None, member_keyword=None, message_keyword=None, destination_keyword=None, - **kwargs): + arg0namespace=None, **kwargs): if member is not None: validate_member_name(member) if dbus_interface is not None: @@ -80,6 +82,7 @@ class SignalMatch(object): self._interface = dbus_interface self._member = member self._path = object_path + self._arg0namespace = arg0namespace self._handler = handler # if the connection is actually a bus, it's responsible for changing @@ -141,6 +144,8 @@ class SignalMatch(object): rule.append("interface='%s'" % self._interface) if self._member is not None: rule.append("member='%s'" % self._member) + if self._arg0namespace is not None: + rule.append("arg0namespace='%s'" % self._arg0namespace) if self._int_args_match is not None: for index, value in self._int_args_match.items(): rule.append("arg%d='%s'" % (index, value)) @@ -187,6 +192,16 @@ class SignalMatch(object): or not isinstance(args[index], String) or args[index] != value): return False + if self._arg0namespace is not None: + kwargs = dict(byte_arrays=True) + args = message.get_args_list(**kwargs) + namespace_len = len(self._arg0namespace) + if ( len(args) == 0 + or not isinstance(args[0], String) + or not args[0].startswith(self._arg0namespace) + or args[0][namespace_len:namespace_len + 1] not in ('', '.') + ): + return False # these have likely already been checked by the match tree if self._member not in (None, message.get_member()): @@ -382,6 +397,11 @@ class Connection(_Connection): is the value given for that keyword parameter. As of this time only string arguments can be matched (in particular, object paths and signatures can't). + `arg0namespace` : str + If not None (the default) match only signals where the first + argument is a string that either is equal to the + keyword parameter, or starts with the keyword parameter + followed by a dot (and optionally more text). `named_service` : str A deprecated alias for `bus_name`. """ diff --git a/dbus/proxies.py b/dbus/proxies.py index 487976c..a31b5ea 100644 --- a/dbus/proxies.py +++ b/dbus/proxies.py @@ -361,6 +361,11 @@ class ProxyObject(object): is the value given for that keyword parameter. As of this time only string arguments can be matched (in particular, object paths and signatures can't). + `arg0namespace` : str + If not None (the default) match only signals where the first + argument is a string that either is equal to the + keyword parameter, or starts with the keyword parameter + followed by a dot (and optionally more text). """ return \ self._bus.add_signal_receiver(handler_function, -- cgit v1.2.3 From d159c6115f53b83b934e5537ba94268ce4bffa49 Mon Sep 17 00:00:00 2001 From: Jeroen Hofstee Date: Thu, 13 Apr 2023 11:16:31 +0200 Subject: test: Add basic coverage for arg0namespace Signed-off-by: Jeroen Hofstee --- test/test-standalone.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/test-standalone.py b/test/test-standalone.py index c091228..7cca6a0 100755 --- a/test/test-standalone.py +++ b/test/test-standalone.py @@ -581,6 +581,42 @@ class TestMatching(unittest.TestCase): self._message.append('/', signature='o') self.assertFalse(self._match.maybe_handle_message(self._message)) +class TestArg0Namespace(unittest.TestCase): + def setUp(self): + self._match = dbus.connection.SignalMatch(object, None, '/', None, None, + None, arg0namespace='org.freedesktop.dbus-python') + + def test_invalid_arg1namespace(self): + try: + dbus.connection.SignalMatch(object, None, '/', None, None, + None, arg1namespace='org.freedesktop.dbus-python') + except TypeError: + pass + else: + raise AssertionError('arg1namespace is not a valid keyword argument') + + def test_namespace_match(self): + message = _dbus_bindings.SignalMessage('/', 'a.b', 'c') + message.append('org.freedesktop.dbus-python', signature='s') + self.assertTrue(self._match.maybe_handle_message(message)) + + def test_namespace_match2(self): + message = _dbus_bindings.SignalMessage('/', 'a.b', 'c') + message.append('org.freedesktop.dbus-python.test', signature='s') + self.assertTrue(self._match.maybe_handle_message(message)) + + def test_namespace_mismatch(self): + message = _dbus_bindings.SignalMessage('/', 'a.b', 'c') + message.append('org.freedesktop.dbus-python-test', signature='s') + self.assertFalse(self._match.maybe_handle_message(message)) + + def test_namespace_type_mismatch(self): + match = dbus.connection.SignalMatch(object, None, '/', None, None, + None, arg0namespace='1') + message = _dbus_bindings.SignalMessage('/', 'a.b', 'c') + message.append(1, signature='i') + self.assertFalse(match.maybe_handle_message(message)) + class TestVersion(unittest.TestCase): if sys.version_info[:2] < (2, 7): def assertGreater(self, first, second): -- cgit v1.2.3