Skip to content

Commit 615218f

Browse files
author
Tony Crisci
committed
Lazily add high level client match rules
Makes the low level client and high level service a bit more predictable to not have to deal with a default match rule and should lower connection overhead by eliminating a call in those cases. Also fix several bugs.
1 parent 6a001ac commit 615218f

File tree

8 files changed

+162
-75
lines changed

8 files changed

+162
-75
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ dist: xenial
44
services:
55
- docker
66
before_install:
7-
- docker build -t dbus-next .
7+
- docker build -t dbus-next-test .
88
script:
9-
- docker run -it dbus-next
9+
- docker run -it dbus-next-test

Makefile

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: lint check format test docker-test coverage clean publish docs livedocs all
1+
.PHONY: lint check format test docker-test clean publish docs livedocs all
22
.DEFAULT_GOAL := all
33

44
source_dirs = dbus_next test examples
@@ -13,14 +13,11 @@ format:
1313
yapf -rip $(source_dirs)
1414

1515
test:
16-
dbus-run-session python3 -m pytest -s
16+
dbus-run-session python3 -m pytest -svv --cov=dbus_next
1717

1818
docker-test:
19-
docker build -t dbus-next38 .
20-
docker run -it dbus-next38
21-
22-
coverage:
23-
dbus-run-session pytest --cov=dbus_next
19+
docker build -t dbus-next-test .
20+
docker run -it dbus-next-test
2421

2522
clean:
2623
rm -rf dist dbus_next.egg-info build docs/_build

dbus_next/aio/message_bus.py

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from ..message import Message
44
from ..constants import BusType, NameFlag, RequestNameReply, ReleaseNameReply, MessageType, MessageFlag
55
from ..service import ServiceInterface
6-
from ..errors import AuthError, DBusError
6+
from ..errors import AuthError
77
from .proxy_object import ProxyObject
88
from .. import introspection as intr
99
from ..auth import Authenticator, AuthExternal
@@ -81,34 +81,14 @@ def on_hello(reply, err):
8181
self._buffered_messages.clear()
8282
future.set_result(self)
8383

84-
def on_match_added(reply, err):
85-
if err:
86-
logging.error('adding match to "NameOwnerChanged" failed')
87-
self.disconnect()
88-
self._finalize(err)
89-
elif reply.message_type == MessageType.ERROR:
90-
logging.error('adding match to "NameOwnerChanged" failed')
91-
self.disconnect()
92-
self._finalize(DBusError._from_message(reply))
93-
9484
hello_msg = Message(destination='org.freedesktop.DBus',
9585
path='/org/freedesktop/DBus',
9686
interface='org.freedesktop.DBus',
9787
member='Hello',
9888
serial=self.next_serial())
9989

100-
add_match_msg = Message(destination='org.freedesktop.DBus',
101-
path='/org/freedesktop/DBus',
102-
interface='org.freedesktop.DBus',
103-
member='AddMatch',
104-
signature='s',
105-
body=[self._name_owner_match_rule],
106-
serial=self.next_serial())
107-
10890
self._method_return_handlers[hello_msg.serial] = on_hello
109-
self._method_return_handlers[add_match_msg.serial] = on_match_added
11091
self._stream.write(hello_msg._marshall())
111-
self._stream.write(add_match_msg._marshall())
11292
self._stream.flush()
11393

11494
return await future

dbus_next/glib/message_bus.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from .. import introspection as intr
99
from ..auth import Authenticator, AuthExternal
1010

11-
import logging
1211
import io
1312
from typing import Callable, Optional
1413

@@ -197,30 +196,14 @@ def on_hello(reply, err):
197196
if connect_notify:
198197
connect_notify(self, err)
199198

200-
def on_match_added(reply, err):
201-
if err:
202-
logging.error(f'adding match to "NameOwnerChanged" failed: {err}')
203-
self.disconnect()
204-
return
205-
206199
hello_msg = Message(destination='org.freedesktop.DBus',
207200
path='/org/freedesktop/DBus',
208201
interface='org.freedesktop.DBus',
209202
member='Hello',
210203
serial=self.next_serial())
211204

212-
add_match_msg = Message(destination='org.freedesktop.DBus',
213-
path='/org/freedesktop/DBus',
214-
interface='org.freedesktop.DBus',
215-
member='AddMatch',
216-
signature='s',
217-
body=[self._name_owner_match_rule],
218-
serial=self.next_serial())
219-
220205
self._method_return_handlers[hello_msg.serial] = on_hello
221-
self._method_return_handlers[add_match_msg.serial] = on_match_added
222206
self._stream.write(hello_msg._marshall())
223-
self._stream.write(add_match_msg._marshall())
224207
self._stream.flush()
225208

226209
self._authenticate(authenticate_notify)

dbus_next/message_bus.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,22 @@ def __init__(self,
5959
self._stream = self._sock.makefile('rwb')
6060
self._fd = self._sock.fileno()
6161
self._user_message_handlers = []
62+
# the key is the name and the value is the unique name of the owner.
63+
# This cache is kept up to date by the NameOwnerChanged signal and is
64+
# used to route messages to the correct proxy object. (used for the
65+
# high level client only)
6266
self._name_owners = {}
67+
# used for the high level service
6368
self._path_exports = {}
6469
self._bus_address = parse_address(bus_address) if bus_address else parse_address(
6570
get_bus_address(bus_type))
66-
# the bus implementations need this rule to work correctly
71+
# the bus implementations need this rule for the high level client to
72+
# work correctly.
6773
self._name_owner_match_rule = "sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',path='/org/freedesktop/DBus',member='NameOwnerChanged'"
6874
# _match_rules: the keys are match rules and the values are ref counts
75+
# (used for the high level client only)
6976
self._match_rules = {}
77+
self._high_level_client_initialized = False
7078
self._ProxyObject = ProxyObject
7179

7280
# machine id is lazy loaded
@@ -351,6 +359,8 @@ def get_proxy_object(self, bus_name: str, path: str,
351359
if self._ProxyObject is None:
352360
raise Exception('the message bus implementation did not provide a proxy object class')
353361

362+
self._init_high_level_client()
363+
354364
return self._ProxyObject(bus_name, path, introspection, self)
355365

356366
def disconnect(self):
@@ -631,8 +641,8 @@ def _process_message(self, msg):
631641
[name, old_owner, new_owner] = msg.body
632642
if new_owner:
633643
self._name_owners[name] = new_owner
634-
elif old_owner in self._name_owners:
635-
del self._name_owners[old_owner]
644+
elif name in self._name_owners:
645+
del self._name_owners[name]
636646

637647
elif msg.message_type == MessageType.METHOD_CALL:
638648
if not handled:
@@ -832,7 +842,35 @@ def _get_all_properties(self, interface):
832842

833843
return result
834844

845+
def _init_high_level_client(self):
846+
'''The high level client is initialized when the first proxy object is
847+
gotten. Currently just sets up the match rules for the name owner cache
848+
so signals can be routed to the right objects.'''
849+
if self._high_level_client_initialized:
850+
return
851+
self._high_level_client_initialized = True
852+
853+
def add_match_notify(msg, err):
854+
if err:
855+
logging.error(
856+
f'add match request failed. match="{self._name_owner_match_rule}", {err}')
857+
if msg.message_type == MessageType.ERROR:
858+
logging.error(
859+
f'add match request failed. match="{self._name_owner_match_rule}", {msg.body[0]}'
860+
)
861+
862+
self._call(
863+
Message(destination='org.freedesktop.DBus',
864+
interface='org.freedesktop.DBus',
865+
path='/org/freedesktop/DBus',
866+
member='AddMatch',
867+
signature='s',
868+
body=[self._name_owner_match_rule]), add_match_notify)
869+
835870
def _add_match_rule(self, match_rule):
871+
'''Add a match rule. Match rules added by this function are refcounted
872+
and must be removed by _remove_match_rule(). This is for use in the
873+
high level client only.'''
836874
if match_rule == self._name_owner_match_rule:
837875
return
838876

@@ -857,6 +895,8 @@ def add_match_notify(msg, err):
857895
body=[match_rule]), add_match_notify)
858896

859897
def _remove_match_rule(self, match_rule):
898+
'''Remove a match rule added with _add_match_rule(). This is for use in
899+
the high level client only.'''
860900
if match_rule == self._name_owner_match_rule:
861901
return
862902

dbus_next/proxy_object.py

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -71,24 +71,31 @@ def _add_property(self, intr_property):
7171
raise NotImplementedError('this must be implemented in the inheriting class')
7272

7373
def _message_handler(self, msg):
74-
if msg._matches(message_type=MessageType.SIGNAL,
75-
interface=self.introspection.name,
76-
path=self.path) and msg.member in self._signal_handlers:
77-
if msg.sender != self.bus_name and self.bus._name_owners.get(self.bus_name,
78-
'') != msg.sender:
79-
return
80-
match = [s for s in self.introspection.signals if s.name == msg.member]
81-
if not len(match):
82-
return
83-
intr_signal = match[0]
84-
if intr_signal.signature != msg.signature:
85-
logging.warning(
86-
f'got signal "{self.introspection.name}.{msg.member}" with unexpected signature "{msg.signature}"'
87-
)
88-
return
89-
90-
for handler in self._signal_handlers[msg.member]:
91-
handler(*msg.body)
74+
if not msg._matches(message_type=MessageType.SIGNAL,
75+
interface=self.introspection.name,
76+
path=self.path) or msg.member not in self._signal_handlers:
77+
return
78+
79+
if msg.sender != self.bus_name and self.bus._name_owners.get(self.bus_name,
80+
'') != msg.sender:
81+
# The sender is always a unique name, but the bus name given might
82+
# be a well known name. If the sender isn't an exact match, check
83+
# to see if it owns the bus_name we were given from the cache kept
84+
# on the bus for this purpose.
85+
return
86+
87+
match = [s for s in self.introspection.signals if s.name == msg.member]
88+
if not len(match):
89+
return
90+
intr_signal = match[0]
91+
if intr_signal.signature != msg.signature:
92+
logging.warning(
93+
f'got signal "{self.introspection.name}.{msg.member}" with unexpected signature "{msg.signature}"'
94+
)
95+
return
96+
97+
for handler in self._signal_handlers[msg.member]:
98+
handler(*msg.body)
9299

93100
def _add_signal(self, intr_signal, interface):
94101
def on_signal_fn(fn):
@@ -218,12 +225,15 @@ def get_interface(self, name: str) -> BaseProxyInterface:
218225
def get_owner_notify(msg, err):
219226
if err:
220227
logging.error(f'getting name owner for "{name}" failed, {err}')
228+
return
221229
if msg.message_type == MessageType.ERROR:
222-
logging.error(f'getting name owner for "{name}" failed, {msg.body[0]}')
230+
if msg.error_name != ErrorType.NAME_HAS_NO_OWNER.value:
231+
logging.error(f'getting name owner for "{name}" failed, {msg.body[0]}')
232+
return
223233

224234
self.bus._name_owners[self.bus_name] = msg.body[0]
225235

226-
if self.bus_name[0] != ':':
236+
if self.bus_name[0] != ':' and not self.bus._name_owners.get(self.bus_name, ''):
227237
self.bus._call(
228238
Message(destination='org.freedesktop.DBus',
229239
interface='org.freedesktop.DBus',

docs/low-level-interface/index.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ The primary methods and classes of the low-level interface are:
2323
- :func:`glib.MessageBus.call() <dbus_next.glib.MessageBus.call>`
2424
- :func:`glib.MessageBus.call_sync() <dbus_next.glib.MessageBus.call_sync>`
2525

26+
Mixed use of the low and high level interfaces on the same bus connection is not recommended.
27+
2628
:example: Call a standard interface
2729

2830
.. code-block:: python3
@@ -41,7 +43,7 @@ The primary methods and classes of the low-level interface are:
4143
4244
print(reply.body[0])
4345
44-
:example: A custom method handler
46+
:example: A custom method handler. Note that to receive these messages, you must `add a match rule <https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules>`_ for the types of messages you want to receive.
4547

4648
.. code-block:: python3
4749

0 commit comments

Comments
 (0)