Bump version to 0.82.1.YYYYMMDD
[dbus-python-phuang.git] / dbus / service.py
blob5e7d4c9c1e7b707e702c8cd48d28cac587238782
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
29 try:
30 import thread
31 except ImportError:
32 import dummy_thread as thread
34 import _dbus_bindings
35 from dbus import SessionBus
36 from dbus.exceptions import DBusException, \
37 NameExistsException, \
38 UnknownMethodException
39 from dbus.decorators import method
40 from dbus.decorators import signal
41 from dbus.proxies import LOCAL_PATH
44 _logger = logging.getLogger('dbus.service')
47 class _VariantSignature(object):
48 """A fake method signature which, when iterated, yields an endless stream
49 of 'v' characters representing variants (handy with zip()).
51 It has no string representation.
52 """
53 def __iter__(self):
54 """Return self."""
55 return self
57 def next(self):
58 """Return 'v' whenever called."""
59 return 'v'
61 class BusName(object):
62 """A base class for exporting your own Named Services across the Bus.
64 When instantiated, objects of this class attempt to claim the given
65 well-known name on the given bus for the current process. The name is
66 released when the BusName object becomes unreferenced.
68 If a well-known name is requested multiple times, multiple references
69 to the same BusName object will be returned.
71 Caveats
72 -------
73 - Assumes that named services are only ever requested using this class -
74 if you request names from the bus directly, confusion may occur.
75 - Does not handle queueing.
76 """
77 def __new__(cls, name, bus=None, allow_replacement=False , replace_existing=False, do_not_queue=False):
78 """Constructor, which may either return an existing cached object
79 or a new object.
81 :Parameters:
82 `name` : str
83 The well-known name to be advertised
84 `bus` : dbus.Bus
85 A Bus on which this service will be advertised.
87 Omitting this parameter or setting it to None has been
88 deprecated since version 0.82.1. For backwards compatibility,
89 if this is done, the global shared connection to the session
90 bus will be used.
92 `allow_replacement` : bool
93 If True, other processes trying to claim the same well-known
94 name will take precedence over this one.
95 `replace_existing` : bool
96 If True, this process can take over the well-known name
97 from other processes already holding it.
98 `do_not_queue` : bool
99 If True, this service will not be placed in the queue of
100 services waiting for the requested name if another service
101 already holds it.
103 _dbus_bindings.validate_bus_name(name, allow_well_known=True,
104 allow_unique=False)
106 # if necessary, get default bus (deprecated)
107 if bus is None:
108 import warnings
109 warnings.warn('Omitting the "bus" parameter to '
110 'dbus.service.BusName.__init__ is deprecated',
111 DeprecationWarning, stacklevel=2)
112 bus = SessionBus()
114 # see if this name is already defined, return it if so
115 # FIXME: accessing internals of Bus
116 if name in bus._bus_names:
117 return bus._bus_names[name]
119 # otherwise register the name
120 name_flags = (
121 (allow_replacement and _dbus_bindings.NAME_FLAG_ALLOW_REPLACEMENT or 0) |
122 (replace_existing and _dbus_bindings.NAME_FLAG_REPLACE_EXISTING or 0) |
123 (do_not_queue and _dbus_bindings.NAME_FLAG_DO_NOT_QUEUE or 0))
125 retval = bus.request_name(name, name_flags)
127 # TODO: more intelligent tracking of bus name states?
128 if retval == _dbus_bindings.REQUEST_NAME_REPLY_PRIMARY_OWNER:
129 pass
130 elif retval == _dbus_bindings.REQUEST_NAME_REPLY_IN_QUEUE:
131 # queueing can happen by default, maybe we should
132 # track this better or let the user know if they're
133 # queued or not?
134 pass
135 elif retval == _dbus_bindings.REQUEST_NAME_REPLY_EXISTS:
136 raise NameExistsException(name)
137 elif retval == _dbus_bindings.REQUEST_NAME_REPLY_ALREADY_OWNER:
138 # if this is a shared bus which is being used by someone
139 # else in this process, this can happen legitimately
140 pass
141 else:
142 raise RuntimeError('requesting bus name %s returned unexpected value %s' % (name, retval))
144 # and create the object
145 bus_name = object.__new__(cls)
146 bus_name._bus = bus
147 bus_name._name = name
149 # cache instance (weak ref only)
150 # FIXME: accessing Bus internals again
151 bus._bus_names[name] = bus_name
153 return bus_name
155 # do nothing because this is called whether or not the bus name
156 # object was retrieved from the cache or created new
157 def __init__(self, *args, **keywords):
158 pass
160 # we can delete the low-level name here because these objects
161 # are guaranteed to exist only once for each bus name
162 def __del__(self):
163 self._bus.release_name(self._name)
164 pass
166 def get_bus(self):
167 """Get the Bus this Service is on"""
168 return self._bus
170 def get_name(self):
171 """Get the name of this service"""
172 return self._name
174 def __repr__(self):
175 return '<dbus.service.BusName %s on %r at %#x>' % (self._name, self._bus, id(self))
176 __str__ = __repr__
179 def _method_lookup(self, method_name, dbus_interface):
180 """Walks the Python MRO of the given class to find the method to invoke.
182 Returns two methods, the one to call, and the one it inherits from which
183 defines its D-Bus interface name, signature, and attributes.
185 parent_method = None
186 candidate_class = None
187 successful = False
189 # split up the cases when we do and don't have an interface because the
190 # latter is much simpler
191 if dbus_interface:
192 # search through the class hierarchy in python MRO order
193 for cls in self.__class__.__mro__:
194 # if we haven't got a candidate class yet, and we find a class with a
195 # suitably named member, save this as a candidate class
196 if (not candidate_class and method_name in cls.__dict__):
197 if ("_dbus_is_method" in cls.__dict__[method_name].__dict__
198 and "_dbus_interface" in cls.__dict__[method_name].__dict__):
199 # however if it is annotated for a different interface
200 # than we are looking for, it cannot be a candidate
201 if cls.__dict__[method_name]._dbus_interface == dbus_interface:
202 candidate_class = cls
203 parent_method = cls.__dict__[method_name]
204 successful = True
205 break
206 else:
207 pass
208 else:
209 candidate_class = cls
211 # if we have a candidate class, carry on checking this and all
212 # superclasses for a method annoated as a dbus method
213 # on the correct interface
214 if (candidate_class and method_name in cls.__dict__
215 and "_dbus_is_method" in cls.__dict__[method_name].__dict__
216 and "_dbus_interface" in cls.__dict__[method_name].__dict__
217 and cls.__dict__[method_name]._dbus_interface == dbus_interface):
218 # the candidate class has a dbus method on the correct interface,
219 # or overrides a method that is, success!
220 parent_method = cls.__dict__[method_name]
221 successful = True
222 break
224 else:
225 # simpler version of above
226 for cls in self.__class__.__mro__:
227 if (not candidate_class and method_name in cls.__dict__):
228 candidate_class = cls
230 if (candidate_class and method_name in cls.__dict__
231 and "_dbus_is_method" in cls.__dict__[method_name].__dict__):
232 parent_method = cls.__dict__[method_name]
233 successful = True
234 break
236 if successful:
237 return (candidate_class.__dict__[method_name], parent_method)
238 else:
239 if dbus_interface:
240 raise UnknownMethodException('%s is not a valid method of interface %s' % (method_name, dbus_interface))
241 else:
242 raise UnknownMethodException('%s is not a valid method' % method_name)
245 def _method_reply_return(connection, message, method_name, signature, *retval):
246 reply = _dbus_bindings.MethodReturnMessage(message)
247 try:
248 reply.append(signature=signature, *retval)
249 except Exception, e:
250 logging.basicConfig()
251 if signature is None:
252 try:
253 signature = reply.guess_signature(retval) + ' (guessed)'
254 except Exception, e:
255 _logger.error('Unable to guess signature for arguments %r: '
256 '%s: %s', retval, e.__class__, e)
257 raise
258 _logger.error('Unable to append %r to message with signature %s: '
259 '%s: %s', retval, signature, e.__class__, e)
260 raise
262 connection.send_message(reply)
265 def _method_reply_error(connection, message, exception):
266 name = getattr(exception, '_dbus_error_name', None)
268 if name is not None:
269 pass
270 elif getattr(exception, '__module__', '') in ('', '__main__'):
271 name = 'org.freedesktop.DBus.Python.%s' % exception.__class__.__name__
272 else:
273 name = 'org.freedesktop.DBus.Python.%s.%s' % (exception.__module__, exception.__class__.__name__)
275 contents = traceback.format_exc()
276 reply = _dbus_bindings.ErrorMessage(message, name, contents)
278 connection.send_message(reply)
281 class InterfaceType(type):
282 def __init__(cls, name, bases, dct):
283 # these attributes are shared between all instances of the Interface
284 # object, so this has to be a dictionary that maps class names to
285 # the per-class introspection/interface data
286 class_table = getattr(cls, '_dbus_class_table', {})
287 cls._dbus_class_table = class_table
288 interface_table = class_table[cls.__module__ + '.' + name] = {}
290 # merge all the name -> method tables for all the interfaces
291 # implemented by our base classes into our own
292 for b in bases:
293 base_name = b.__module__ + '.' + b.__name__
294 if getattr(b, '_dbus_class_table', False):
295 for (interface, method_table) in class_table[base_name].iteritems():
296 our_method_table = interface_table.setdefault(interface, {})
297 our_method_table.update(method_table)
299 # add in all the name -> method entries for our own methods/signals
300 for func in dct.values():
301 if getattr(func, '_dbus_interface', False):
302 method_table = interface_table.setdefault(func._dbus_interface, {})
303 method_table[func.__name__] = func
305 super(InterfaceType, cls).__init__(name, bases, dct)
307 # methods are different to signals, so we have two functions... :)
308 def _reflect_on_method(cls, func):
309 args = func._dbus_args
311 if func._dbus_in_signature:
312 # convert signature into a tuple so length refers to number of
313 # types, not number of characters. the length is checked by
314 # the decorator to make sure it matches the length of args.
315 in_sig = tuple(_dbus_bindings.Signature(func._dbus_in_signature))
316 else:
317 # magic iterator which returns as many v's as we need
318 in_sig = _VariantSignature()
320 if func._dbus_out_signature:
321 out_sig = _dbus_bindings.Signature(func._dbus_out_signature)
322 else:
323 # its tempting to default to _dbus_bindings.Signature('v'), but
324 # for methods that return nothing, providing incorrect
325 # introspection data is worse than providing none at all
326 out_sig = []
328 reflection_data = ' <method name="%s">\n' % (func.__name__)
329 for pair in zip(in_sig, args):
330 reflection_data += ' <arg direction="in" type="%s" name="%s" />\n' % pair
331 for type in out_sig:
332 reflection_data += ' <arg direction="out" type="%s" />\n' % type
333 reflection_data += ' </method>\n'
335 return reflection_data
337 def _reflect_on_signal(cls, func):
338 args = func._dbus_args
340 if func._dbus_signature:
341 # convert signature into a tuple so length refers to number of
342 # types, not number of characters
343 sig = tuple(_dbus_bindings.Signature(func._dbus_signature))
344 else:
345 # magic iterator which returns as many v's as we need
346 sig = _VariantSignature()
348 reflection_data = ' <signal name="%s">\n' % (func.__name__)
349 for pair in zip(sig, args):
350 reflection_data = reflection_data + ' <arg type="%s" name="%s" />\n' % pair
351 reflection_data = reflection_data + ' </signal>\n'
353 return reflection_data
355 class Interface(object):
356 __metaclass__ = InterfaceType
358 #: A unique object used as the value of Object._object_path and
359 #: Object._connection if it's actually in more than one place
360 _MANY = object()
362 class Object(Interface):
363 r"""A base class for exporting your own Objects across the Bus.
365 Just inherit from Object and mark exported methods with the
366 @\ `dbus.service.method` or @\ `dbus.service.signal` decorator.
368 Example::
370 class Example(dbus.service.object):
371 def __init__(self, object_path):
372 dbus.service.Object.__init__(self, dbus.SessionBus(), path)
373 self._last_input = None
375 @dbus.service.method(interface='com.example.Sample',
376 in_signature='v', out_signature='s')
377 def StringifyVariant(self, var):
378 self.LastInputChanged(var) # emits the signal
379 return str(var)
381 @dbus.service.signal(interface='com.example.Sample',
382 signature='v')
383 def LastInputChanged(self, var):
384 # run just before the signal is actually emitted
385 # just put "pass" if nothing should happen
386 self._last_input = var
388 @dbus.service.method(interface='com.example.Sample',
389 in_signature='', out_signature='v')
390 def GetLastInput(self):
391 return self._last_input
394 #: If True, this object can be made available at more than one object path.
395 #: If True but `SUPPORTS_MULTIPLE_CONNECTIONS` is False, the object may
396 #: handle more than one object path, but they must all be on the same
397 #: connection.
398 SUPPORTS_MULTIPLE_OBJECT_PATHS = False
400 #: If True, this object can be made available on more than one connection.
401 #: If True but `SUPPORTS_MULTIPLE_OBJECT_PATHS` is False, the object must
402 #: have the same object path on all its connections.
403 SUPPORTS_MULTIPLE_CONNECTIONS = False
405 def __init__(self, conn=None, object_path=None, bus_name=None):
406 """Constructor. Either conn or bus_name is required; object_path
407 is also required.
409 :Parameters:
410 `conn` : dbus.connection.Connection or None
411 The connection on which to export this object.
413 If None, use the Bus associated with the given ``bus_name``.
414 If there is no ``bus_name`` either, the object is not
415 initially available on any Connection.
417 For backwards compatibility, if an instance of
418 dbus.service.BusName is passed as the first parameter,
419 this is equivalent to passing its associated Bus as
420 ``conn``, and passing the BusName itself as ``bus_name``.
422 `object_path` : str or None
423 A D-Bus object path at which to make this Object available
424 immediately. If this is not None, a `conn` or `bus_name` must
425 also be provided.
427 `bus_name` : dbus.service.BusName or None
428 Represents a well-known name claimed by this process. A
429 reference to the BusName object will be held by this
430 Object, preventing the name from being released during this
431 Object's lifetime (unless it's released manually).
433 if object_path is not None:
434 _dbus_bindings.validate_object_path(object_path)
436 if isinstance(conn, BusName):
437 # someone's using the old API; don't gratuitously break them
438 bus_name = conn
439 conn = bus_name.get_bus()
440 elif conn is None:
441 if bus_name is not None:
442 # someone's using the old API but naming arguments, probably
443 conn = bus_name.get_bus()
445 #: Either an object path, None or _MANY
446 self._object_path = None
447 #: Either a dbus.connection.Connection, None or _MANY
448 self._connection = None
449 #: A list of tuples (Connection, object path, False) where the False
450 #: is for future expansion (to support fallback paths)
451 self._locations = []
452 #: Lock protecting `_locations`, `_connection` and `_object_path`
453 self._locations_lock = thread.allocate_lock()
455 #: True if this is a fallback object handling a whole subtree.
456 self._fallback = False
458 self._name = bus_name
460 if conn is None and object_path is not None:
461 raise TypeError('If object_path is given, either conn or bus_name '
462 'is required')
463 if conn is not None and object_path is not None:
464 self.add_to_connection(conn, object_path)
466 @property
467 def __dbus_object_path__(self):
468 """The object-path at which this object is available.
469 Access raises AttributeError if there is no object path, or more than
470 one object path.
472 Changed in 0.82.0: AttributeError can be raised.
474 if self._object_path is _MANY:
475 raise AttributeError('Object %r has more than one object path: '
476 'use Object.locations instead' % self)
477 elif self._object_path is None:
478 raise AttributeError('Object %r has no object path yet' % self)
479 else:
480 return self._object_path
482 @property
483 def connection(self):
484 """The Connection on which this object is available.
485 Access raises AttributeError if there is no Connection, or more than
486 one Connection.
488 Changed in 0.82.0: AttributeError can be raised.
490 if self._connection is _MANY:
491 raise AttributeError('Object %r is on more than one Connection: '
492 'use Object.locations instead' % self)
493 elif self._connection is None:
494 raise AttributeError('Object %r has no Connection yet' % self)
495 else:
496 return self._connection
498 @property
499 def locations(self):
500 """An iterable over tuples representing locations at which this
501 object is available.
503 Each tuple has at least two items, but may have more in future
504 versions of dbus-python, so do not rely on their exact length.
505 The first two items are the dbus.connection.Connection and the object
506 path.
508 :Since: 0.82.0
510 return iter(self._locations)
512 def add_to_connection(self, connection, path):
513 """Make this object accessible via the given D-Bus connection and
514 object path.
516 :Parameters:
517 `connection` : dbus.connection.Connection
518 Export the object on this connection. If the class attribute
519 SUPPORTS_MULTIPLE_CONNECTIONS is False (default), this object
520 can only be made available on one connection; if the class
521 attribute is set True by a subclass, the object can be made
522 available on more than one connection.
524 `path` : dbus.ObjectPath or other str
525 Place the object at this object path. If the class attribute
526 SUPPORTS_MULTIPLE_OBJECT_PATHS is False (default), this object
527 can only be made available at one object path; if the class
528 attribute is set True by a subclass, the object can be made
529 available with more than one object path.
531 :Raises ValueError: if the object's class attributes do not allow the
532 object to be exported in the desired way.
533 :Since: 0.82.0
535 if path == LOCAL_PATH:
536 raise ValueError('Objects may not be exported on the reserved '
537 'path %s' % LOCAL_PATH)
539 self._locations_lock.acquire()
540 try:
541 if (self._connection is not None and
542 self._connection is not connection and
543 not self.SUPPORTS_MULTIPLE_CONNECTIONS):
544 raise ValueError('%r is already exported on '
545 'connection %r' % (self, self._connection))
547 if (self._object_path is not None and
548 not self.SUPPORTS_MULTIPLE_OBJECT_PATHS and
549 self._object_path != path):
550 raise ValueError('%r is already exported at object '
551 'path %s' % (self, self._object_path))
553 connection._register_object_path(path, self._message_cb,
554 self._unregister_cb,
555 self._fallback)
557 if self._connection is None:
558 self._connection = connection
559 elif self._connection is not connection:
560 self._connection = _MANY
562 if self._object_path is None:
563 self._object_path = path
564 elif self._object_path != path:
565 self._object_path = _MANY
567 self._locations.append((connection, path, self._fallback))
568 finally:
569 self._locations_lock.release()
571 def remove_from_connection(self, connection=None, path=None):
572 """Make this object inaccessible via the given D-Bus connection
573 and object path. If no connection or path is specified,
574 the object ceases to be accessible via any connection or path.
576 It's not currently possible to export an object on more than one
577 connection or with more than one object-path, but this will be
578 supported in future.
580 :Parameters:
581 `connection` : dbus.connection.Connection or None
582 Only remove the object from this Connection. If None,
583 remove from all Connections on which it's exported.
584 `path` : dbus.ObjectPath or other str, or None
585 Only remove the object from this object path. If None,
586 remove from all object paths.
587 :Raises LookupError:
588 if the object was not exported on the requested connection
589 or path, or (if both are None) was not exported at all.
590 :Since: 0.81.1
592 self._locations_lock.acquire()
593 try:
594 if self._object_path is None or self._connection is None:
595 raise LookupError('%r is not exported' % self)
597 if connection is not None or path is not None:
598 dropped = []
599 for location in self._locations:
600 if ((connection is None or location[0] is connection) and
601 (path is None or location[1] == path)):
602 dropped.append(location)
603 else:
604 dropped = self._locations
605 self._locations = []
607 if not dropped:
608 raise LookupError('%r is not exported at a location matching '
609 '(%r,%r)' % (self, connection, path))
611 for location in dropped:
612 try:
613 location[0]._unregister_object_path(location[1])
614 except LookupError:
615 pass
616 if self._locations:
617 try:
618 self._locations.remove(location)
619 except ValueError:
620 pass
621 finally:
622 self._locations_lock.release()
624 def _unregister_cb(self, connection):
625 # there's not really enough information to do anything useful here
626 _logger.info('Unregistering exported object %r from some path '
627 'on %r', self, connection)
629 def _message_cb(self, connection, message):
630 try:
631 # lookup candidate method and parent method
632 method_name = message.get_member()
633 interface_name = message.get_interface()
634 (candidate_method, parent_method) = _method_lookup(self, method_name, interface_name)
636 # set up method call parameters
637 args = message.get_args_list(**parent_method._dbus_get_args_options)
638 keywords = {}
640 if parent_method._dbus_out_signature is not None:
641 signature = _dbus_bindings.Signature(parent_method._dbus_out_signature)
642 else:
643 signature = None
645 # set up async callback functions
646 if parent_method._dbus_async_callbacks:
647 (return_callback, error_callback) = parent_method._dbus_async_callbacks
648 keywords[return_callback] = lambda *retval: _method_reply_return(connection, message, method_name, signature, *retval)
649 keywords[error_callback] = lambda exception: _method_reply_error(connection, message, exception)
651 # include the sender etc. if desired
652 if parent_method._dbus_sender_keyword:
653 keywords[parent_method._dbus_sender_keyword] = message.get_sender()
654 if parent_method._dbus_path_keyword:
655 keywords[parent_method._dbus_path_keyword] = message.get_path()
656 if parent_method._dbus_destination_keyword:
657 keywords[parent_method._dbus_destination_keyword] = message.get_destination()
658 if parent_method._dbus_message_keyword:
659 keywords[parent_method._dbus_message_keyword] = message
660 if parent_method._dbus_connection_keyword:
661 keywords[parent_method._dbus_connection_keyword] = connection
663 # call method
664 retval = candidate_method(self, *args, **keywords)
666 # we're done - the method has got callback functions to reply with
667 if parent_method._dbus_async_callbacks:
668 return
670 # otherwise we send the return values in a reply. if we have a
671 # signature, use it to turn the return value into a tuple as
672 # appropriate
673 if signature is not None:
674 signature_tuple = tuple(signature)
675 # if we have zero or one return values we want make a tuple
676 # for the _method_reply_return function, otherwise we need
677 # to check we're passing it a sequence
678 if len(signature_tuple) == 0:
679 if retval == None:
680 retval = ()
681 else:
682 raise TypeError('%s has an empty output signature but did not return None' %
683 method_name)
684 elif len(signature_tuple) == 1:
685 retval = (retval,)
686 else:
687 if operator.isSequenceType(retval):
688 # multi-value signature, multi-value return... proceed unchanged
689 pass
690 else:
691 raise TypeError('%s has multiple output values in signature %s but did not return a sequence' %
692 (method_name, signature))
694 # no signature, so just turn the return into a tuple and send it as normal
695 else:
696 if retval is None:
697 retval = ()
698 elif (isinstance(retval, tuple)
699 and not isinstance(retval, _dbus_bindings.Struct)):
700 # If the return is a tuple that is not a Struct, we use it
701 # as-is on the assumption that there are multiple return
702 # values - this is the usual Python idiom. (fd.o #10174)
703 pass
704 else:
705 retval = (retval,)
707 _method_reply_return(connection, message, method_name, signature, *retval)
708 except Exception, exception:
709 # send error reply
710 _method_reply_error(connection, message, exception)
712 @method('org.freedesktop.DBus.Introspectable', in_signature='', out_signature='s')
713 def Introspect(self):
714 """Return a string of XML encoding this object's supported interfaces,
715 methods and signals.
717 reflection_data = _dbus_bindings.DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
718 reflection_data += '<node name="%s">\n' % (self._object_path)
720 interfaces = self._dbus_class_table[self.__class__.__module__ + '.' + self.__class__.__name__]
721 for (name, funcs) in interfaces.iteritems():
722 reflection_data += ' <interface name="%s">\n' % (name)
724 for func in funcs.values():
725 if getattr(func, '_dbus_is_method', False):
726 reflection_data += self.__class__._reflect_on_method(func)
727 elif getattr(func, '_dbus_is_signal', False):
728 reflection_data += self.__class__._reflect_on_signal(func)
730 reflection_data += ' </interface>\n'
732 for name in self._connection.list_exported_child_objects(
733 self._object_path):
734 reflection_data += ' <node name="%s"/>\n' % name
736 reflection_data += '</node>\n'
738 return reflection_data
740 def __repr__(self):
741 where = ''
742 if (self._object_path is not _MANY
743 and self._object_path is not None):
744 where = ' at %s' % self._object_path
745 return '<%s.%s%s at %#x>' % (self.__class__.__module__,
746 self.__class__.__name__, where,
747 id(self))
748 __str__ = __repr__
750 class FallbackObject(Object):
751 """An object that implements an entire subtree of the object-path
752 tree.
754 :Since: 0.82.0
757 SUPPORTS_MULTIPLE_OBJECT_PATHS = True
759 def __init__(self, conn=None, object_path=None):
760 """Constructor.
762 Note that the superclass' ``bus_name`` __init__ argument is not
763 supported here.
765 :Parameters:
766 `conn` : dbus.connection.Connection or None
767 The connection on which to export this object. If this is not
768 None, an `object_path` must also be provided.
770 If None, the object is not initially available on any
771 Connection.
773 `object_path` : str or None
774 A D-Bus object path at which to make this Object available
775 immediately. If this is not None, a `conn` must also be
776 provided.
778 This object will implements all object-paths in the subtree
779 starting at this object-path, except where a more specific
780 object has been added.
782 super(FallbackObject, self).__init__()
783 self._fallback = True
785 if conn is None:
786 if object_path is not None:
787 raise TypeError('If object_path is given, conn is required')
788 elif object_path is None:
789 raise TypeError('If conn is given, object_path is required')
790 else:
791 self.add_to_connection(conn, object_path)