Try to avoid importing things from _dbus_bindings when they could be imported from...
[dbus-python-phuang.git] / dbus / bus.py
blob553a4a9e78e28a9377cdfbd3bc8f2131095d8461
1 # Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
3 # Licensed under the Academic Free License version 2.1
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU Lesser General Public License as published
7 # by the Free Software Foundation; either version 2.1 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 __all__ = ('BusConnection',)
20 __docformat__ = 'reStructuredText'
22 import logging
23 import weakref
25 from _dbus_bindings import validate_interface_name, validate_member_name,\
26 validate_bus_name, validate_object_path,\
27 validate_error_name,\
28 BUS_SESSION, BUS_STARTER, BUS_SYSTEM, \
29 DBUS_START_REPLY_SUCCESS, \
30 DBUS_START_REPLY_ALREADY_RUNNING, \
31 BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_DAEMON_IFACE,\
32 NAME_FLAG_ALLOW_REPLACEMENT, \
33 NAME_FLAG_DO_NOT_QUEUE, \
34 NAME_FLAG_REPLACE_EXISTING, \
35 RELEASE_NAME_REPLY_NON_EXISTENT, \
36 RELEASE_NAME_REPLY_NOT_OWNER, \
37 RELEASE_NAME_REPLY_RELEASED, \
38 REQUEST_NAME_REPLY_ALREADY_OWNER, \
39 REQUEST_NAME_REPLY_EXISTS, \
40 REQUEST_NAME_REPLY_IN_QUEUE, \
41 REQUEST_NAME_REPLY_PRIMARY_OWNER
42 from dbus.connection import Connection
43 from dbus.exceptions import DBusException
44 from dbus.lowlevel import HANDLER_RESULT_NOT_YET_HANDLED
47 _NAME_OWNER_CHANGE_MATCH = ("type='signal',sender='%s',"
48 "interface='%s',member='NameOwnerChanged',"
49 "path='%s',arg0='%%s'"
50 % (BUS_DAEMON_NAME, BUS_DAEMON_IFACE,
51 BUS_DAEMON_PATH))
52 """(_NAME_OWNER_CHANGE_MATCH % sender) matches relevant NameOwnerChange
53 messages"""
55 _NAME_HAS_NO_OWNER = 'org.freedesktop.DBus.Error.NameHasNoOwner'
57 _logger = logging.getLogger('dbus.bus')
60 class NameOwnerWatch(object):
61 __slots__ = ('_match', '_pending_call')
63 def __init__(self, bus_conn, bus_name, callback):
64 validate_bus_name(bus_name)
66 def signal_cb(owned, old_owner, new_owner):
67 callback(new_owner)
69 def error_cb(e):
70 if e.get_dbus_name() == _NAME_HAS_NO_OWNER:
71 callback('')
72 else:
73 logging.basicConfig()
74 _logger.debug('GetNameOwner(%s) failed:', bus_name,
75 exc_info=(e.__class__, e, None))
77 self._match = bus_conn.add_signal_receiver(signal_cb,
78 'NameOwnerChanged',
79 BUS_DAEMON_IFACE,
80 BUS_DAEMON_NAME,
81 BUS_DAEMON_PATH,
82 arg0=bus_name)
83 self._pending_call = bus_conn.call_async(BUS_DAEMON_NAME,
84 BUS_DAEMON_PATH,
85 BUS_DAEMON_IFACE,
86 'GetNameOwner',
87 's', (bus_name,),
88 callback, error_cb,
89 utf8_strings=True)
91 def cancel(self):
92 if self._match is not None:
93 self._match.remove()
94 if self._pending_call is not None:
95 self._pending_call.cancel()
96 self._match = None
97 self._pending_call = None
100 class BusConnection(Connection):
101 """A connection to a D-Bus daemon that implements the
102 ``org.freedesktop.DBus`` pseudo-service.
104 :Since: 0.81.0
107 TYPE_SESSION = BUS_SESSION
108 """Represents a session bus (same as the global dbus.BUS_SESSION)"""
110 TYPE_SYSTEM = BUS_SYSTEM
111 """Represents the system bus (same as the global dbus.BUS_SYSTEM)"""
113 TYPE_STARTER = BUS_STARTER
114 """Represents the bus that started this service by activation (same as
115 the global dbus.BUS_STARTER)"""
117 START_REPLY_SUCCESS = DBUS_START_REPLY_SUCCESS
118 START_REPLY_ALREADY_RUNNING = DBUS_START_REPLY_ALREADY_RUNNING
120 def __new__(cls, address_or_type=TYPE_SESSION, mainloop=None):
121 bus = cls._new_for_bus(address_or_type, mainloop=mainloop)
123 # _bus_names is used by dbus.service.BusName!
124 bus._bus_names = weakref.WeakValueDictionary()
126 bus._signal_sender_matches = {}
127 """Map from SignalMatch to NameOwnerWatch."""
129 return bus
131 def add_signal_receiver(self, handler_function, signal_name=None,
132 dbus_interface=None, bus_name=None,
133 path=None, **keywords):
134 named_service = keywords.pop('named_service', None)
135 if named_service is not None:
136 if bus_name is not None:
137 raise TypeError('bus_name and named_service cannot both be '
138 'specified')
139 bus_name = named_service
140 from warnings import warn
141 warn('Passing the named_service parameter to add_signal_receiver '
142 'by name is deprecated: please use positional parameters',
143 DeprecationWarning, stacklevel=2)
145 match = super(BusConnection, self).add_signal_receiver(
146 handler_function, signal_name, dbus_interface, bus_name,
147 path, **keywords)
149 if (bus_name is not None and bus_name != BUS_DAEMON_NAME):
150 if bus_name[:1] == ':':
151 def callback(new_owner):
152 if new_owner == '':
153 match.remove()
154 else:
155 callback = match.set_sender_name_owner
156 watch = self.watch_name_owner(bus_name, callback)
157 self._signal_sender_matches[match] = watch
159 self.add_match_string(str(match))
161 return match
163 def _clean_up_signal_match(self, match):
164 # The signals lock is no longer held here (it was in <= 0.81.0)
165 self.remove_match_string_non_blocking(str(match))
166 watch = self._signal_sender_matches.pop(match, None)
167 if watch is not None:
168 watch.cancel()
170 def activate_name_owner(self, bus_name):
171 if (bus_name is not None and bus_name[:1] != ':'
172 and bus_name != BUS_DAEMON_NAME):
173 try:
174 return self.get_name_owner(bus_name)
175 except DBusException, e:
176 if e.get_dbus_name() != _NAME_HAS_NO_OWNER:
177 raise
178 # else it doesn't exist: try to start it
179 self.start_service_by_name(bus_name)
180 return self.get_name_owner(bus_name)
181 else:
182 # already unique
183 return bus_name
185 def get_object(self, bus_name, object_path, introspect=True,
186 follow_name_owner_changes=False, **kwargs):
187 """Return a local proxy for the given remote object.
189 Method calls on the proxy are translated into method calls on the
190 remote object.
192 :Parameters:
193 `bus_name` : str
194 A bus name (either the unique name or a well-known name)
195 of the application owning the object. The keyword argument
196 named_service is a deprecated alias for this.
197 `object_path` : str
198 The object path of the desired object
199 `introspect` : bool
200 If true (default), attempt to introspect the remote
201 object to find out supported methods and their signatures
202 `follow_name_owner_changes` : bool
203 If the object path is a well-known name and this parameter
204 is false (default), resolve the well-known name to the unique
205 name of its current owner and bind to that instead; if the
206 ownership of the well-known name changes in future,
207 keep communicating with the original owner.
208 This is necessary if the D-Bus API used is stateful.
210 If the object path is a well-known name and this parameter
211 is true, whenever the well-known name changes ownership in
212 future, bind to the new owner, if any.
214 If the given object path is a unique name, this parameter
215 has no effect.
217 :Returns: a `dbus.proxies.ProxyObject`
218 :Raises `DBusException`: if resolving the well-known name to a
219 unique name fails
221 if follow_name_owner_changes:
222 self._require_main_loop() # we don't get the signals otherwise
224 named_service = kwargs.pop('named_service', None)
225 if named_service is not None:
226 if bus_name is not None:
227 raise TypeError('bus_name and named_service cannot both '
228 'be specified')
229 from warnings import warn
230 warn('Passing the named_service parameter to get_object by name '
231 'is deprecated: please use positional parameters',
232 DeprecationWarning, stacklevel=2)
233 bus_name = named_service
234 if kwargs:
235 raise TypeError('get_object does not take these keyword '
236 'arguments: %s' % ', '.join(kwargs.iterkeys()))
238 return self.ProxyObjectClass(self, bus_name, object_path,
239 introspect=introspect,
240 follow_name_owner_changes=follow_name_owner_changes)
242 def get_unix_user(self, bus_name):
243 """Get the numeric uid of the process owning the given bus name.
245 :Parameters:
246 `bus_name` : str
247 A bus name, either unique or well-known
248 :Returns: a `dbus.UInt32`
249 :Since: 0.80.0
251 validate_bus_name(bus_name)
252 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
253 BUS_DAEMON_IFACE, 'GetConnectionUnixUser',
254 's', (bus_name,))
256 def start_service_by_name(self, bus_name, flags=0):
257 """Start a service which will implement the given bus name on this Bus.
259 :Parameters:
260 `bus_name` : str
261 The well-known bus name to be activated.
262 `flags` : dbus.UInt32
263 Flags to pass to StartServiceByName (currently none are
264 defined)
266 :Returns: A tuple of 2 elements. The first is always True, the
267 second is either START_REPLY_SUCCESS or
268 START_REPLY_ALREADY_RUNNING.
270 :Raises `DBusException`: if the service could not be started.
271 :Since: 0.80.0
273 validate_bus_name(bus_name)
274 return (True, self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
275 BUS_DAEMON_IFACE,
276 'StartServiceByName',
277 'su', (bus_name, flags)))
279 # XXX: it might be nice to signal IN_QUEUE, EXISTS by exception,
280 # but this would not be backwards-compatible
281 def request_name(self, name, flags=0):
282 """Request a bus name.
284 :Parameters:
285 `name` : str
286 The well-known name to be requested
287 `flags` : dbus.UInt32
288 A bitwise-OR of 0 or more of the flags
289 `NAME_FLAG_ALLOW_REPLACEMENT`,
290 `NAME_FLAG_REPLACE_EXISTING`
291 and `NAME_FLAG_DO_NOT_QUEUE`
292 :Returns: `REQUEST_NAME_REPLY_PRIMARY_OWNER`,
293 `REQUEST_NAME_REPLY_IN_QUEUE`,
294 `REQUEST_NAME_REPLY_EXISTS` or
295 `REQUEST_NAME_REPLY_ALREADY_OWNER`
296 :Raises `DBusException`: if the bus daemon cannot be contacted or
297 returns an error.
299 validate_bus_name(name, allow_unique=False)
300 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
301 BUS_DAEMON_IFACE, 'RequestName',
302 'su', (name, flags))
304 def release_name(self, name):
305 """Release a bus name.
307 :Parameters:
308 `name` : str
309 The well-known name to be released
310 :Returns: `RELEASE_NAME_REPLY_RELEASED`,
311 `RELEASE_NAME_REPLY_NON_EXISTENT`
312 or `RELEASE_NAME_REPLY_NOT_OWNER`
313 :Raises `DBusException`: if the bus daemon cannot be contacted or
314 returns an error.
316 validate_bus_name(name, allow_unique=False)
317 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
318 BUS_DAEMON_IFACE, 'ReleaseName',
319 's', (name,))
321 def list_names(self):
322 """Return a list of all currently-owned names on the bus.
324 :Returns: a dbus.Array of dbus.UTF8String
325 :Since: 0.81.0
327 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
328 BUS_DAEMON_IFACE, 'ListNames',
329 '', (), utf8_strings=True)
331 def list_activatable_names(self):
332 """Return a list of all names that can be activated on the bus.
334 :Returns: a dbus.Array of dbus.UTF8String
335 :Since: 0.81.0
337 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
338 BUS_DAEMON_IFACE, 'ListNames',
339 '', (), utf8_strings=True)
341 def get_name_owner(self, bus_name):
342 """Return the unique connection name of the primary owner of the
343 given name.
345 :Raises `DBusException`: if the `bus_name` has no owner
346 :Since: 0.81.0
348 validate_bus_name(bus_name, allow_unique=False)
349 return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
350 BUS_DAEMON_IFACE, 'GetNameOwner',
351 's', (bus_name,), utf8_strings=True)
353 def watch_name_owner(self, bus_name, callback):
354 """Watch the unique connection name of the primary owner of the
355 given name.
357 `callback` will be called with one argument, which is either the
358 unique connection name, or the empty string (meaning the name is
359 not owned).
361 :Since: 0.81.0
363 return NameOwnerWatch(self, bus_name, callback)
365 def name_has_owner(self, bus_name):
366 """Return True iff the given bus name has an owner on this bus.
368 :Parameters:
369 `bus_name` : str
370 The bus name to look up
371 :Returns: a `bool`
373 return bool(self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
374 BUS_DAEMON_IFACE, 'NameHasOwner',
375 's', (bus_name,)))
377 def add_match_string(self, rule):
378 """Arrange for this application to receive messages on the bus that
379 match the given rule. This version will block.
381 :Parameters:
382 `rule` : str
383 The match rule
384 :Raises `DBusException`: on error.
385 :Since: 0.80.0
387 self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
388 BUS_DAEMON_IFACE, 'AddMatch', 's', (rule,))
390 # FIXME: add an async success/error handler capability?
391 # (and the same for remove_...)
392 def add_match_string_non_blocking(self, rule):
393 """Arrange for this application to receive messages on the bus that
394 match the given rule. This version will not block, but any errors
395 will be ignored.
398 :Parameters:
399 `rule` : str
400 The match rule
401 :Raises `DBusException`: on error.
402 :Since: 0.80.0
404 self.call_async(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
405 BUS_DAEMON_IFACE, 'AddMatch', 's', (rule,),
406 None, None)
408 def remove_match_string(self, rule):
409 """Arrange for this application to receive messages on the bus that
410 match the given rule. This version will block.
412 :Parameters:
413 `rule` : str
414 The match rule
415 :Raises `DBusException`: on error.
416 :Since: 0.80.0
418 self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
419 BUS_DAEMON_IFACE, 'RemoveMatch', 's', (rule,))
421 def remove_match_string_non_blocking(self, rule):
422 """Arrange for this application to receive messages on the bus that
423 match the given rule. This version will not block, but any errors
424 will be ignored.
427 :Parameters:
428 `rule` : str
429 The match rule
430 :Raises `DBusException`: on error.
431 :Since: 0.80.0
433 self.call_async(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
434 BUS_DAEMON_IFACE, 'RemoveMatch', 's', (rule,),
435 None, None)