Package dbus :: Module service
[hide private]
[frames] | no frames]

Source Code for Module dbus.service

  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/> 
  5  # 
  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: 
 13  # 
 14  # The above copyright notice and this permission notice shall be 
 15  # included in all copies or substantial portions of the Software. 
 16  # 
 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. 
 25   
 26  __all__ = ('BusName', 'Object', 'method', 'signal') 
 27  __docformat__ = 'restructuredtext' 
 28   
 29  import sys 
 30  import logging 
 31  import operator 
 32  import traceback 
 33  try: 
 34      import thread 
 35  except ImportError: 
 36      import dummy_thread as thread 
 37   
 38  import _dbus_bindings 
 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 
 47   
 48   
 49  _logger = logging.getLogger('dbus.service') 
50 51 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()). 55 56 It has no string representation. 57 """
58 - def __iter__(self):
59 """Return self.""" 60 return self
61
62 - def next(self):
63 """Return 'v' whenever called.""" 64 return 'v'
65
66 -class BusName(object):
67 """A base class for exporting your own Named Services across the Bus. 68 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. 72 73 If a well-known name is requested multiple times, multiple references 74 to the same BusName object will be returned. 75 76 Caveats 77 ------- 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. 81 """
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 84 or a new object. 85 86 :Parameters: 87 `name` : str 88 The well-known name to be advertised 89 `bus` : dbus.Bus 90 A Bus on which this service will be advertised. 91 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 95 bus will be used. 96 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 106 already holds it. 107 """ 108 validate_bus_name(name, allow_well_known=True, allow_unique=False) 109 110 # if necessary, get default bus (deprecated) 111 if bus is None: 112 import warnings 113 warnings.warn('Omitting the "bus" parameter to ' 114 'dbus.service.BusName.__init__ is deprecated', 115 DeprecationWarning, stacklevel=2) 116 bus = SessionBus() 117 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] 122 123 # otherwise register the name 124 name_flags = ( 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)) 128 129 retval = bus.request_name(name, name_flags) 130 131 # TODO: more intelligent tracking of bus name states? 132 if retval == _dbus_bindings.REQUEST_NAME_REPLY_PRIMARY_OWNER: 133 pass 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 137 # queued or not? 138 pass 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 144 pass 145 else: 146 raise RuntimeError('requesting bus name %s returned unexpected value %s' % (name, retval)) 147 148 # and create the object 149 bus_name = object.__new__(cls) 150 bus_name._bus = bus 151 bus_name._name = name 152 153 # cache instance (weak ref only) 154 # FIXME: accessing Bus internals again 155 bus._bus_names[name] = bus_name 156 157 return bus_name
158 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):
162 pass
163 164 # we can delete the low-level name here because these objects 165 # are guaranteed to exist only once for each bus name
166 - def __del__(self):
167 self._bus.release_name(self._name) 168 pass
169
170 - def get_bus(self):
171 """Get the Bus this Service is on""" 172 return self._bus
173
174 - def get_name(self):
175 """Get the name of this service""" 176 return self._name
177
178 - def __repr__(self):
179 return '<dbus.service.BusName %s on %r at %#x>' % (self._name, self._bus, id(self))
180 __str__ = __repr__
181
182 183 -def _method_lookup(self, method_name, dbus_interface):
184 """Walks the Python MRO of the given class to find the method to invoke. 185 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. 188 """ 189 parent_method = None 190 candidate_class = None 191 successful = False 192 193 # split up the cases when we do and don't have an interface because the 194 # latter is much simpler 195 if dbus_interface: 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] 208 successful = True 209 break 210 else: 211 pass 212 else: 213 candidate_class = cls 214 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] 225 successful = True 226 break 227 228 else: 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 233 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] 237 successful = True 238 break 239 240 if successful: 241 return (candidate_class.__dict__[method_name], parent_method) 242 else: 243 if dbus_interface: 244 raise UnknownMethodException('%s is not a valid method of interface %s' % (method_name, dbus_interface)) 245 else: 246 raise UnknownMethodException('%s is not a valid method' % method_name)
247
248 249 -def _method_reply_return(connection, message, method_name, signature, *retval):
250 reply = MethodReturnMessage(message) 251 try: 252 reply.append(signature=signature, *retval) 253 except Exception, e: 254 logging.basicConfig() 255 if signature is None: 256 try: 257 signature = reply.guess_signature(retval) + ' (guessed)' 258 except Exception, e: 259 _logger.error('Unable to guess signature for arguments %r: ' 260 '%s: %s', retval, e.__class__, e) 261 raise 262 _logger.error('Unable to append %r to message with signature %s: ' 263 '%s: %s', retval, signature, e.__class__, e) 264 raise 265 266 connection.send_message(reply)
267
268 269 -def _method_reply_error(connection, message, exception):
270 name = getattr(exception, '_dbus_error_name', None) 271 272 if name is not None: 273 pass 274 elif getattr(exception, '__module__', '') in ('', '__main__'): 275 name = 'org.freedesktop.DBus.Python.%s' % exception.__class__.__name__ 276 else: 277 name = 'org.freedesktop.DBus.Python.%s.%s' % (exception.__module__, exception.__class__.__name__) 278 279 et, ev, etb = sys.exc_info() 280 if isinstance(exception, DBusException) and not exception.include_traceback: 281 # We don't actually want the traceback anyway 282 contents = exception.get_dbus_message() 283 elif ev is exception: 284 # The exception was actually thrown, so we can get a traceback 285 contents = ''.join(traceback.format_exception(et, ev, etb)) 286 else: 287 # We don't have any traceback for it, e.g. 288 # async_err_cb(MyException('Failed to badger the mushroom')) 289 # see also https://bugs.freedesktop.org/show_bug.cgi?id=12403 290 contents = ''.join(traceback.format_exception_only(exception.__class__, 291 exception)) 292 reply = ErrorMessage(message, name, contents) 293 294 connection.send_message(reply)
295
296 297 -class InterfaceType(type):
298 - def __init__(cls, name, bases, dct):
299 # these attributes are shared between all instances of the Interface 300 # object, so this has to be a dictionary that maps class names to 301 # the per-class introspection/interface data 302 class_table = getattr(cls, '_dbus_class_table', {}) 303 cls._dbus_class_table = class_table 304 interface_table = class_table[cls.__module__ + '.' + name] = {} 305 306 # merge all the name -> method tables for all the interfaces 307 # implemented by our base classes into our own 308 for b in bases: 309 base_name = b.__module__ + '.' + b.__name__ 310 if getattr(b, '_dbus_class_table', False): 311 for (interface, method_table) in class_table[base_name].iteritems(): 312 our_method_table = interface_table.setdefault(interface, {}) 313 our_method_table.update(method_table) 314 315 # add in all the name -> method entries for our own methods/signals 316 for func in dct.values(): 317 if getattr(func, '_dbus_interface', False): 318 method_table = interface_table.setdefault(func._dbus_interface, {}) 319 method_table[func.__name__] = func 320 321 super(InterfaceType, cls).__init__(name, bases, dct)
322 323 # methods are different to signals, so we have two functions... :)
324 - def _reflect_on_method(cls, func):
325 args = func._dbus_args 326 327 if func._dbus_in_signature: 328 # convert signature into a tuple so length refers to number of 329 # types, not number of characters. the length is checked by 330 # the decorator to make sure it matches the length of args. 331 in_sig = tuple(Signature(func._dbus_in_signature)) 332 else: 333 # magic iterator which returns as many v's as we need 334 in_sig = _VariantSignature() 335 336 if func._dbus_out_signature: 337 out_sig = Signature(func._dbus_out_signature) 338 else: 339 # its tempting to default to Signature('v'), but 340 # for methods that return nothing, providing incorrect 341 # introspection data is worse than providing none at all 342 out_sig = [] 343 344 reflection_data = ' <method name="%s">\n' % (func.__name__) 345 for pair in zip(in_sig, args): 346 reflection_data += ' <arg direction="in" type="%s" name="%s" />\n' % pair 347 for type in out_sig: 348 reflection_data += ' <arg direction="out" type="%s" />\n' % type 349 reflection_data += ' </method>\n' 350 351 return reflection_data
352
353 - def _reflect_on_signal(cls, func):
354 args = func._dbus_args 355 356 if func._dbus_signature: 357 # convert signature into a tuple so length refers to number of 358 # types, not number of characters 359 sig = tuple(Signature(func._dbus_signature)) 360 else: 361 # magic iterator which returns as many v's as we need 362 sig = _VariantSignature() 363 364 reflection_data = ' <signal name="%s">\n' % (func.__name__) 365 for pair in zip(sig, args): 366 reflection_data = reflection_data + ' <arg type="%s" name="%s" />\n' % pair 367 reflection_data = reflection_data + ' </signal>\n' 368 369 return reflection_data
370
371 -class Interface(object):
372 __metaclass__ = InterfaceType
373 374 #: A unique object used as the value of Object._object_path and 375 #: Object._connection if it's actually in more than one place 376 _MANY = object()
377 378 -class Object(Interface):
379 r"""A base class for exporting your own Objects across the Bus. 380 381 Just inherit from Object and mark exported methods with the 382 @\ `dbus.service.method` or @\ `dbus.service.signal` decorator. 383 384 Example:: 385 386 class Example(dbus.service.object): 387 def __init__(self, object_path): 388 dbus.service.Object.__init__(self, dbus.SessionBus(), path) 389 self._last_input = None 390 391 @dbus.service.method(interface='com.example.Sample', 392 in_signature='v', out_signature='s') 393 def StringifyVariant(self, var): 394 self.LastInputChanged(var) # emits the signal 395 return str(var) 396 397 @dbus.service.signal(interface='com.example.Sample', 398 signature='v') 399 def LastInputChanged(self, var): 400 # run just before the signal is actually emitted 401 # just put "pass" if nothing should happen 402 self._last_input = var 403 404 @dbus.service.method(interface='com.example.Sample', 405 in_signature='', out_signature='v') 406 def GetLastInput(self): 407 return self._last_input 408 """ 409 410 #: If True, this object can be made available at more than one object path. 411 #: If True but `SUPPORTS_MULTIPLE_CONNECTIONS` is False, the object may 412 #: handle more than one object path, but they must all be on the same 413 #: connection. 414 SUPPORTS_MULTIPLE_OBJECT_PATHS = False 415 416 #: If True, this object can be made available on more than one connection. 417 #: If True but `SUPPORTS_MULTIPLE_OBJECT_PATHS` is False, the object must 418 #: have the same object path on all its connections. 419 SUPPORTS_MULTIPLE_CONNECTIONS = False 420
421 - def __init__(self, conn=None, object_path=None, bus_name=None):
422 """Constructor. Either conn or bus_name is required; object_path 423 is also required. 424 425 :Parameters: 426 `conn` : dbus.connection.Connection or None 427 The connection on which to export this object. 428 429 If None, use the Bus associated with the given ``bus_name``. 430 If there is no ``bus_name`` either, the object is not 431 initially available on any Connection. 432 433 For backwards compatibility, if an instance of 434 dbus.service.BusName is passed as the first parameter, 435 this is equivalent to passing its associated Bus as 436 ``conn``, and passing the BusName itself as ``bus_name``. 437 438 `object_path` : str or None 439 A D-Bus object path at which to make this Object available 440 immediately. If this is not None, a `conn` or `bus_name` must 441 also be provided. 442 443 `bus_name` : dbus.service.BusName or None 444 Represents a well-known name claimed by this process. A 445 reference to the BusName object will be held by this 446 Object, preventing the name from being released during this 447 Object's lifetime (unless it's released manually). 448 """ 449 if object_path is not None: 450 validate_object_path(object_path) 451 452 if isinstance(conn, BusName): 453 # someone's using the old API; don't gratuitously break them 454 bus_name = conn 455 conn = bus_name.get_bus() 456 elif conn is None: 457 if bus_name is not None: 458 # someone's using the old API but naming arguments, probably 459 conn = bus_name.get_bus() 460 461 #: Either an object path, None or _MANY 462 self._object_path = None 463 #: Either a dbus.connection.Connection, None or _MANY 464 self._connection = None 465 #: A list of tuples (Connection, object path, False) where the False 466 #: is for future expansion (to support fallback paths) 467 self._locations = [] 468 #: Lock protecting `_locations`, `_connection` and `_object_path` 469 self._locations_lock = thread.allocate_lock() 470 471 #: True if this is a fallback object handling a whole subtree. 472 self._fallback = False 473 474 self._name = bus_name 475 476 if conn is None and object_path is not None: 477 raise TypeError('If object_path is given, either conn or bus_name ' 478 'is required') 479 if conn is not None and object_path is not None: 480 self.add_to_connection(conn, object_path)
481 482 @property
483 - def __dbus_object_path__(self):
484 """The object-path at which this object is available. 485 Access raises AttributeError if there is no object path, or more than 486 one object path. 487 488 Changed in 0.82.0: AttributeError can be raised. 489 """ 490 if self._object_path is _MANY: 491 raise AttributeError('Object %r has more than one object path: ' 492 'use Object.locations instead' % self) 493 elif self._object_path is None: 494 raise AttributeError('Object %r has no object path yet' % self) 495 else: 496 return self._object_path
497 498 @property
499 - def connection(self):
500 """The Connection on which this object is available. 501 Access raises AttributeError if there is no Connection, or more than 502 one Connection. 503 504 Changed in 0.82.0: AttributeError can be raised. 505 """ 506 if self._connection is _MANY: 507 raise AttributeError('Object %r is on more than one Connection: ' 508 'use Object.locations instead' % self) 509 elif self._connection is None: 510 raise AttributeError('Object %r has no Connection yet' % self) 511 else: 512 return self._connection
513 514 @property
515 - def locations(self):
516 """An iterable over tuples representing locations at which this 517 object is available. 518 519 Each tuple has at least two items, but may have more in future 520 versions of dbus-python, so do not rely on their exact length. 521 The first two items are the dbus.connection.Connection and the object 522 path. 523 524 :Since: 0.82.0 525 """ 526 return iter(self._locations)
527
528 - def add_to_connection(self, connection, path):
529 """Make this object accessible via the given D-Bus connection and 530 object path. 531 532 :Parameters: 533 `connection` : dbus.connection.Connection 534 Export the object on this connection. If the class attribute 535 SUPPORTS_MULTIPLE_CONNECTIONS is False (default), this object 536 can only be made available on one connection; if the class 537 attribute is set True by a subclass, the object can be made 538 available on more than one connection. 539 540 `path` : dbus.ObjectPath or other str 541 Place the object at this object path. If the class attribute 542 SUPPORTS_MULTIPLE_OBJECT_PATHS is False (default), this object 543 can only be made available at one object path; if the class 544 attribute is set True by a subclass, the object can be made 545 available with more than one object path. 546 547 :Raises ValueError: if the object's class attributes do not allow the 548 object to be exported in the desired way. 549 :Since: 0.82.0 550 """ 551 if path == LOCAL_PATH: 552 raise ValueError('Objects may not be exported on the reserved ' 553 'path %s' % LOCAL_PATH) 554 555 self._locations_lock.acquire() 556 try: 557 if (self._connection is not None and 558 self._connection is not connection and 559 not self.SUPPORTS_MULTIPLE_CONNECTIONS): 560 raise ValueError('%r is already exported on ' 561 'connection %r' % (self, self._connection)) 562 563 if (self._object_path is not None and 564 not self.SUPPORTS_MULTIPLE_OBJECT_PATHS and 565 self._object_path != path): 566 raise ValueError('%r is already exported at object ' 567 'path %s' % (self, self._object_path)) 568 569 connection._register_object_path(path, self._message_cb, 570 self._unregister_cb, 571 self._fallback) 572 573 if self._connection is None: 574 self._connection = connection 575 elif self._connection is not connection: 576 self._connection = _MANY 577 578 if self._object_path is None: 579 self._object_path = path 580 elif self._object_path != path: 581 self._object_path = _MANY 582 583 self._locations.append((connection, path, self._fallback)) 584 finally: 585 self._locations_lock.release()
586
587 - def remove_from_connection(self, connection=None, path=None):
588 """Make this object inaccessible via the given D-Bus connection 589 and object path. If no connection or path is specified, 590 the object ceases to be accessible via any connection or path. 591 592 :Parameters: 593 `connection` : dbus.connection.Connection or None 594 Only remove the object from this Connection. If None, 595 remove from all Connections on which it's exported. 596 `path` : dbus.ObjectPath or other str, or None 597 Only remove the object from this object path. If None, 598 remove from all object paths. 599 :Raises LookupError: 600 if the object was not exported on the requested connection 601 or path, or (if both are None) was not exported at all. 602 :Since: 0.81.1 603 """ 604 self._locations_lock.acquire() 605 try: 606 if self._object_path is None or self._connection is None: 607 raise LookupError('%r is not exported' % self) 608 609 if connection is not None or path is not None: 610 dropped = [] 611 for location in self._locations: 612 if ((connection is None or location[0] is connection) and 613 (path is None or location[1] == path)): 614 dropped.append(location) 615 else: 616 dropped = self._locations 617 self._locations = [] 618 619 if not dropped: 620 raise LookupError('%r is not exported at a location matching ' 621 '(%r,%r)' % (self, connection, path)) 622 623 for location in dropped: 624 try: 625 location[0]._unregister_object_path(location[1]) 626 except LookupError: 627 pass 628 if self._locations: 629 try: 630 self._locations.remove(location) 631 except ValueError: 632 pass 633 finally: 634 self._locations_lock.release()
635
636 - def _unregister_cb(self, connection):
637 # there's not really enough information to do anything useful here 638 _logger.info('Unregistering exported object %r from some path ' 639 'on %r', self, connection)
640
641 - def _message_cb(self, connection, message):
642 if not isinstance(message, MethodCallMessage): 643 return 644 645 try: 646 # lookup candidate method and parent method 647 method_name = message.get_member() 648 interface_name = message.get_interface() 649 (candidate_method, parent_method) = _method_lookup(self, method_name, interface_name) 650 651 # set up method call parameters 652 args = message.get_args_list(**parent_method._dbus_get_args_options) 653 keywords = {} 654 655 if parent_method._dbus_out_signature is not None: 656 signature = Signature(parent_method._dbus_out_signature) 657 else: 658 signature = None 659 660 # set up async callback functions 661 if parent_method._dbus_async_callbacks: 662 (return_callback, error_callback) = parent_method._dbus_async_callbacks 663 keywords[return_callback] = lambda *retval: _method_reply_return(connection, message, method_name, signature, *retval) 664 keywords[error_callback] = lambda exception: _method_reply_error(connection, message, exception) 665 666 # include the sender etc. if desired 667 if parent_method._dbus_sender_keyword: 668 keywords[parent_method._dbus_sender_keyword] = message.get_sender() 669 if parent_method._dbus_path_keyword: 670 keywords[parent_method._dbus_path_keyword] = message.get_path() 671 if parent_method._dbus_rel_path_keyword: 672 path = message.get_path() 673 rel_path = path 674 for exp in self._locations: 675 # pathological case: if we're exported in two places, 676 # one of which is a subtree of the other, then pick the 677 # subtree by preference (i.e. minimize the length of 678 # rel_path) 679 if exp[0] is connection: 680 if path == exp[1]: 681 rel_path = '/' 682 break 683 if exp[1] == '/': 684 # we already have rel_path == path at the beginning 685 continue 686 if path.startswith(exp[1] + '/'): 687 # yes we're in this exported subtree 688 suffix = path[len(exp[1]):] 689 if len(suffix) < len(rel_path): 690 rel_path = suffix 691 rel_path = ObjectPath(rel_path) 692 keywords[parent_method._dbus_rel_path_keyword] = rel_path 693 694 if parent_method._dbus_destination_keyword: 695 keywords[parent_method._dbus_destination_keyword] = message.get_destination() 696 if parent_method._dbus_message_keyword: 697 keywords[parent_method._dbus_message_keyword] = message 698 if parent_method._dbus_connection_keyword: 699 keywords[parent_method._dbus_connection_keyword] = connection 700 701 # call method 702 retval = candidate_method(self, *args, **keywords) 703 704 # we're done - the method has got callback functions to reply with 705 if parent_method._dbus_async_callbacks: 706 return 707 708 # otherwise we send the return values in a reply. if we have a 709 # signature, use it to turn the return value into a tuple as 710 # appropriate 711 if signature is not None: 712 signature_tuple = tuple(signature) 713 # if we have zero or one return values we want make a tuple 714 # for the _method_reply_return function, otherwise we need 715 # to check we're passing it a sequence 716 if len(signature_tuple) == 0: 717 if retval == None: 718 retval = () 719 else: 720 raise TypeError('%s has an empty output signature but did not return None' % 721 method_name) 722 elif len(signature_tuple) == 1: 723 retval = (retval,) 724 else: 725 if operator.isSequenceType(retval): 726 # multi-value signature, multi-value return... proceed unchanged 727 pass 728 else: 729 raise TypeError('%s has multiple output values in signature %s but did not return a sequence' % 730 (method_name, signature)) 731 732 # no signature, so just turn the return into a tuple and send it as normal 733 else: 734 if retval is None: 735 retval = () 736 elif (isinstance(retval, tuple) 737 and not isinstance(retval, Struct)): 738 # If the return is a tuple that is not a Struct, we use it 739 # as-is on the assumption that there are multiple return 740 # values - this is the usual Python idiom. (fd.o #10174) 741 pass 742 else: 743 retval = (retval,) 744 745 _method_reply_return(connection, message, method_name, signature, *retval) 746 except Exception, exception: 747 # send error reply 748 _method_reply_error(connection, message, exception)
749 750 @method(INTROSPECTABLE_IFACE, in_signature='', out_signature='s', 751 path_keyword='object_path', connection_keyword='connection')
752 - def Introspect(self, object_path, connection):
753 """Return a string of XML encoding this object's supported interfaces, 754 methods and signals. 755 """ 756 reflection_data = _dbus_bindings.DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE 757 reflection_data += '<node name="%s">\n' % object_path 758 759 interfaces = self._dbus_class_table[self.__class__.__module__ + '.' + self.__class__.__name__] 760 for (name, funcs) in interfaces.iteritems(): 761 reflection_data += ' <interface name="%s">\n' % (name) 762 763 for func in funcs.values(): 764 if getattr(func, '_dbus_is_method', False): 765 reflection_data += self.__class__._reflect_on_method(func) 766 elif getattr(func, '_dbus_is_signal', False): 767 reflection_data += self.__class__._reflect_on_signal(func) 768 769 reflection_data += ' </interface>\n' 770 771 for name in connection.list_exported_child_objects(object_path): 772 reflection_data += ' <node name="%s"/>\n' % name 773 774 reflection_data += '</node>\n' 775 776 return reflection_data
777
778 - def __repr__(self):
779 where = '' 780 if (self._object_path is not _MANY 781 and self._object_path is not None): 782 where = ' at %s' % self._object_path 783 return '<%s.%s%s at %#x>' % (self.__class__.__module__, 784 self.__class__.__name__, where, 785 id(self))
786 __str__ = __repr__
787
788 -class FallbackObject(Object):
789 """An object that implements an entire subtree of the object-path 790 tree. 791 792 :Since: 0.82.0 793 """ 794 795 SUPPORTS_MULTIPLE_OBJECT_PATHS = True 796
797 - def __init__(self, conn=None, object_path=None):
798 """Constructor. 799 800 Note that the superclass' ``bus_name`` __init__ argument is not 801 supported here. 802 803 :Parameters: 804 `conn` : dbus.connection.Connection or None 805 The connection on which to export this object. If this is not 806 None, an `object_path` must also be provided. 807 808 If None, the object is not initially available on any 809 Connection. 810 811 `object_path` : str or None 812 A D-Bus object path at which to make this Object available 813 immediately. If this is not None, a `conn` must also be 814 provided. 815 816 This object will implements all object-paths in the subtree 817 starting at this object-path, except where a more specific 818 object has been added. 819 """ 820 super(FallbackObject, self).__init__() 821 self._fallback = True 822 823 if conn is None: 824 if object_path is not None: 825 raise TypeError('If object_path is given, conn is required') 826 elif object_path is None: 827 raise TypeError('If conn is given, object_path is required') 828 else: 829 self.add_to_connection(conn, object_path)
830