1 # Copyright (C) 2003-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 # Permission is hereby granted, free of charge, to any person
7 # obtaining a copy of this software and associated documentation
8 # files (the "Software"), to deal in the Software without
9 # restriction, including without limitation the rights to use, copy,
10 # modify, merge, publish, distribute, sublicense, and/or sell copies
11 # of the Software, and to permit persons to whom the Software is
12 # furnished to do so, subject to the following conditions:
14 # The above copyright notice and this permission notice shall be
15 # included in all copies or substantial portions of the Software.
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 # DEALINGS IN THE SOFTWARE.
26 __all__
= ('BusName', 'Object', 'method', 'signal')
27 __docformat__
= 'restructuredtext'
36 import dummy_thread
as thread
39 from dbus
import SessionBus
, Signature
, Struct
, validate_bus_name
, \
40 validate_object_path
, INTROSPECTABLE_IFACE
, ObjectPath
41 from dbus
.decorators
import method
, signal
42 from dbus
.exceptions
import DBusException
, \
43 NameExistsException
, \
44 UnknownMethodException
45 from dbus
.lowlevel
import ErrorMessage
, MethodReturnMessage
, MethodCallMessage
46 from dbus
.proxies
import LOCAL_PATH
49 _logger
= logging
.getLogger('dbus.service')
52 class _VariantSignature(object):
53 """A fake method signature which, when iterated, yields an endless stream
54 of 'v' characters representing variants (handy with zip()).
56 It has no string representation.
63 """Return 'v' whenever called."""
66 class BusName(object):
67 """A base class for exporting your own Named Services across the Bus.
69 When instantiated, objects of this class attempt to claim the given
70 well-known name on the given bus for the current process. The name is
71 released when the BusName object becomes unreferenced.
73 If a well-known name is requested multiple times, multiple references
74 to the same BusName object will be returned.
78 - Assumes that named services are only ever requested using this class -
79 if you request names from the bus directly, confusion may occur.
80 - Does not handle queueing.
82 def __new__(cls
, name
, bus
=None, allow_replacement
=False , replace_existing
=False, do_not_queue
=False):
83 """Constructor, which may either return an existing cached object
88 The well-known name to be advertised
90 A Bus on which this service will be advertised.
92 Omitting this parameter or setting it to None has been
93 deprecated since version 0.82.1. For backwards compatibility,
94 if this is done, the global shared connection to the session
97 `allow_replacement` : bool
98 If True, other processes trying to claim the same well-known
99 name will take precedence over this one.
100 `replace_existing` : bool
101 If True, this process can take over the well-known name
102 from other processes already holding it.
103 `do_not_queue` : bool
104 If True, this service will not be placed in the queue of
105 services waiting for the requested name if another service
108 validate_bus_name(name
, allow_well_known
=True, allow_unique
=False)
110 # if necessary, get default bus (deprecated)
113 warnings
.warn('Omitting the "bus" parameter to '
114 'dbus.service.BusName.__init__ is deprecated',
115 DeprecationWarning, stacklevel
=2)
118 # see if this name is already defined, return it if so
119 # FIXME: accessing internals of Bus
120 if name
in bus
._bus
_names
:
121 return bus
._bus
_names
[name
]
123 # otherwise register the name
125 (allow_replacement
and _dbus_bindings
.NAME_FLAG_ALLOW_REPLACEMENT
or 0) |
126 (replace_existing
and _dbus_bindings
.NAME_FLAG_REPLACE_EXISTING
or 0) |
127 (do_not_queue
and _dbus_bindings
.NAME_FLAG_DO_NOT_QUEUE
or 0))
129 retval
= bus
.request_name(name
, name_flags
)
131 # TODO: more intelligent tracking of bus name states?
132 if retval
== _dbus_bindings
.REQUEST_NAME_REPLY_PRIMARY_OWNER
:
134 elif retval
== _dbus_bindings
.REQUEST_NAME_REPLY_IN_QUEUE
:
135 # queueing can happen by default, maybe we should
136 # track this better or let the user know if they're
139 elif retval
== _dbus_bindings
.REQUEST_NAME_REPLY_EXISTS
:
140 raise NameExistsException(name
)
141 elif retval
== _dbus_bindings
.REQUEST_NAME_REPLY_ALREADY_OWNER
:
142 # if this is a shared bus which is being used by someone
143 # else in this process, this can happen legitimately
146 raise RuntimeError('requesting bus name %s returned unexpected value %s' % (name
, retval
))
148 # and create the object
149 bus_name
= object.__new
__(cls
)
151 bus_name
._name
= name
153 # cache instance (weak ref only)
154 # FIXME: accessing Bus internals again
155 bus
._bus
_names
[name
] = bus_name
159 # do nothing because this is called whether or not the bus name
160 # object was retrieved from the cache or created new
161 def __init__(self
, *args
, **keywords
):
164 # we can delete the low-level name here because these objects
165 # are guaranteed to exist only once for each bus name
167 self
._bus
.release_name(self
._name
)
171 """Get the Bus this Service is on"""
175 """Get the name of this service"""
179 return '<dbus.service.BusName %s on %r at %#x>' % (self
._name
, self
._bus
, id(self
))
183 def _method_lookup(self
, method_name
, dbus_interface
):
184 """Walks the Python MRO of the given class to find the method to invoke.
186 Returns two methods, the one to call, and the one it inherits from which
187 defines its D-Bus interface name, signature, and attributes.
190 candidate_class
= None
193 # split up the cases when we do and don't have an interface because the
194 # latter is much simpler
196 # search through the class hierarchy in python MRO order
197 for cls
in self
.__class
__.__mro
__:
198 # if we haven't got a candidate class yet, and we find a class with a
199 # suitably named member, save this as a candidate class
200 if (not candidate_class
and method_name
in cls
.__dict
__):
201 if ("_dbus_is_method" in cls
.__dict
__[method_name
].__dict
__
202 and "_dbus_interface" in cls
.__dict
__[method_name
].__dict
__):
203 # however if it is annotated for a different interface
204 # than we are looking for, it cannot be a candidate
205 if cls
.__dict
__[method_name
]._dbus
_interface
== dbus_interface
:
206 candidate_class
= cls
207 parent_method
= cls
.__dict
__[method_name
]
213 candidate_class
= cls
215 # if we have a candidate class, carry on checking this and all
216 # superclasses for a method annoated as a dbus method
217 # on the correct interface
218 if (candidate_class
and method_name
in cls
.__dict
__
219 and "_dbus_is_method" in cls
.__dict
__[method_name
].__dict
__
220 and "_dbus_interface" in cls
.__dict
__[method_name
].__dict
__
221 and cls
.__dict
__[method_name
]._dbus
_interface
== dbus_interface
):
222 # the candidate class has a dbus method on the correct interface,
223 # or overrides a method that is, success!
224 parent_method
= cls
.__dict
__[method_name
]
229 # simpler version of above
230 for cls
in self
.__class
__.__mro
__:
231 if (not candidate_class
and method_name
in cls
.__dict
__):
232 candidate_class
= cls
234 if (candidate_class
and method_name
in cls
.__dict
__
235 and "_dbus_is_method" in cls
.__dict
__[method_name
].__dict
__):
236 parent_method
= cls
.__dict
__[method_name
]
241 return (candidate_class
.__dict
__[method_name
], parent_method
)
244 raise UnknownMethodException('%s is not a valid method of interface %s' % (method_name
, dbus_interface
))
246 raise UnknownMethodException('%s is not a valid method' % method_name
)
249 def _method_reply_return(connection
, message
, method_name
, signature
, *retval
):
250 reply
= MethodReturnMessage(message
)
252 reply
.append(signature
=signature
, *retval
)
254 logging
.basicConfig()
255 if signature
is None:
257 signature
= reply
.guess_signature(retval
) + ' (guessed)'
259 _logger
.error('Unable to guess signature for arguments %r: '
260 '%s: %s', retval
, e
.__class
__, e
)
262 _logger
.error('Unable to append %r to message with signature %s: '
263 '%s: %s', retval
, signature
, e
.__class
__, e
)
266 connection
.send_message(reply
)
269 def _method_reply_error(connection
, message
, exception
):
270 name
= getattr(exception
, '_dbus_error_name', None)
274 elif getattr(exception
, '__module__', '') in ('', '__main__'):
275 name
= 'org.freedesktop.DBus.Python.%s' % exception
.__class
__.__name
__
277 name
= 'org.freedesktop.DBus.Python.%s.%s' % (exception
.__module
__, exception
.__class
__.__name
__)
279 et
, ev
, etb
= sys
.exc_info()
281 # The exception was actually thrown, so we can get a traceback
282 contents
= ''.join(traceback
.format_exception(et
, ev
, etb
))
284 # We don't have any traceback for it, e.g.
285 # async_err_cb(MyException('Failed to badger the mushroom'))
286 # see also https://bugs.freedesktop.org/show_bug.cgi?id=12403
287 contents
= ''.join(traceback
.format_exception_only(exception
.__class
__,
289 reply
= ErrorMessage(message
, name
, contents
)
291 connection
.send_message(reply
)
294 class InterfaceType(type):
295 def __init__(cls
, name
, bases
, dct
):
296 # these attributes are shared between all instances of the Interface
297 # object, so this has to be a dictionary that maps class names to
298 # the per-class introspection/interface data
299 class_table
= getattr(cls
, '_dbus_class_table', {})
300 cls
._dbus
_class
_table
= class_table
301 interface_table
= class_table
[cls
.__module
__ + '.' + name
] = {}
303 # merge all the name -> method tables for all the interfaces
304 # implemented by our base classes into our own
306 base_name
= b
.__module
__ + '.' + b
.__name
__
307 if getattr(b
, '_dbus_class_table', False):
308 for (interface
, method_table
) in class_table
[base_name
].iteritems():
309 our_method_table
= interface_table
.setdefault(interface
, {})
310 our_method_table
.update(method_table
)
312 # add in all the name -> method entries for our own methods/signals
313 for func
in dct
.values():
314 if getattr(func
, '_dbus_interface', False):
315 method_table
= interface_table
.setdefault(func
._dbus
_interface
, {})
316 method_table
[func
.__name
__] = func
318 super(InterfaceType
, cls
).__init
__(name
, bases
, dct
)
320 # methods are different to signals, so we have two functions... :)
321 def _reflect_on_method(cls
, func
):
322 args
= func
._dbus
_args
324 if func
._dbus
_in
_signature
:
325 # convert signature into a tuple so length refers to number of
326 # types, not number of characters. the length is checked by
327 # the decorator to make sure it matches the length of args.
328 in_sig
= tuple(Signature(func
._dbus
_in
_signature
))
330 # magic iterator which returns as many v's as we need
331 in_sig
= _VariantSignature()
333 if func
._dbus
_out
_signature
:
334 out_sig
= Signature(func
._dbus
_out
_signature
)
336 # its tempting to default to Signature('v'), but
337 # for methods that return nothing, providing incorrect
338 # introspection data is worse than providing none at all
341 reflection_data
= ' <method name="%s">\n' % (func
.__name
__)
342 for pair
in zip(in_sig
, args
):
343 reflection_data
+= ' <arg direction="in" type="%s" name="%s" />\n' % pair
345 reflection_data
+= ' <arg direction="out" type="%s" />\n' % type
346 reflection_data
+= ' </method>\n'
348 return reflection_data
350 def _reflect_on_signal(cls
, func
):
351 args
= func
._dbus
_args
353 if func
._dbus
_signature
:
354 # convert signature into a tuple so length refers to number of
355 # types, not number of characters
356 sig
= tuple(Signature(func
._dbus
_signature
))
358 # magic iterator which returns as many v's as we need
359 sig
= _VariantSignature()
361 reflection_data
= ' <signal name="%s">\n' % (func
.__name
__)
362 for pair
in zip(sig
, args
):
363 reflection_data
= reflection_data
+ ' <arg type="%s" name="%s" />\n' % pair
364 reflection_data
= reflection_data
+ ' </signal>\n'
366 return reflection_data
368 class Interface(object):
369 __metaclass__
= InterfaceType
371 #: A unique object used as the value of Object._object_path and
372 #: Object._connection if it's actually in more than one place
375 class Object(Interface
):
376 r
"""A base class for exporting your own Objects across the Bus.
378 Just inherit from Object and mark exported methods with the
379 @\ `dbus.service.method` or @\ `dbus.service.signal` decorator.
383 class Example(dbus.service.object):
384 def __init__(self, object_path):
385 dbus.service.Object.__init__(self, dbus.SessionBus(), path)
386 self._last_input = None
388 @dbus.service.method(interface='com.example.Sample',
389 in_signature='v', out_signature='s')
390 def StringifyVariant(self, var):
391 self.LastInputChanged(var) # emits the signal
394 @dbus.service.signal(interface='com.example.Sample',
396 def LastInputChanged(self, var):
397 # run just before the signal is actually emitted
398 # just put "pass" if nothing should happen
399 self._last_input = var
401 @dbus.service.method(interface='com.example.Sample',
402 in_signature='', out_signature='v')
403 def GetLastInput(self):
404 return self._last_input
407 #: If True, this object can be made available at more than one object path.
408 #: If True but `SUPPORTS_MULTIPLE_CONNECTIONS` is False, the object may
409 #: handle more than one object path, but they must all be on the same
411 SUPPORTS_MULTIPLE_OBJECT_PATHS
= False
413 #: If True, this object can be made available on more than one connection.
414 #: If True but `SUPPORTS_MULTIPLE_OBJECT_PATHS` is False, the object must
415 #: have the same object path on all its connections.
416 SUPPORTS_MULTIPLE_CONNECTIONS
= False
418 def __init__(self
, conn
=None, object_path
=None, bus_name
=None):
419 """Constructor. Either conn or bus_name is required; object_path
423 `conn` : dbus.connection.Connection or None
424 The connection on which to export this object.
426 If None, use the Bus associated with the given ``bus_name``.
427 If there is no ``bus_name`` either, the object is not
428 initially available on any Connection.
430 For backwards compatibility, if an instance of
431 dbus.service.BusName is passed as the first parameter,
432 this is equivalent to passing its associated Bus as
433 ``conn``, and passing the BusName itself as ``bus_name``.
435 `object_path` : str or None
436 A D-Bus object path at which to make this Object available
437 immediately. If this is not None, a `conn` or `bus_name` must
440 `bus_name` : dbus.service.BusName or None
441 Represents a well-known name claimed by this process. A
442 reference to the BusName object will be held by this
443 Object, preventing the name from being released during this
444 Object's lifetime (unless it's released manually).
446 if object_path
is not None:
447 validate_object_path(object_path
)
449 if isinstance(conn
, BusName
):
450 # someone's using the old API; don't gratuitously break them
452 conn
= bus_name
.get_bus()
454 if bus_name
is not None:
455 # someone's using the old API but naming arguments, probably
456 conn
= bus_name
.get_bus()
458 #: Either an object path, None or _MANY
459 self
._object
_path
= None
460 #: Either a dbus.connection.Connection, None or _MANY
461 self
._connection
= None
462 #: A list of tuples (Connection, object path, False) where the False
463 #: is for future expansion (to support fallback paths)
465 #: Lock protecting `_locations`, `_connection` and `_object_path`
466 self
._locations
_lock
= thread
.allocate_lock()
468 #: True if this is a fallback object handling a whole subtree.
469 self
._fallback
= False
471 self
._name
= bus_name
473 if conn
is None and object_path
is not None:
474 raise TypeError('If object_path is given, either conn or bus_name '
476 if conn
is not None and object_path
is not None:
477 self
.add_to_connection(conn
, object_path
)
480 def __dbus_object_path__(self
):
481 """The object-path at which this object is available.
482 Access raises AttributeError if there is no object path, or more than
485 Changed in 0.82.0: AttributeError can be raised.
487 if self
._object
_path
is _MANY
:
488 raise AttributeError('Object %r has more than one object path: '
489 'use Object.locations instead' % self
)
490 elif self
._object
_path
is None:
491 raise AttributeError('Object %r has no object path yet' % self
)
493 return self
._object
_path
496 def connection(self
):
497 """The Connection on which this object is available.
498 Access raises AttributeError if there is no Connection, or more than
501 Changed in 0.82.0: AttributeError can be raised.
503 if self
._connection
is _MANY
:
504 raise AttributeError('Object %r is on more than one Connection: '
505 'use Object.locations instead' % self
)
506 elif self
._connection
is None:
507 raise AttributeError('Object %r has no Connection yet' % self
)
509 return self
._connection
513 """An iterable over tuples representing locations at which this
516 Each tuple has at least two items, but may have more in future
517 versions of dbus-python, so do not rely on their exact length.
518 The first two items are the dbus.connection.Connection and the object
523 return iter(self
._locations
)
525 def add_to_connection(self
, connection
, path
):
526 """Make this object accessible via the given D-Bus connection and
530 `connection` : dbus.connection.Connection
531 Export the object on this connection. If the class attribute
532 SUPPORTS_MULTIPLE_CONNECTIONS is False (default), this object
533 can only be made available on one connection; if the class
534 attribute is set True by a subclass, the object can be made
535 available on more than one connection.
537 `path` : dbus.ObjectPath or other str
538 Place the object at this object path. If the class attribute
539 SUPPORTS_MULTIPLE_OBJECT_PATHS is False (default), this object
540 can only be made available at one object path; if the class
541 attribute is set True by a subclass, the object can be made
542 available with more than one object path.
544 :Raises ValueError: if the object's class attributes do not allow the
545 object to be exported in the desired way.
548 if path
== LOCAL_PATH
:
549 raise ValueError('Objects may not be exported on the reserved '
550 'path %s' % LOCAL_PATH
)
552 self
._locations
_lock
.acquire()
554 if (self
._connection
is not None and
555 self
._connection
is not connection
and
556 not self
.SUPPORTS_MULTIPLE_CONNECTIONS
):
557 raise ValueError('%r is already exported on '
558 'connection %r' % (self
, self
._connection
))
560 if (self
._object
_path
is not None and
561 not self
.SUPPORTS_MULTIPLE_OBJECT_PATHS
and
562 self
._object
_path
!= path
):
563 raise ValueError('%r is already exported at object '
564 'path %s' % (self
, self
._object
_path
))
566 connection
._register
_object
_path
(path
, self
._message
_cb
,
570 if self
._connection
is None:
571 self
._connection
= connection
572 elif self
._connection
is not connection
:
573 self
._connection
= _MANY
575 if self
._object
_path
is None:
576 self
._object
_path
= path
577 elif self
._object
_path
!= path
:
578 self
._object
_path
= _MANY
580 self
._locations
.append((connection
, path
, self
._fallback
))
582 self
._locations
_lock
.release()
584 def remove_from_connection(self
, connection
=None, path
=None):
585 """Make this object inaccessible via the given D-Bus connection
586 and object path. If no connection or path is specified,
587 the object ceases to be accessible via any connection or path.
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
):
639 if not isinstance(message
, MethodCallMessage
):
643 # lookup candidate method and parent method
644 method_name
= message
.get_member()
645 interface_name
= message
.get_interface()
646 (candidate_method
, parent_method
) = _method_lookup(self
, method_name
, interface_name
)
648 # set up method call parameters
649 args
= message
.get_args_list(**parent_method
._dbus
_get
_args
_options
)
652 if parent_method
._dbus
_out
_signature
is not None:
653 signature
= Signature(parent_method
._dbus
_out
_signature
)
657 # set up async callback functions
658 if parent_method
._dbus
_async
_callbacks
:
659 (return_callback
, error_callback
) = parent_method
._dbus
_async
_callbacks
660 keywords
[return_callback
] = lambda *retval
: _method_reply_return(connection
, message
, method_name
, signature
, *retval
)
661 keywords
[error_callback
] = lambda exception
: _method_reply_error(connection
, message
, exception
)
663 # include the sender etc. if desired
664 if parent_method
._dbus
_sender
_keyword
:
665 keywords
[parent_method
._dbus
_sender
_keyword
] = message
.get_sender()
666 if parent_method
._dbus
_path
_keyword
:
667 keywords
[parent_method
._dbus
_path
_keyword
] = message
.get_path()
668 if parent_method
._dbus
_rel
_path
_keyword
:
669 path
= message
.get_path()
671 for exp
in self
._locations
:
672 # pathological case: if we're exported in two places,
673 # one of which is a subtree of the other, then pick the
674 # subtree by preference (i.e. minimize the length of
676 if exp
[0] is connection
:
681 # we already have rel_path == path at the beginning
683 if path
.startswith(exp
[1] + '/'):
684 # yes we're in this exported subtree
685 suffix
= path
[len(exp
[1]):]
686 if len(suffix
) < len(rel_path
):
688 rel_path
= ObjectPath(rel_path
)
689 keywords
[parent_method
._dbus
_rel
_path
_keyword
] = rel_path
691 if parent_method
._dbus
_destination
_keyword
:
692 keywords
[parent_method
._dbus
_destination
_keyword
] = message
.get_destination()
693 if parent_method
._dbus
_message
_keyword
:
694 keywords
[parent_method
._dbus
_message
_keyword
] = message
695 if parent_method
._dbus
_connection
_keyword
:
696 keywords
[parent_method
._dbus
_connection
_keyword
] = connection
699 retval
= candidate_method(self
, *args
, **keywords
)
701 # we're done - the method has got callback functions to reply with
702 if parent_method
._dbus
_async
_callbacks
:
705 # otherwise we send the return values in a reply. if we have a
706 # signature, use it to turn the return value into a tuple as
708 if signature
is not None:
709 signature_tuple
= tuple(signature
)
710 # if we have zero or one return values we want make a tuple
711 # for the _method_reply_return function, otherwise we need
712 # to check we're passing it a sequence
713 if len(signature_tuple
) == 0:
717 raise TypeError('%s has an empty output signature but did not return None' %
719 elif len(signature_tuple
) == 1:
722 if operator
.isSequenceType(retval
):
723 # multi-value signature, multi-value return... proceed unchanged
726 raise TypeError('%s has multiple output values in signature %s but did not return a sequence' %
727 (method_name
, signature
))
729 # no signature, so just turn the return into a tuple and send it as normal
733 elif (isinstance(retval
, tuple)
734 and not isinstance(retval
, Struct
)):
735 # If the return is a tuple that is not a Struct, we use it
736 # as-is on the assumption that there are multiple return
737 # values - this is the usual Python idiom. (fd.o #10174)
742 _method_reply_return(connection
, message
, method_name
, signature
, *retval
)
743 except Exception, exception
:
745 _method_reply_error(connection
, message
, exception
)
747 @method(INTROSPECTABLE_IFACE
, in_signature
='', out_signature
='s',
748 path_keyword
='object_path', connection_keyword
='connection')
749 def Introspect(self
, object_path
, connection
):
750 """Return a string of XML encoding this object's supported interfaces,
753 reflection_data
= _dbus_bindings
.DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
754 reflection_data
+= '<node name="%s">\n' % object_path
756 interfaces
= self
._dbus
_class
_table
[self
.__class
__.__module
__ + '.' + self
.__class
__.__name
__]
757 for (name
, funcs
) in interfaces
.iteritems():
758 reflection_data
+= ' <interface name="%s">\n' % (name
)
760 for func
in funcs
.values():
761 if getattr(func
, '_dbus_is_method', False):
762 reflection_data
+= self
.__class
__._reflect
_on
_method
(func
)
763 elif getattr(func
, '_dbus_is_signal', False):
764 reflection_data
+= self
.__class
__._reflect
_on
_signal
(func
)
766 reflection_data
+= ' </interface>\n'
768 for name
in connection
.list_exported_child_objects(object_path
):
769 reflection_data
+= ' <node name="%s"/>\n' % name
771 reflection_data
+= '</node>\n'
773 return reflection_data
777 if (self
._object
_path
is not _MANY
778 and self
._object
_path
is not None):
779 where
= ' at %s' % self
._object
_path
780 return '<%s.%s%s at %#x>' % (self
.__class
__.__module
__,
781 self
.__class
__.__name
__, where
,
785 class FallbackObject(Object
):
786 """An object that implements an entire subtree of the object-path
792 SUPPORTS_MULTIPLE_OBJECT_PATHS
= True
794 def __init__(self
, conn
=None, object_path
=None):
797 Note that the superclass' ``bus_name`` __init__ argument is not
801 `conn` : dbus.connection.Connection or None
802 The connection on which to export this object. If this is not
803 None, an `object_path` must also be provided.
805 If None, the object is not initially available on any
808 `object_path` : str or None
809 A D-Bus object path at which to make this Object available
810 immediately. If this is not None, a `conn` must also be
813 This object will implements all object-paths in the subtree
814 starting at this object-path, except where a more specific
815 object has been added.
817 super(FallbackObject
, self
).__init
__()
818 self
._fallback
= True
821 if object_path
is not None:
822 raise TypeError('If object_path is given, conn is required')
823 elif object_path
is None:
824 raise TypeError('If conn is given, object_path is required')
826 self
.add_to_connection(conn
, object_path
)