374
382
return self._last_input
377
# the signature of __init__ is a bit mad, for backwards compatibility
385
#: If True, this object can be made available at more than one object path.
386
#: If True but `SUPPORTS_MULTIPLE_CONNECTIONS` is False, the object may
387
#: handle more than one object path, but they must all be on the same
389
SUPPORTS_MULTIPLE_OBJECT_PATHS = False
391
#: If True, this object can be made available on more than one connection.
392
#: If True but `SUPPORTS_MULTIPLE_OBJECT_PATHS` is False, the object must
393
#: have the same object path on all its connections.
394
SUPPORTS_MULTIPLE_CONNECTIONS = False
378
396
def __init__(self, conn=None, object_path=None, bus_name=None):
379
397
"""Constructor. Either conn or bus_name is required; object_path
380
398
is also required.
383
`conn` : dbus.connection.Connection
401
`conn` : dbus.connection.Connection or None
384
402
The connection on which to export this object.
386
If None, use the Bus associated with the given ``bus_name``,
387
or raise TypeError if there is no ``bus_name`` either.
404
If None, use the Bus associated with the given ``bus_name``.
405
If there is no ``bus_name`` either, the object is not
406
initially available on any Connection.
389
408
For backwards compatibility, if an instance of
390
409
dbus.service.BusName is passed as the first parameter,
391
410
this is equivalent to passing its associated Bus as
392
411
``conn``, and passing the BusName itself as ``bus_name``.
395
The D-Bus object path at which to export this Object.
413
`object_path` : str or None
414
A D-Bus object path at which to make this Object available
415
immediately. If this is not None, a `conn` or `bus_name` must
397
`bus_name` : dbus.service.BusName
418
`bus_name` : dbus.service.BusName or None
398
419
Represents a well-known name claimed by this process. A
399
420
reference to the BusName object will be held by this
400
421
Object, preventing the name from being released during this
401
422
Object's lifetime (unless it's released manually).
403
if object_path is None:
404
raise TypeError('The object_path argument is required')
405
_dbus_bindings.validate_object_path(object_path)
406
if object_path == LOCAL_PATH:
407
raise DBusException('Objects may not be exported on the reserved '
408
'path %s' % LOCAL_PATH)
424
if object_path is not None:
425
_dbus_bindings.validate_object_path(object_path)
410
427
if isinstance(conn, BusName):
411
428
# someone's using the old API; don't gratuitously break them
413
430
conn = bus_name.get_bus()
414
431
elif conn is None:
415
# someone's using the old API but naming arguments, probably
417
raise TypeError('Either conn or bus_name is required')
418
conn = bus_name.get_bus()
420
self._object_path = object_path
432
if bus_name is not None:
433
# someone's using the old API but naming arguments, probably
434
conn = bus_name.get_bus()
436
#: Either an object path, None or _MANY
437
self._object_path = None
438
#: Either a dbus.connection.Connection, None or _MANY
439
self._connection = None
440
#: A list of tuples (Connection, object path, False) where the False
441
#: is for future expansion (to support fallback paths)
443
#: Lock protecting `_locations`, `_connection` and `_object_path`
444
self._locations_lock = thread.allocate_lock()
446
#: True if this is a fallback object handling a whole subtree.
447
self._fallback = False
421
449
self._name = bus_name
422
self._connection = conn
424
self._connection._register_object_path(object_path, self._message_cb, self._unregister_cb)
426
__dbus_object_path__ = property(lambda self: self._object_path, None, None,
427
"The D-Bus object path of this object")
451
if conn is None and object_path is not None:
452
raise TypeError('If object_path is given, either conn or bus_name '
454
if conn is not None and object_path is not None:
455
self.add_to_connection(conn, object_path)
458
def __dbus_object_path__(self):
459
"""The object-path at which this object is available.
460
Access raises AttributeError if there is no object path, or more than
463
Changed in 0.82.0: AttributeError can be raised.
465
if self._object_path is _MANY:
466
raise AttributeError('Object %r has more than one object path: '
467
'use Object.locations instead' % self)
468
elif self._object_path is None:
469
raise AttributeError('Object %r has no object path yet' % self)
471
return self._object_path
474
def connection(self):
475
"""The Connection on which this object is available.
476
Access raises AttributeError if there is no Connection, or more than
479
Changed in 0.82.0: AttributeError can be raised.
481
if self._connection is _MANY:
482
raise AttributeError('Object %r is on more than one Connection: '
483
'use Object.locations instead' % self)
484
elif self._connection is None:
485
raise AttributeError('Object %r has no Connection yet' % self)
487
return self._connection
491
"""An iterable over tuples representing locations at which this
494
Each tuple has at least two items, but may have more in future
495
versions of dbus-python, so do not rely on their exact length.
496
The first two items are the dbus.connection.Connection and the object
501
return iter(self._locations)
503
def add_to_connection(self, connection, path):
504
"""Make this object accessible via the given D-Bus connection and
508
`connection` : dbus.connection.Connection
509
Export the object on this connection. If the class attribute
510
SUPPORTS_MULTIPLE_CONNECTIONS is False (default), this object
511
can only be made available on one connection; if the class
512
attribute is set True by a subclass, the object can be made
513
available on more than one connection.
515
`path` : dbus.ObjectPath or other str
516
Place the object at this object path. If the class attribute
517
SUPPORTS_MULTIPLE_OBJECT_PATHS is False (default), this object
518
can only be made available at one object path; if the class
519
attribute is set True by a subclass, the object can be made
520
available with more than one object path.
522
:Raises ValueError: if the object's class attributes do not allow the
523
object to be exported in the desired way.
526
if path == LOCAL_PATH:
527
raise ValueError('Objects may not be exported on the reserved '
528
'path %s' % LOCAL_PATH)
530
self._locations_lock.acquire()
532
if (self._connection is not None and
533
self._connection is not connection and
534
not self.SUPPORTS_MULTIPLE_CONNECTIONS):
535
raise ValueError('%r is already exported on '
536
'connection %r' % (self, self._connection))
538
if (self._object_path is not None and
539
not self.SUPPORTS_MULTIPLE_OBJECT_PATHS and
540
self._object_path != path):
541
raise ValueError('%r is already exported at object '
542
'path %s' % (self, self._object_path))
544
connection._register_object_path(path, self._message_cb,
548
if self._connection is None:
549
self._connection = connection
550
elif self._connection is not connection:
551
self._connection = _MANY
553
if self._object_path is None:
554
self._object_path = path
555
elif self._object_path != path:
556
self._object_path = _MANY
558
self._locations.append((connection, path, self._fallback))
560
self._locations_lock.release()
429
562
def remove_from_connection(self, connection=None, path=None):
430
563
"""Make this object inaccessible via the given D-Bus connection
445
578
:Raises LookupError:
446
579
if the object was not exported on the requested connection
447
580
or path, or (if both are None) was not exported at all.
449
if self._object_path is None or self._connection is None:
450
raise LookupError('%r is not exported' % self)
451
if path is not None and self._object_path != path:
452
raise LookupError('%r is not exported at path %r' % (self, path))
453
if connection is not None and self._connection != connection:
454
raise LookupError('%r is not exported on %r' % (self, connection))
583
self._locations_lock.acquire()
457
self._connection._unregister_object_path(self._object_path)
585
if self._object_path is None or self._connection is None:
586
raise LookupError('%r is not exported' % self)
588
if connection is not None or path is not None:
590
for location in self._locations:
591
if ((connection is None or location[0] is connection) and
592
(path is None or location[1] == path)):
593
dropped.append(location)
595
dropped = self._locations
599
raise LookupError('%r is not exported at a location matching '
600
'(%r,%r)' % (self, connection, path))
602
for location in dropped:
604
location[0]._unregister_object_path(location[1])
609
self._locations.remove(location)
459
self._connection = None
460
self._object_path = None
613
self._locations_lock.release()
462
615
def _unregister_cb(self, connection):
463
_logger.info('Unregistering exported object %r', self)
616
# there's not really enough information to do anything useful here
617
_logger.info('Unregistering exported object %r from some path '
618
'on %r', self, connection)
465
620
def _message_cb(self, connection, message):
572
729
return reflection_data
574
731
def __repr__(self):
575
return '<dbus.service.Object %s on %r at %#x>' % (self._object_path, self._name, id(self))
733
if (self._object_path is not _MANY
734
and self._object_path is not None):
735
where = ' at %s' % self._object_path
736
return '<%s.%s%s at %#x>' % (self.__class__.__module__,
737
self.__class__.__name__, where,
576
739
__str__ = __repr__
741
class FallbackObject(Object):
742
"""An object that implements an entire subtree of the object-path
748
SUPPORTS_MULTIPLE_OBJECT_PATHS = True
750
def __init__(self, conn=None, object_path=None):
753
Note that the superclass' ``bus_name`` __init__ argument is not
757
`conn` : dbus.connection.Connection or None
758
The connection on which to export this object. If this is not
759
None, an `object_path` must also be provided.
761
If None, the object is not initially available on any
764
`object_path` : str or None
765
A D-Bus object path at which to make this Object available
766
immediately. If this is not None, a `conn` must also be
769
This object will implements all object-paths in the subtree
770
starting at this object-path, except where a more specific
771
object has been added.
773
super(FallbackObject, self).__init__()
774
self._fallback = True
777
if object_path is not None:
778
raise TypeError('If object_path is given, conn is required')
779
elif object_path is None:
780
raise TypeError('If conn is given, object_path is required')
782
self.add_to_connection(conn, object_path)