~barry/ubuntu/precise/dbus-python/python3-from-upstream

« back to all changes in this revision

Viewing changes to dbus/service.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-06-26 10:28:30 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20070626102830-6rh3gy4nvtt1c5z2
Tags: 0.82.0-1ubuntu1
* Merge with Debian; remaining changes:
  - Build a python-dbus-dbg package.
  - debian/rules:
    - Don't remove intermediate stamp files.
    - Don't use optimization for the debug build.
  - debian/python-dbus.postinst:
    - Also remove stale .py[co] files from 2.5 python directories

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
import logging
27
27
import operator
28
28
import traceback
 
29
try:
 
30
    import thread
 
31
except ImportError:
 
32
    import dummy_thread as thread
29
33
 
30
34
import _dbus_bindings
31
35
from dbus import SessionBus
342
346
class Interface(object):
343
347
    __metaclass__ = InterfaceType
344
348
 
 
349
#: A unique object used as the value of Object._object_path and
 
350
#: Object._connection if it's actually in more than one place
 
351
_MANY = object()
 
352
 
345
353
class Object(Interface):
346
354
    r"""A base class for exporting your own Objects across the Bus.
347
355
 
374
382
                return self._last_input
375
383
    """
376
384
 
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
 
388
    #: connection.
 
389
    SUPPORTS_MULTIPLE_OBJECT_PATHS = False
 
390
 
 
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
 
395
 
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.
381
399
 
382
400
        :Parameters:
383
 
            `conn` : dbus.connection.Connection
 
401
            `conn` : dbus.connection.Connection or None
384
402
                The connection on which to export this object.
385
403
 
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.
388
407
 
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``.
393
412
 
394
 
            `object_path` : str
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
 
416
                also be provided.
396
417
 
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).
402
423
        """
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)
409
426
 
410
427
        if isinstance(conn, BusName):
411
428
            # someone's using the old API; don't gratuitously break them
412
429
            bus_name = conn
413
430
            conn = bus_name.get_bus()
414
431
        elif conn is None:
415
 
            # someone's using the old API but naming arguments, probably
416
 
            if bus_name is None:
417
 
                raise TypeError('Either conn or bus_name is required')
418
 
            conn = bus_name.get_bus()
419
 
 
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()
 
435
 
 
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)
 
442
        self._locations = []
 
443
        #: Lock protecting `_locations`, `_connection` and `_object_path`
 
444
        self._locations_lock = thread.allocate_lock()
 
445
 
 
446
        #: True if this is a fallback object handling a whole subtree.
 
447
        self._fallback = False
 
448
 
421
449
        self._name = bus_name
422
 
        self._connection = conn
423
 
 
424
 
        self._connection._register_object_path(object_path, self._message_cb, self._unregister_cb)
425
 
 
426
 
    __dbus_object_path__ = property(lambda self: self._object_path, None, None,
427
 
                                    "The D-Bus object path of this object")
 
450
 
 
451
        if conn is None and object_path is not None:
 
452
            raise TypeError('If object_path is given, either conn or bus_name '
 
453
                            'is required')
 
454
        if conn is not None and object_path is not None:
 
455
            self.add_to_connection(conn, object_path)
 
456
 
 
457
    @property
 
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
 
461
        one object path.
 
462
 
 
463
        Changed in 0.82.0: AttributeError can be raised.
 
464
        """
 
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)
 
470
        else:
 
471
            return self._object_path
 
472
 
 
473
    @property
 
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
 
477
        one Connection.
 
478
 
 
479
        Changed in 0.82.0: AttributeError can be raised.
 
480
        """
 
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)
 
486
        else:
 
487
            return self._connection
 
488
 
 
489
    @property
 
490
    def locations(self):
 
491
        """An iterable over tuples representing locations at which this
 
492
        object is available.
 
493
 
 
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
 
497
        path.
 
498
 
 
499
        :Since: 0.82.0
 
500
        """
 
501
        return iter(self._locations)
 
502
 
 
503
    def add_to_connection(self, connection, path):
 
504
        """Make this object accessible via the given D-Bus connection and
 
505
        object path.
 
506
 
 
507
        :Parameters:
 
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.
 
514
 
 
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.
 
521
 
 
522
        :Raises ValueError: if the object's class attributes do not allow the
 
523
            object to be exported in the desired way.
 
524
        :Since: 0.82.0
 
525
        """
 
526
        if path == LOCAL_PATH:
 
527
            raise ValueError('Objects may not be exported on the reserved '
 
528
                             'path %s' % LOCAL_PATH)
 
529
 
 
530
        self._locations_lock.acquire()
 
531
        try:
 
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))
 
537
 
 
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))
 
543
 
 
544
            connection._register_object_path(path, self._message_cb,
 
545
                                             self._unregister_cb,
 
546
                                             self._fallback)
 
547
 
 
548
            if self._connection is None:
 
549
                self._connection = connection
 
550
            elif self._connection is not connection:
 
551
                self._connection = _MANY
 
552
 
 
553
            if self._object_path is None:
 
554
                self._object_path = path
 
555
            elif self._object_path != path:
 
556
                self._object_path = _MANY
 
557
 
 
558
            self._locations.append((connection, path, self._fallback))
 
559
        finally:
 
560
            self._locations_lock.release()
428
561
 
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.
 
581
        :Since: 0.81.1
448
582
        """
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))
455
 
 
 
583
        self._locations_lock.acquire()
456
584
        try:
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)
 
587
 
 
588
            if connection is not None or path is not None:
 
589
                dropped = []
 
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)
 
594
            else:
 
595
                dropped = self._locations
 
596
                self._locations = []
 
597
 
 
598
            if not dropped:
 
599
                raise LookupError('%r is not exported at a location matching '
 
600
                                  '(%r,%r)' % (self, connection, path))
 
601
 
 
602
            for location in dropped:
 
603
                try:
 
604
                    location[0]._unregister_object_path(location[1])
 
605
                except LookupError:
 
606
                    pass
 
607
                if self._locations:
 
608
                    try:
 
609
                        self._locations.remove(location)
 
610
                    except ValueError:
 
611
                        pass
458
612
        finally:
459
 
            self._connection = None
460
 
            self._object_path = None
 
613
            self._locations_lock.release()
461
614
 
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)
464
619
 
465
620
    def _message_cb(self, connection, message):
466
621
        try:
493
648
                keywords[parent_method._dbus_destination_keyword] = message.get_destination()
494
649
            if parent_method._dbus_message_keyword:
495
650
                keywords[parent_method._dbus_message_keyword] = message
 
651
            if parent_method._dbus_connection_keyword:
 
652
                keywords[parent_method._dbus_connection_keyword] = connection
496
653
 
497
654
            # call method
498
655
            retval = candidate_method(self, *args, **keywords)
572
729
        return reflection_data
573
730
 
574
731
    def __repr__(self):
575
 
        return '<dbus.service.Object %s on %r at %#x>' % (self._object_path, self._name, id(self))
 
732
        where = ''
 
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,
 
738
                                   id(self))
576
739
    __str__ = __repr__
577
740
 
 
741
class FallbackObject(Object):
 
742
    """An object that implements an entire subtree of the object-path
 
743
    tree.
 
744
 
 
745
    :Since: 0.82.0
 
746
    """
 
747
 
 
748
    SUPPORTS_MULTIPLE_OBJECT_PATHS = True
 
749
 
 
750
    def __init__(self, conn=None, object_path=None):
 
751
        """Constructor.
 
752
 
 
753
        Note that the superclass' ``bus_name`` __init__ argument is not
 
754
        supported here.
 
755
 
 
756
        :Parameters:
 
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.
 
760
 
 
761
                If None, the object is not initially available on any
 
762
                Connection.
 
763
 
 
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
 
767
                provided.
 
768
 
 
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.
 
772
        """
 
773
        super(FallbackObject, self).__init__()
 
774
        self._fallback = True
 
775
 
 
776
        if conn is None:
 
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')
 
781
        else:
 
782
            self.add_to_connection(conn, object_path)