Create doc directory before writing HTML into it
[dbus-python-phuang.git] / dbus / service.py
blob9b4c262c545101cc59036999b2650b2a76bd120c
1 # Copyright (C) 2003, 2004, 2005, 2006 Red Hat Inc. <http://www.redhat.com/>
2 # Copyright (C) 2003 David Zeuthen
3 # Copyright (C) 2004 Rob Taylor
4 # Copyright (C) 2005, 2006 Collabora Ltd. <http://www.collabora.co.uk/>
6 # Licensed under the Academic Free License version 2.1
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 __all__ = ('BusName', 'Object', 'method', 'signal')
23 __docformat__ = 'restructuredtext'
25 import sys
26 import logging
27 import operator
28 import traceback
30 import _dbus_bindings
31 from dbus import SessionBus
32 from dbus.exceptions import NameExistsException
33 from dbus.exceptions import UnknownMethodException
34 from dbus.decorators import method
35 from dbus.decorators import signal
36 from dbus.proxies import LOCAL_PATH
39 _logger = logging.getLogger('dbus.service')
42 class _VariantSignature(object):
43 """A fake method signature which, when iterated, yields an endless stream
44 of 'v' characters representing variants (handy with zip()).
46 It has no string representation.
47 """
48 def __iter__(self):
49 """Return self."""
50 return self
52 def next(self):
53 """Return 'v' whenever called."""
54 return 'v'
56 class BusName(object):
57 """A base class for exporting your own Named Services across the Bus.
59 When instantiated, objects of this class attempt to claim the given
60 well-known name on the given bus for the current process. The name is
61 released when the BusName object becomes unreferenced.
63 If a well-known name is requested multiple times, multiple references
64 to the same BusName object will be returned.
66 Caveats
67 -------
68 - Assumes that named services are only ever requested using this class -
69 if you request names from the bus directly, confusion may occur.
70 - Does not handle queueing.
71 """
72 def __new__(cls, name, bus=None, allow_replacement=False , replace_existing=False, do_not_queue=False):
73 """Constructor, which may either return an existing cached object
74 or a new object.
76 :Parameters:
77 `name` : str
78 The well-known name to be advertised
79 `bus` : dbus.Bus
80 A Bus on which this service will be advertised; if None
81 (default) the session bus will be used
82 `allow_replacement` : bool
83 If True, other processes trying to claim the same well-known
84 name will take precedence over this one.
85 `replace_existing` : bool
86 If True, this process can take over the well-known name
87 from other processes already holding it.
88 `do_not_queue` : bool
89 If True, this service will not be placed in the queue of
90 services waiting for the requested name if another service
91 already holds it.
92 """
93 _dbus_bindings.validate_bus_name(name, allow_well_known=True,
94 allow_unique=False)
96 # get default bus
97 if bus == None:
98 bus = SessionBus()
100 # see if this name is already defined, return it if so
101 # FIXME: accessing internals of Bus
102 if name in bus._bus_names:
103 return bus._bus_names[name]
105 # otherwise register the name
106 name_flags = (
107 (allow_replacement and _dbus_bindings.NAME_FLAG_ALLOW_REPLACEMENT or 0) |
108 (replace_existing and _dbus_bindings.NAME_FLAG_REPLACE_EXISTING or 0) |
109 (do_not_queue and _dbus_bindings.NAME_FLAG_DO_NOT_QUEUE or 0))
111 retval = bus.request_name(name, name_flags)
113 # TODO: more intelligent tracking of bus name states?
114 if retval == _dbus_bindings.REQUEST_NAME_REPLY_PRIMARY_OWNER:
115 pass
116 elif retval == _dbus_bindings.REQUEST_NAME_REPLY_IN_QUEUE:
117 # queueing can happen by default, maybe we should
118 # track this better or let the user know if they're
119 # queued or not?
120 pass
121 elif retval == _dbus_bindings.REQUEST_NAME_REPLY_EXISTS:
122 raise NameExistsException(name)
123 elif retval == _dbus_bindings.REQUEST_NAME_REPLY_ALREADY_OWNER:
124 # if this is a shared bus which is being used by someone
125 # else in this process, this can happen legitimately
126 pass
127 else:
128 raise RuntimeError('requesting bus name %s returned unexpected value %s' % (name, retval))
130 # and create the object
131 bus_name = object.__new__(cls)
132 bus_name._bus = bus
133 bus_name._name = name
135 # cache instance (weak ref only)
136 # FIXME: accessing Bus internals again
137 bus._bus_names[name] = bus_name
139 return bus_name
141 # do nothing because this is called whether or not the bus name
142 # object was retrieved from the cache or created new
143 def __init__(self, *args, **keywords):
144 pass
146 # we can delete the low-level name here because these objects
147 # are guaranteed to exist only once for each bus name
148 def __del__(self):
149 self._bus.release_name(self._name)
150 pass
152 def get_bus(self):
153 """Get the Bus this Service is on"""
154 return self._bus
156 def get_name(self):
157 """Get the name of this service"""
158 return self._name
160 def __repr__(self):
161 return '<dbus.service.BusName %s on %r at %#x>' % (self._name, self._bus, id(self))
162 __str__ = __repr__
165 def _method_lookup(self, method_name, dbus_interface):
166 """Walks the Python MRO of the given class to find the method to invoke.
168 Returns two methods, the one to call, and the one it inherits from which
169 defines its D-Bus interface name, signature, and attributes.
171 parent_method = None
172 candidate_class = None
173 successful = False
175 # split up the cases when we do and don't have an interface because the
176 # latter is much simpler
177 if dbus_interface:
178 # search through the class hierarchy in python MRO order
179 for cls in self.__class__.__mro__:
180 # if we haven't got a candidate class yet, and we find a class with a
181 # suitably named member, save this as a candidate class
182 if (not candidate_class and method_name in cls.__dict__):
183 if ("_dbus_is_method" in cls.__dict__[method_name].__dict__
184 and "_dbus_interface" in cls.__dict__[method_name].__dict__):
185 # however if it is annotated for a different interface
186 # than we are looking for, it cannot be a candidate
187 if cls.__dict__[method_name]._dbus_interface == dbus_interface:
188 candidate_class = cls
189 parent_method = cls.__dict__[method_name]
190 successful = True
191 break
192 else:
193 pass
194 else:
195 candidate_class = cls
197 # if we have a candidate class, carry on checking this and all
198 # superclasses for a method annoated as a dbus method
199 # on the correct interface
200 if (candidate_class and method_name in cls.__dict__
201 and "_dbus_is_method" in cls.__dict__[method_name].__dict__
202 and "_dbus_interface" in cls.__dict__[method_name].__dict__
203 and cls.__dict__[method_name]._dbus_interface == dbus_interface):
204 # the candidate class has a dbus method on the correct interface,
205 # or overrides a method that is, success!
206 parent_method = cls.__dict__[method_name]
207 successful = True
208 break
210 else:
211 # simpler version of above
212 for cls in self.__class__.__mro__:
213 if (not candidate_class and method_name in cls.__dict__):
214 candidate_class = cls
216 if (candidate_class and method_name in cls.__dict__
217 and "_dbus_is_method" in cls.__dict__[method_name].__dict__):
218 parent_method = cls.__dict__[method_name]
219 successful = True
220 break
222 if successful:
223 return (candidate_class.__dict__[method_name], parent_method)
224 else:
225 if dbus_interface:
226 raise UnknownMethodException('%s is not a valid method of interface %s' % (method_name, dbus_interface))
227 else:
228 raise UnknownMethodException('%s is not a valid method' % method_name)
231 def _method_reply_return(connection, message, method_name, signature, *retval):
232 reply = _dbus_bindings.MethodReturnMessage(message)
233 try:
234 reply.append(signature=signature, *retval)
235 except Exception, e:
236 logging.basicConfig()
237 if signature is None:
238 try:
239 signature = reply.guess_signature(retval) + ' (guessed)'
240 except Exception, e:
241 _logger.error('Unable to guess signature for arguments %r: '
242 '%s: %s', retval, e.__class__, e)
243 raise
244 _logger.error('Unable to append %r to message with signature %s: '
245 '%s: %s', retval, signature, e.__class__, e)
246 raise
248 connection.send_message(reply)
251 def _method_reply_error(connection, message, exception):
252 if hasattr(exception, '_dbus_error_name'):
253 name = exception._dbus_error_name
254 elif getattr(exception, '__module__', '') in ('', '__main__'):
255 name = 'org.freedesktop.DBus.Python.%s' % exception.__class__.__name__
256 else:
257 name = 'org.freedesktop.DBus.Python.%s.%s' % (exception.__module__, exception.__class__.__name__)
259 contents = traceback.format_exc()
260 reply = _dbus_bindings.ErrorMessage(message, name, contents)
262 connection.send_message(reply)
265 class InterfaceType(type):
266 def __init__(cls, name, bases, dct):
267 # these attributes are shared between all instances of the Interface
268 # object, so this has to be a dictionary that maps class names to
269 # the per-class introspection/interface data
270 class_table = getattr(cls, '_dbus_class_table', {})
271 cls._dbus_class_table = class_table
272 interface_table = class_table[cls.__module__ + '.' + name] = {}
274 # merge all the name -> method tables for all the interfaces
275 # implemented by our base classes into our own
276 for b in bases:
277 base_name = b.__module__ + '.' + b.__name__
278 if getattr(b, '_dbus_class_table', False):
279 for (interface, method_table) in class_table[base_name].iteritems():
280 our_method_table = interface_table.setdefault(interface, {})
281 our_method_table.update(method_table)
283 # add in all the name -> method entries for our own methods/signals
284 for func in dct.values():
285 if getattr(func, '_dbus_interface', False):
286 method_table = interface_table.setdefault(func._dbus_interface, {})
287 method_table[func.__name__] = func
289 super(InterfaceType, cls).__init__(name, bases, dct)
291 # methods are different to signals, so we have two functions... :)
292 def _reflect_on_method(cls, func):
293 args = func._dbus_args
295 if func._dbus_in_signature:
296 # convert signature into a tuple so length refers to number of
297 # types, not number of characters. the length is checked by
298 # the decorator to make sure it matches the length of args.
299 in_sig = tuple(_dbus_bindings.Signature(func._dbus_in_signature))
300 else:
301 # magic iterator which returns as many v's as we need
302 in_sig = _VariantSignature()
304 if func._dbus_out_signature:
305 out_sig = _dbus_bindings.Signature(func._dbus_out_signature)
306 else:
307 # its tempting to default to _dbus_bindings.Signature('v'), but
308 # for methods that return nothing, providing incorrect
309 # introspection data is worse than providing none at all
310 out_sig = []
312 reflection_data = ' <method name="%s">\n' % (func.__name__)
313 for pair in zip(in_sig, args):
314 reflection_data += ' <arg direction="in" type="%s" name="%s" />\n' % pair
315 for type in out_sig:
316 reflection_data += ' <arg direction="out" type="%s" />\n' % type
317 reflection_data += ' </method>\n'
319 return reflection_data
321 def _reflect_on_signal(cls, func):
322 args = func._dbus_args
324 if func._dbus_signature:
325 # convert signature into a tuple so length refers to number of
326 # types, not number of characters
327 sig = tuple(_dbus_bindings.Signature(func._dbus_signature))
328 else:
329 # magic iterator which returns as many v's as we need
330 sig = _VariantSignature()
332 reflection_data = ' <signal name="%s">\n' % (func.__name__)
333 for pair in zip(sig, args):
334 reflection_data = reflection_data + ' <arg type="%s" name="%s" />\n' % pair
335 reflection_data = reflection_data + ' </signal>\n'
337 return reflection_data
339 class Interface(object):
340 __metaclass__ = InterfaceType
342 class Object(Interface):
343 r"""A base class for exporting your own Objects across the Bus.
345 Just inherit from Object and mark exported methods with the
346 @\ `dbus.service.method` or @\ `dbus.service.signal` decorator.
348 Example::
350 class Example(dbus.service.object):
351 def __init__(self, object_path):
352 dbus.service.Object.__init__(self, dbus.SessionBus(), path)
353 self._last_input = None
355 @dbus.service.method(interface='com.example.Sample',
356 in_signature='v', out_signature='s')
357 def StringifyVariant(self, var):
358 self.LastInputChanged(var) # emits the signal
359 return str(var)
361 @dbus.service.signal(interface='com.example.Sample',
362 signature='v')
363 def LastInputChanged(self, var):
364 # run just before the signal is actually emitted
365 # just put "pass" if nothing should happen
366 self._last_input = var
368 @dbus.service.method(interface='com.example.Sample',
369 in_signature='', out_signature='v')
370 def GetLastInput(self):
371 return self._last_input
374 # the signature of __init__ is a bit mad, for backwards compatibility
375 def __init__(self, conn=None, object_path=None, bus_name=None):
376 """Constructor. Either conn or bus_name is required; object_path
377 is also required.
379 :Parameters:
380 `conn` : dbus.Connection
381 The connection on which to export this object.
383 If None, use the Bus associated with the given ``bus_name``,
384 or raise TypeError if there is no ``bus_name`` either.
386 For backwards compatibility, if an instance of
387 dbus.service.BusName is passed as the first parameter,
388 this is equivalent to passing its associated Bus as
389 ``conn``, and passing the BusName itself as ``bus_name``.
391 `object_path` : str
392 The D-Bus object path at which to export this Object.
394 `bus_name` : dbus.service.BusName
395 Represents a well-known name claimed by this process. A
396 reference to the BusName object will be held by this
397 Object, preventing the name from being released during this
398 Object's lifetime (unless it's released manually).
400 if object_path is None:
401 raise TypeError('The object_path argument is required')
402 _dbus_bindings.validate_object_path(object_path)
403 if object_path == LOCAL_PATH:
404 raise DBusException('Objects may not be exported on the reserved '
405 'path %s' % LOCAL_PATH)
407 if isinstance(conn, BusName):
408 # someone's using the old API; don't gratuitously break them
409 bus_name = conn
410 conn = bus_name.get_bus()
411 elif conn is None:
412 # someone's using the old API but naming arguments, probably
413 if bus_name is None:
414 raise TypeError('Either conn or bus_name is required')
415 conn = bus_name.get_bus()
417 self._object_path = object_path
418 self._name = bus_name
419 self._connection = conn
421 self._connection._register_object_path(object_path, self._message_cb, self._unregister_cb)
423 __dbus_object_path__ = property(lambda self: self._object_path, None, None,
424 "The D-Bus object path of this object")
426 def _unregister_cb(self, connection):
427 _logger.info('Unregistering exported object %r', self)
429 def _message_cb(self, connection, message):
430 try:
431 # lookup candidate method and parent method
432 method_name = message.get_member()
433 interface_name = message.get_interface()
434 (candidate_method, parent_method) = _method_lookup(self, method_name, interface_name)
436 # set up method call parameters
437 args = message.get_args_list(**parent_method._dbus_get_args_options)
438 keywords = {}
440 if parent_method._dbus_out_signature is not None:
441 signature = _dbus_bindings.Signature(parent_method._dbus_out_signature)
442 else:
443 signature = None
445 # set up async callback functions
446 if parent_method._dbus_async_callbacks:
447 (return_callback, error_callback) = parent_method._dbus_async_callbacks
448 keywords[return_callback] = lambda *retval: _method_reply_return(connection, message, method_name, signature, *retval)
449 keywords[error_callback] = lambda exception: _method_reply_error(connection, message, exception)
451 # include the sender etc. if desired
452 if parent_method._dbus_sender_keyword:
453 keywords[parent_method._dbus_sender_keyword] = message.get_sender()
454 if parent_method._dbus_path_keyword:
455 keywords[parent_method._dbus_path_keyword] = message.get_path()
456 if parent_method._dbus_destination_keyword:
457 keywords[parent_method._dbus_destination_keyword] = message.get_destination()
458 if parent_method._dbus_message_keyword:
459 keywords[parent_method._dbus_message_keyword] = message
461 # call method
462 retval = candidate_method(self, *args, **keywords)
464 # we're done - the method has got callback functions to reply with
465 if parent_method._dbus_async_callbacks:
466 return
468 # otherwise we send the return values in a reply. if we have a
469 # signature, use it to turn the return value into a tuple as
470 # appropriate
471 if signature is not None:
472 signature_tuple = tuple(signature)
473 # if we have zero or one return values we want make a tuple
474 # for the _method_reply_return function, otherwise we need
475 # to check we're passing it a sequence
476 if len(signature_tuple) == 0:
477 if retval == None:
478 retval = ()
479 else:
480 raise TypeError('%s has an empty output signature but did not return None' %
481 method_name)
482 elif len(signature_tuple) == 1:
483 retval = (retval,)
484 else:
485 if operator.isSequenceType(retval):
486 # multi-value signature, multi-value return... proceed unchanged
487 pass
488 else:
489 raise TypeError('%s has multiple output values in signature %s but did not return a sequence' %
490 (method_name, signature))
492 # no signature, so just turn the return into a tuple and send it as normal
493 else:
494 if retval is None:
495 retval = ()
496 elif (isinstance(retval, tuple)
497 and not isinstance(retval, _dbus_bindings.Struct)):
498 # If the return is a tuple that is not a Struct, we use it
499 # as-is on the assumption that there are multiple return
500 # values - this is the usual Python idiom. (fd.o #10174)
501 pass
502 else:
503 retval = (retval,)
505 _method_reply_return(connection, message, method_name, signature, *retval)
506 except Exception, exception:
507 # send error reply
508 _method_reply_error(connection, message, exception)
510 @method('org.freedesktop.DBus.Introspectable', in_signature='', out_signature='s')
511 def Introspect(self):
512 """Return a string of XML encoding this object's supported interfaces,
513 methods and signals.
515 reflection_data = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">\n'
516 reflection_data += '<node name="%s">\n' % (self._object_path)
518 interfaces = self._dbus_class_table[self.__class__.__module__ + '.' + self.__class__.__name__]
519 for (name, funcs) in interfaces.iteritems():
520 reflection_data += ' <interface name="%s">\n' % (name)
522 for func in funcs.values():
523 if getattr(func, '_dbus_is_method', False):
524 reflection_data += self.__class__._reflect_on_method(func)
525 elif getattr(func, '_dbus_is_signal', False):
526 reflection_data += self.__class__._reflect_on_signal(func)
528 reflection_data += ' </interface>\n'
530 reflection_data += '</node>\n'
532 return reflection_data
534 def __repr__(self):
535 return '<dbus.service.Object %s on %r at %#x>' % (self._object_path, self._name, id(self))
536 __str__ = __repr__