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'
32 import dummy_thread
as thread
35 from dbus
import SessionBus
, Signature
, Struct
, validate_bus_name
, \
36 validate_object_path
, INTROSPECTABLE_IFACE
, ObjectPath
37 from dbus
.decorators
import method
, signal
38 from dbus
.exceptions
import DBusException
, \
39 NameExistsException
, \
40 UnknownMethodException
41 from dbus
.lowlevel
import ErrorMessage
, MethodReturnMessage
42 from dbus
.proxies
import LOCAL_PATH
45 _logger
= logging
.getLogger('dbus.service')
48 class _VariantSignature(object):
49 """A fake method signature which, when iterated, yields an endless stream
50 of 'v' characters representing variants (handy with zip()).
52 It has no string representation.
59 """Return 'v' whenever called."""
62 class BusName(object):
63 """A base class for exporting your own Named Services across the Bus.
65 When instantiated, objects of this class attempt to claim the given
66 well-known name on the given bus for the current process. The name is
67 released when the BusName object becomes unreferenced.
69 If a well-known name is requested multiple times, multiple references
70 to the same BusName object will be returned.
74 - Assumes that named services are only ever requested using this class -
75 if you request names from the bus directly, confusion may occur.
76 - Does not handle queueing.
78 def __new__(cls
, name
, bus
=None, allow_replacement
=False , replace_existing
=False, do_not_queue
=False):
79 """Constructor, which may either return an existing cached object
84 The well-known name to be advertised
86 A Bus on which this service will be advertised.
88 Omitting this parameter or setting it to None has been
89 deprecated since version 0.82.1. For backwards compatibility,
90 if this is done, the global shared connection to the session
93 `allow_replacement` : bool
94 If True, other processes trying to claim the same well-known
95 name will take precedence over this one.
96 `replace_existing` : bool
97 If True, this process can take over the well-known name
98 from other processes already holding it.
100 If True, this service will not be placed in the queue of
101 services waiting for the requested name if another service
104 validate_bus_name(name
, allow_well_known
=True, allow_unique
=False)
106 # if necessary, get default bus (deprecated)
109 warnings
.warn('Omitting the "bus" parameter to '
110 'dbus.service.BusName.__init__ is deprecated',
111 DeprecationWarning, stacklevel
=2)
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
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
:
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
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
142 raise RuntimeError('requesting bus name %s returned unexpected value %s' % (name
, retval
))
144 # and create the object
145 bus_name
= object.__new
__(cls
)
147 bus_name
._name
= name
149 # cache instance (weak ref only)
150 # FIXME: accessing Bus internals again
151 bus
._bus
_names
[name
] = 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
):
160 # we can delete the low-level name here because these objects
161 # are guaranteed to exist only once for each bus name
163 self
._bus
.release_name(self
._name
)
167 """Get the Bus this Service is on"""
171 """Get the name of this service"""
175 return '<dbus.service.BusName %s on %r at %#x>' % (self
._name
, self
._bus
, id(self
))
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.
186 candidate_class
= None
189 # split up the cases when we do and don't have an interface because the
190 # latter is much simpler
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
]
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
]
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
]
237 return (candidate_class
.__dict
__[method_name
], parent_method
)
240 raise UnknownMethodException('%s is not a valid method of interface %s' % (method_name
, dbus_interface
))
242 raise UnknownMethodException('%s is not a valid method' % method_name
)
245 def _method_reply_return(connection
, message
, method_name
, signature
, *retval
):
246 reply
= MethodReturnMessage(message
)
248 reply
.append(signature
=signature
, *retval
)
250 logging
.basicConfig()
251 if signature
is None:
253 signature
= reply
.guess_signature(retval
) + ' (guessed)'
255 _logger
.error('Unable to guess signature for arguments %r: '
256 '%s: %s', retval
, e
.__class
__, e
)
258 _logger
.error('Unable to append %r to message with signature %s: '
259 '%s: %s', retval
, signature
, e
.__class
__, e
)
262 connection
.send_message(reply
)
265 def _method_reply_error(connection
, message
, exception
):
266 name
= getattr(exception
, '_dbus_error_name', None)
270 elif getattr(exception
, '__module__', '') in ('', '__main__'):
271 name
= 'org.freedesktop.DBus.Python.%s' % exception
.__class
__.__name
__
273 name
= 'org.freedesktop.DBus.Python.%s.%s' % (exception
.__module
__, exception
.__class
__.__name
__)
275 et
, ev
, etb
= sys
.exc_info()
277 # The exception was actually thrown, so we can get a traceback
278 contents
= ''.join(traceback
.format_exception(et
, ev
, etb
))
280 # We don't have any traceback for it, e.g.
281 # async_err_cb(MyException('Failed to badger the mushroom'))
282 # see also https://bugs.freedesktop.org/show_bug.cgi?id=12403
283 contents
= ''.join(traceback
.format_exception_only(exception
.__class
__,
285 reply
= ErrorMessage(message
, name
, contents
)
287 connection
.send_message(reply
)
290 class InterfaceType(type):
291 def __init__(cls
, name
, bases
, dct
):
292 # these attributes are shared between all instances of the Interface
293 # object, so this has to be a dictionary that maps class names to
294 # the per-class introspection/interface data
295 class_table
= getattr(cls
, '_dbus_class_table', {})
296 cls
._dbus
_class
_table
= class_table
297 interface_table
= class_table
[cls
.__module
__ + '.' + name
] = {}
299 # merge all the name -> method tables for all the interfaces
300 # implemented by our base classes into our own
302 base_name
= b
.__module
__ + '.' + b
.__name
__
303 if getattr(b
, '_dbus_class_table', False):
304 for (interface
, method_table
) in class_table
[base_name
].iteritems():
305 our_method_table
= interface_table
.setdefault(interface
, {})
306 our_method_table
.update(method_table
)
308 # add in all the name -> method entries for our own methods/signals
309 for func
in dct
.values():
310 if getattr(func
, '_dbus_interface', False):
311 method_table
= interface_table
.setdefault(func
._dbus
_interface
, {})
312 method_table
[func
.__name
__] = func
314 super(InterfaceType
, cls
).__init
__(name
, bases
, dct
)
316 # methods are different to signals, so we have two functions... :)
317 def _reflect_on_method(cls
, func
):
318 args
= func
._dbus
_args
320 if func
._dbus
_in
_signature
:
321 # convert signature into a tuple so length refers to number of
322 # types, not number of characters. the length is checked by
323 # the decorator to make sure it matches the length of args.
324 in_sig
= tuple(Signature(func
._dbus
_in
_signature
))
326 # magic iterator which returns as many v's as we need
327 in_sig
= _VariantSignature()
329 if func
._dbus
_out
_signature
:
330 out_sig
= Signature(func
._dbus
_out
_signature
)
332 # its tempting to default to Signature('v'), but
333 # for methods that return nothing, providing incorrect
334 # introspection data is worse than providing none at all
337 reflection_data
= ' <method name="%s">\n' % (func
.__name
__)
338 for pair
in zip(in_sig
, args
):
339 reflection_data
+= ' <arg direction="in" type="%s" name="%s" />\n' % pair
341 reflection_data
+= ' <arg direction="out" type="%s" />\n' % type
342 reflection_data
+= ' </method>\n'
344 return reflection_data
346 def _reflect_on_signal(cls
, func
):
347 args
= func
._dbus
_args
349 if func
._dbus
_signature
:
350 # convert signature into a tuple so length refers to number of
351 # types, not number of characters
352 sig
= tuple(Signature(func
._dbus
_signature
))
354 # magic iterator which returns as many v's as we need
355 sig
= _VariantSignature()
357 reflection_data
= ' <signal name="%s">\n' % (func
.__name
__)
358 for pair
in zip(sig
, args
):
359 reflection_data
= reflection_data
+ ' <arg type="%s" name="%s" />\n' % pair
360 reflection_data
= reflection_data
+ ' </signal>\n'
362 return reflection_data
364 class Interface(object):
365 __metaclass__
= InterfaceType
367 #: A unique object used as the value of Object._object_path and
368 #: Object._connection if it's actually in more than one place
371 class Object(Interface
):
372 r
"""A base class for exporting your own Objects across the Bus.
374 Just inherit from Object and mark exported methods with the
375 @\ `dbus.service.method` or @\ `dbus.service.signal` decorator.
379 class Example(dbus.service.object):
380 def __init__(self, object_path):
381 dbus.service.Object.__init__(self, dbus.SessionBus(), path)
382 self._last_input = None
384 @dbus.service.method(interface='com.example.Sample',
385 in_signature='v', out_signature='s')
386 def StringifyVariant(self, var):
387 self.LastInputChanged(var) # emits the signal
390 @dbus.service.signal(interface='com.example.Sample',
392 def LastInputChanged(self, var):
393 # run just before the signal is actually emitted
394 # just put "pass" if nothing should happen
395 self._last_input = var
397 @dbus.service.method(interface='com.example.Sample',
398 in_signature='', out_signature='v')
399 def GetLastInput(self):
400 return self._last_input
403 #: If True, this object can be made available at more than one object path.
404 #: If True but `SUPPORTS_MULTIPLE_CONNECTIONS` is False, the object may
405 #: handle more than one object path, but they must all be on the same
407 SUPPORTS_MULTIPLE_OBJECT_PATHS
= False
409 #: If True, this object can be made available on more than one connection.
410 #: If True but `SUPPORTS_MULTIPLE_OBJECT_PATHS` is False, the object must
411 #: have the same object path on all its connections.
412 SUPPORTS_MULTIPLE_CONNECTIONS
= False
414 def __init__(self
, conn
=None, object_path
=None, bus_name
=None):
415 """Constructor. Either conn or bus_name is required; object_path
419 `conn` : dbus.connection.Connection or None
420 The connection on which to export this object.
422 If None, use the Bus associated with the given ``bus_name``.
423 If there is no ``bus_name`` either, the object is not
424 initially available on any Connection.
426 For backwards compatibility, if an instance of
427 dbus.service.BusName is passed as the first parameter,
428 this is equivalent to passing its associated Bus as
429 ``conn``, and passing the BusName itself as ``bus_name``.
431 `object_path` : str or None
432 A D-Bus object path at which to make this Object available
433 immediately. If this is not None, a `conn` or `bus_name` must
436 `bus_name` : dbus.service.BusName or None
437 Represents a well-known name claimed by this process. A
438 reference to the BusName object will be held by this
439 Object, preventing the name from being released during this
440 Object's lifetime (unless it's released manually).
442 if object_path
is not None:
443 validate_object_path(object_path
)
445 if isinstance(conn
, BusName
):
446 # someone's using the old API; don't gratuitously break them
448 conn
= bus_name
.get_bus()
450 if bus_name
is not None:
451 # someone's using the old API but naming arguments, probably
452 conn
= bus_name
.get_bus()
454 #: Either an object path, None or _MANY
455 self
._object
_path
= None
456 #: Either a dbus.connection.Connection, None or _MANY
457 self
._connection
= None
458 #: A list of tuples (Connection, object path, False) where the False
459 #: is for future expansion (to support fallback paths)
461 #: Lock protecting `_locations`, `_connection` and `_object_path`
462 self
._locations
_lock
= thread
.allocate_lock()
464 #: True if this is a fallback object handling a whole subtree.
465 self
._fallback
= False
467 self
._name
= bus_name
469 if conn
is None and object_path
is not None:
470 raise TypeError('If object_path is given, either conn or bus_name '
472 if conn
is not None and object_path
is not None:
473 self
.add_to_connection(conn
, object_path
)
476 def __dbus_object_path__(self
):
477 """The object-path at which this object is available.
478 Access raises AttributeError if there is no object path, or more than
481 Changed in 0.82.0: AttributeError can be raised.
483 if self
._object
_path
is _MANY
:
484 raise AttributeError('Object %r has more than one object path: '
485 'use Object.locations instead' % self
)
486 elif self
._object
_path
is None:
487 raise AttributeError('Object %r has no object path yet' % self
)
489 return self
._object
_path
492 def connection(self
):
493 """The Connection on which this object is available.
494 Access raises AttributeError if there is no Connection, or more than
497 Changed in 0.82.0: AttributeError can be raised.
499 if self
._connection
is _MANY
:
500 raise AttributeError('Object %r is on more than one Connection: '
501 'use Object.locations instead' % self
)
502 elif self
._connection
is None:
503 raise AttributeError('Object %r has no Connection yet' % self
)
505 return self
._connection
509 """An iterable over tuples representing locations at which this
512 Each tuple has at least two items, but may have more in future
513 versions of dbus-python, so do not rely on their exact length.
514 The first two items are the dbus.connection.Connection and the object
519 return iter(self
._locations
)
521 def add_to_connection(self
, connection
, path
):
522 """Make this object accessible via the given D-Bus connection and
526 `connection` : dbus.connection.Connection
527 Export the object on this connection. If the class attribute
528 SUPPORTS_MULTIPLE_CONNECTIONS is False (default), this object
529 can only be made available on one connection; if the class
530 attribute is set True by a subclass, the object can be made
531 available on more than one connection.
533 `path` : dbus.ObjectPath or other str
534 Place the object at this object path. If the class attribute
535 SUPPORTS_MULTIPLE_OBJECT_PATHS is False (default), this object
536 can only be made available at one object path; if the class
537 attribute is set True by a subclass, the object can be made
538 available with more than one object path.
540 :Raises ValueError: if the object's class attributes do not allow the
541 object to be exported in the desired way.
544 if path
== LOCAL_PATH
:
545 raise ValueError('Objects may not be exported on the reserved '
546 'path %s' % LOCAL_PATH
)
548 self
._locations
_lock
.acquire()
550 if (self
._connection
is not None and
551 self
._connection
is not connection
and
552 not self
.SUPPORTS_MULTIPLE_CONNECTIONS
):
553 raise ValueError('%r is already exported on '
554 'connection %r' % (self
, self
._connection
))
556 if (self
._object
_path
is not None and
557 not self
.SUPPORTS_MULTIPLE_OBJECT_PATHS
and
558 self
._object
_path
!= path
):
559 raise ValueError('%r is already exported at object '
560 'path %s' % (self
, self
._object
_path
))
562 connection
._register
_object
_path
(path
, self
._message
_cb
,
566 if self
._connection
is None:
567 self
._connection
= connection
568 elif self
._connection
is not connection
:
569 self
._connection
= _MANY
571 if self
._object
_path
is None:
572 self
._object
_path
= path
573 elif self
._object
_path
!= path
:
574 self
._object
_path
= _MANY
576 self
._locations
.append((connection
, path
, self
._fallback
))
578 self
._locations
_lock
.release()
580 def remove_from_connection(self
, connection
=None, path
=None):
581 """Make this object inaccessible via the given D-Bus connection
582 and object path. If no connection or path is specified,
583 the object ceases to be accessible via any connection or path.
585 It's not currently possible to export an object on more than one
586 connection or with more than one object-path, but this will be
590 `connection` : dbus.connection.Connection or None
591 Only remove the object from this Connection. If None,
592 remove from all Connections on which it's exported.
593 `path` : dbus.ObjectPath or other str, or None
594 Only remove the object from this object path. If None,
595 remove from all object paths.
597 if the object was not exported on the requested connection
598 or path, or (if both are None) was not exported at all.
601 self
._locations
_lock
.acquire()
603 if self
._object
_path
is None or self
._connection
is None:
604 raise LookupError('%r is not exported' % self
)
606 if connection
is not None or path
is not None:
608 for location
in self
._locations
:
609 if ((connection
is None or location
[0] is connection
) and
610 (path
is None or location
[1] == path
)):
611 dropped
.append(location
)
613 dropped
= self
._locations
617 raise LookupError('%r is not exported at a location matching '
618 '(%r,%r)' % (self
, connection
, path
))
620 for location
in dropped
:
622 location
[0]._unregister
_object
_path
(location
[1])
627 self
._locations
.remove(location
)
631 self
._locations
_lock
.release()
633 def _unregister_cb(self
, connection
):
634 # there's not really enough information to do anything useful here
635 _logger
.info('Unregistering exported object %r from some path '
636 'on %r', self
, connection
)
638 def _message_cb(self
, connection
, message
):
640 # lookup candidate method and parent method
641 method_name
= message
.get_member()
642 interface_name
= message
.get_interface()
643 (candidate_method
, parent_method
) = _method_lookup(self
, method_name
, interface_name
)
645 # set up method call parameters
646 args
= message
.get_args_list(**parent_method
._dbus
_get
_args
_options
)
649 if parent_method
._dbus
_out
_signature
is not None:
650 signature
= Signature(parent_method
._dbus
_out
_signature
)
654 # set up async callback functions
655 if parent_method
._dbus
_async
_callbacks
:
656 (return_callback
, error_callback
) = parent_method
._dbus
_async
_callbacks
657 keywords
[return_callback
] = lambda *retval
: _method_reply_return(connection
, message
, method_name
, signature
, *retval
)
658 keywords
[error_callback
] = lambda exception
: _method_reply_error(connection
, message
, exception
)
660 # include the sender etc. if desired
661 if parent_method
._dbus
_sender
_keyword
:
662 keywords
[parent_method
._dbus
_sender
_keyword
] = message
.get_sender()
663 if parent_method
._dbus
_path
_keyword
:
664 keywords
[parent_method
._dbus
_path
_keyword
] = message
.get_path()
665 if parent_method
._dbus
_rel
_path
_keyword
:
666 path
= message
.get_path()
668 for exp
in self
._locations
:
669 # pathological case: if we're exported in two places,
670 # one of which is a subtree of the other, then pick the
671 # subtree by preference (i.e. minimize the length of
673 if exp
[0] is connection
:
678 # we already have rel_path == path at the beginning
680 if path
.startswith(exp
[1] + '/'):
681 # yes we're in this exported subtree
682 suffix
= path
[len(exp
[1]):]
683 if len(suffix
) < len(rel_path
):
685 rel_path
= ObjectPath(rel_path
)
686 keywords
[parent_method
._dbus
_rel
_path
_keyword
] = rel_path
688 if parent_method
._dbus
_destination
_keyword
:
689 keywords
[parent_method
._dbus
_destination
_keyword
] = message
.get_destination()
690 if parent_method
._dbus
_message
_keyword
:
691 keywords
[parent_method
._dbus
_message
_keyword
] = message
692 if parent_method
._dbus
_connection
_keyword
:
693 keywords
[parent_method
._dbus
_connection
_keyword
] = connection
696 retval
= candidate_method(self
, *args
, **keywords
)
698 # we're done - the method has got callback functions to reply with
699 if parent_method
._dbus
_async
_callbacks
:
702 # otherwise we send the return values in a reply. if we have a
703 # signature, use it to turn the return value into a tuple as
705 if signature
is not None:
706 signature_tuple
= tuple(signature
)
707 # if we have zero or one return values we want make a tuple
708 # for the _method_reply_return function, otherwise we need
709 # to check we're passing it a sequence
710 if len(signature_tuple
) == 0:
714 raise TypeError('%s has an empty output signature but did not return None' %
716 elif len(signature_tuple
) == 1:
719 if operator
.isSequenceType(retval
):
720 # multi-value signature, multi-value return... proceed unchanged
723 raise TypeError('%s has multiple output values in signature %s but did not return a sequence' %
724 (method_name
, signature
))
726 # no signature, so just turn the return into a tuple and send it as normal
730 elif (isinstance(retval
, tuple)
731 and not isinstance(retval
, Struct
)):
732 # If the return is a tuple that is not a Struct, we use it
733 # as-is on the assumption that there are multiple return
734 # values - this is the usual Python idiom. (fd.o #10174)
739 _method_reply_return(connection
, message
, method_name
, signature
, *retval
)
740 except Exception, exception
:
742 _method_reply_error(connection
, message
, exception
)
744 @method(INTROSPECTABLE_IFACE
, in_signature
='', out_signature
='s',
745 path_keyword
='object_path', connection_keyword
='connection')
746 def Introspect(self
, object_path
, connection
):
747 """Return a string of XML encoding this object's supported interfaces,
750 reflection_data
= _dbus_bindings
.DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
751 reflection_data
+= '<node name="%s">\n' % object_path
753 interfaces
= self
._dbus
_class
_table
[self
.__class
__.__module
__ + '.' + self
.__class
__.__name
__]
754 for (name
, funcs
) in interfaces
.iteritems():
755 reflection_data
+= ' <interface name="%s">\n' % (name
)
757 for func
in funcs
.values():
758 if getattr(func
, '_dbus_is_method', False):
759 reflection_data
+= self
.__class
__._reflect
_on
_method
(func
)
760 elif getattr(func
, '_dbus_is_signal', False):
761 reflection_data
+= self
.__class
__._reflect
_on
_signal
(func
)
763 reflection_data
+= ' </interface>\n'
765 for name
in connection
.list_exported_child_objects(object_path
):
766 reflection_data
+= ' <node name="%s"/>\n' % name
768 reflection_data
+= '</node>\n'
770 return reflection_data
774 if (self
._object
_path
is not _MANY
775 and self
._object
_path
is not None):
776 where
= ' at %s' % self
._object
_path
777 return '<%s.%s%s at %#x>' % (self
.__class
__.__module
__,
778 self
.__class
__.__name
__, where
,
782 class FallbackObject(Object
):
783 """An object that implements an entire subtree of the object-path
789 SUPPORTS_MULTIPLE_OBJECT_PATHS
= True
791 def __init__(self
, conn
=None, object_path
=None):
794 Note that the superclass' ``bus_name`` __init__ argument is not
798 `conn` : dbus.connection.Connection or None
799 The connection on which to export this object. If this is not
800 None, an `object_path` must also be provided.
802 If None, the object is not initially available on any
805 `object_path` : str or None
806 A D-Bus object path at which to make this Object available
807 immediately. If this is not None, a `conn` must also be
810 This object will implements all object-paths in the subtree
811 starting at this object-path, except where a more specific
812 object has been added.
814 super(FallbackObject
, self
).__init
__()
815 self
._fallback
= True
818 if object_path
is not None:
819 raise TypeError('If object_path is given, conn is required')
820 elif object_path
is None:
821 raise TypeError('If conn is given, object_path is required')
823 self
.add_to_connection(conn
, object_path
)