1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 __all__ = ('BusConnection',)
24 __docformat__ = 'reStructuredText'
25
26 import logging
27 import weakref
28
29 from _dbus_bindings import validate_interface_name, validate_member_name,\
30 validate_bus_name, validate_object_path,\
31 validate_error_name,\
32 BUS_SESSION, BUS_STARTER, BUS_SYSTEM, \
33 DBUS_START_REPLY_SUCCESS, \
34 DBUS_START_REPLY_ALREADY_RUNNING, \
35 BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_DAEMON_IFACE,\
36 NAME_FLAG_ALLOW_REPLACEMENT, \
37 NAME_FLAG_DO_NOT_QUEUE, \
38 NAME_FLAG_REPLACE_EXISTING, \
39 RELEASE_NAME_REPLY_NON_EXISTENT, \
40 RELEASE_NAME_REPLY_NOT_OWNER, \
41 RELEASE_NAME_REPLY_RELEASED, \
42 REQUEST_NAME_REPLY_ALREADY_OWNER, \
43 REQUEST_NAME_REPLY_EXISTS, \
44 REQUEST_NAME_REPLY_IN_QUEUE, \
45 REQUEST_NAME_REPLY_PRIMARY_OWNER
46 from dbus.connection import Connection
47 from dbus.exceptions import DBusException
48 from dbus.lowlevel import HANDLER_RESULT_NOT_YET_HANDLED
49
50
51 _NAME_OWNER_CHANGE_MATCH = ("type='signal',sender='%s',"
52 "interface='%s',member='NameOwnerChanged',"
53 "path='%s',arg0='%%s'"
54 % (BUS_DAEMON_NAME, BUS_DAEMON_IFACE,
55 BUS_DAEMON_PATH))
56 """(_NAME_OWNER_CHANGE_MATCH % sender) matches relevant NameOwnerChange
57 messages"""
58
59 _NAME_HAS_NO_OWNER = 'org.freedesktop.DBus.Error.NameHasNoOwner'
60
61 _logger = logging.getLogger('dbus.bus')
62
63
65 __slots__ = ('_match', '_pending_call')
66
67 - def __init__(self, bus_conn, bus_name, callback):
72
73 def error_cb(e):
74 if e.get_dbus_name() == _NAME_HAS_NO_OWNER:
75 callback('')
76 else:
77 logging.basicConfig()
78 _logger.debug('GetNameOwner(%s) failed:', bus_name,
79 exc_info=(e.__class__, e, None))
80
81 self._match = bus_conn.add_signal_receiver(signal_cb,
82 'NameOwnerChanged',
83 BUS_DAEMON_IFACE,
84 BUS_DAEMON_NAME,
85 BUS_DAEMON_PATH,
86 arg0=bus_name)
87 self._pending_call = bus_conn.call_async(BUS_DAEMON_NAME,
88 BUS_DAEMON_PATH,
89 BUS_DAEMON_IFACE,
90 'GetNameOwner',
91 's', (bus_name,),
92 callback, error_cb,
93 utf8_strings=True)
94
102
103
105 """A connection to a D-Bus daemon that implements the
106 ``org.freedesktop.DBus`` pseudo-service.
107
108 :Since: 0.81.0
109 """
110
111 TYPE_SESSION = BUS_SESSION
112 """Represents a session bus (same as the global dbus.BUS_SESSION)"""
113
114 TYPE_SYSTEM = BUS_SYSTEM
115 """Represents the system bus (same as the global dbus.BUS_SYSTEM)"""
116
117 TYPE_STARTER = BUS_STARTER
118 """Represents the bus that started this service by activation (same as
119 the global dbus.BUS_STARTER)"""
120
121 START_REPLY_SUCCESS = DBUS_START_REPLY_SUCCESS
122 START_REPLY_ALREADY_RUNNING = DBUS_START_REPLY_ALREADY_RUNNING
123
125 bus = cls._new_for_bus(address_or_type, mainloop=mainloop)
126
127
128 bus._bus_names = weakref.WeakValueDictionary()
129
130 bus._signal_sender_matches = {}
131 """Map from SignalMatch to NameOwnerWatch."""
132
133 return bus
134
135 - def add_signal_receiver(self, handler_function, signal_name=None,
136 dbus_interface=None, bus_name=None,
137 path=None, **keywords):
138 named_service = keywords.pop('named_service', None)
139 if named_service is not None:
140 if bus_name is not None:
141 raise TypeError('bus_name and named_service cannot both be '
142 'specified')
143 bus_name = named_service
144 from warnings import warn
145 warn('Passing the named_service parameter to add_signal_receiver '
146 'by name is deprecated: please use positional parameters',
147 DeprecationWarning, stacklevel=2)
148
149 match = super(BusConnection, self).add_signal_receiver(
150 handler_function, signal_name, dbus_interface, bus_name,
151 path, **keywords)
152
153 if (bus_name is not None and bus_name != BUS_DAEMON_NAME):
154 if bus_name[:1] == ':':
155 def callback(new_owner):
156 if new_owner == '':
157 match.remove()
158 else:
159 callback = match.set_sender_name_owner
160 watch = self.watch_name_owner(bus_name, callback)
161 self._signal_sender_matches[match] = watch
162
163 self.add_match_string(str(match))
164
165 return match
166
173
188
189 - def get_object(self, bus_name, object_path, introspect=True,
190 follow_name_owner_changes=False, **kwargs):
191 """Return a local proxy for the given remote object.
192
193 Method calls on the proxy are translated into method calls on the
194 remote object.
195
196 :Parameters:
197 `bus_name` : str
198 A bus name (either the unique name or a well-known name)
199 of the application owning the object. The keyword argument
200 named_service is a deprecated alias for this.
201 `object_path` : str
202 The object path of the desired object
203 `introspect` : bool
204 If true (default), attempt to introspect the remote
205 object to find out supported methods and their signatures
206 `follow_name_owner_changes` : bool
207 If the object path is a well-known name and this parameter
208 is false (default), resolve the well-known name to the unique
209 name of its current owner and bind to that instead; if the
210 ownership of the well-known name changes in future,
211 keep communicating with the original owner.
212 This is necessary if the D-Bus API used is stateful.
213
214 If the object path is a well-known name and this parameter
215 is true, whenever the well-known name changes ownership in
216 future, bind to the new owner, if any.
217
218 If the given object path is a unique name, this parameter
219 has no effect.
220
221 :Returns: a `dbus.proxies.ProxyObject`
222 :Raises `DBusException`: if resolving the well-known name to a
223 unique name fails
224 """
225 if follow_name_owner_changes:
226 self._require_main_loop()
227
228 named_service = kwargs.pop('named_service', None)
229 if named_service is not None:
230 if bus_name is not None:
231 raise TypeError('bus_name and named_service cannot both '
232 'be specified')
233 from warnings import warn
234 warn('Passing the named_service parameter to get_object by name '
235 'is deprecated: please use positional parameters',
236 DeprecationWarning, stacklevel=2)
237 bus_name = named_service
238 if kwargs:
239 raise TypeError('get_object does not take these keyword '
240 'arguments: %s' % ', '.join(kwargs.iterkeys()))
241
242 return self.ProxyObjectClass(self, bus_name, object_path,
243 introspect=introspect,
244 follow_name_owner_changes=follow_name_owner_changes)
245
259
261 """Start a service which will implement the given bus name on this Bus.
262
263 :Parameters:
264 `bus_name` : str
265 The well-known bus name to be activated.
266 `flags` : dbus.UInt32
267 Flags to pass to StartServiceByName (currently none are
268 defined)
269
270 :Returns: A tuple of 2 elements. The first is always True, the
271 second is either START_REPLY_SUCCESS or
272 START_REPLY_ALREADY_RUNNING.
273
274 :Raises `DBusException`: if the service could not be started.
275 :Since: 0.80.0
276 """
277 validate_bus_name(bus_name)
278 return (True, self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
279 BUS_DAEMON_IFACE,
280 'StartServiceByName',
281 'su', (bus_name, flags)))
282
283
284
286 """Request a bus name.
287
288 :Parameters:
289 `name` : str
290 The well-known name to be requested
291 `flags` : dbus.UInt32
292 A bitwise-OR of 0 or more of the flags
293 `NAME_FLAG_ALLOW_REPLACEMENT`,
294 `NAME_FLAG_REPLACE_EXISTING`
295 and `NAME_FLAG_DO_NOT_QUEUE`
296 :Returns: `REQUEST_NAME_REPLY_PRIMARY_OWNER`,
297 `REQUEST_NAME_REPLY_IN_QUEUE`,
298 `REQUEST_NAME_REPLY_EXISTS` or
299 `REQUEST_NAME_REPLY_ALREADY_OWNER`
300 :Raises `DBusException`: if the bus daemon cannot be contacted or
301 returns an error.
302 """
303 validate_bus_name(name, allow_unique=False)
304 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
305 BUS_DAEMON_IFACE, 'RequestName',
306 'su', (name, flags))
307
309 """Release a bus name.
310
311 :Parameters:
312 `name` : str
313 The well-known name to be released
314 :Returns: `RELEASE_NAME_REPLY_RELEASED`,
315 `RELEASE_NAME_REPLY_NON_EXISTENT`
316 or `RELEASE_NAME_REPLY_NOT_OWNER`
317 :Raises `DBusException`: if the bus daemon cannot be contacted or
318 returns an error.
319 """
320 validate_bus_name(name, allow_unique=False)
321 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
322 BUS_DAEMON_IFACE, 'ReleaseName',
323 's', (name,))
324
334
344
356
358 """Watch the unique connection name of the primary owner of the
359 given name.
360
361 `callback` will be called with one argument, which is either the
362 unique connection name, or the empty string (meaning the name is
363 not owned).
364
365 :Since: 0.81.0
366 """
367 return NameOwnerWatch(self, bus_name, callback)
368
380
382 """Arrange for this application to receive messages on the bus that
383 match the given rule. This version will block.
384
385 :Parameters:
386 `rule` : str
387 The match rule
388 :Raises `DBusException`: on error.
389 :Since: 0.80.0
390 """
391 self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
392 BUS_DAEMON_IFACE, 'AddMatch', 's', (rule,))
393
394
395
397 """Arrange for this application to receive messages on the bus that
398 match the given rule. This version will not block, but any errors
399 will be ignored.
400
401
402 :Parameters:
403 `rule` : str
404 The match rule
405 :Raises `DBusException`: on error.
406 :Since: 0.80.0
407 """
408 self.call_async(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
409 BUS_DAEMON_IFACE, 'AddMatch', 's', (rule,),
410 None, None)
411
413 """Arrange for this application to receive messages on the bus that
414 match the given rule. This version will block.
415
416 :Parameters:
417 `rule` : str
418 The match rule
419 :Raises `DBusException`: on error.
420 :Since: 0.80.0
421 """
422 self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
423 BUS_DAEMON_IFACE, 'RemoveMatch', 's', (rule,))
424
426 """Arrange for this application to receive messages on the bus that
427 match the given rule. This version will not block, but any errors
428 will be ignored.
429
430
431 :Parameters:
432 `rule` : str
433 The match rule
434 :Raises `DBusException`: on error.
435 :Since: 0.80.0
436 """
437 self.call_async(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
438 BUS_DAEMON_IFACE, 'RemoveMatch', 's', (rule,),
439 None, None)
440