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