Blame dbus/proxies.py

Packit 130fc8
# Copyright (C) 2003-2007 Red Hat Inc. <http://www.redhat.com/>
Packit 130fc8
# Copyright (C) 2003 David Zeuthen
Packit 130fc8
# Copyright (C) 2004 Rob Taylor
Packit 130fc8
# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
Packit 130fc8
#
Packit 130fc8
# Permission is hereby granted, free of charge, to any person
Packit 130fc8
# obtaining a copy of this software and associated documentation
Packit 130fc8
# files (the "Software"), to deal in the Software without
Packit 130fc8
# restriction, including without limitation the rights to use, copy,
Packit 130fc8
# modify, merge, publish, distribute, sublicense, and/or sell copies
Packit 130fc8
# of the Software, and to permit persons to whom the Software is
Packit 130fc8
# furnished to do so, subject to the following conditions:
Packit 130fc8
#
Packit 130fc8
# The above copyright notice and this permission notice shall be
Packit 130fc8
# included in all copies or substantial portions of the Software.
Packit 130fc8
#
Packit 130fc8
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Packit 130fc8
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Packit 130fc8
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Packit 130fc8
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
Packit 130fc8
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
Packit 130fc8
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
Packit 130fc8
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
Packit 130fc8
# DEALINGS IN THE SOFTWARE.
Packit 130fc8
Packit 130fc8
import logging
Packit 130fc8
Packit 130fc8
try:
Packit 130fc8
    from threading import RLock
Packit 130fc8
except ImportError:
Packit 130fc8
    from dummy_threading import RLock
Packit 130fc8
Packit 130fc8
import _dbus_bindings
Packit 130fc8
from dbus._expat_introspect_parser import process_introspection_data
Packit 130fc8
from dbus.exceptions import (
Packit 130fc8
    DBusException, IntrospectionParserException, MissingErrorHandlerException,
Packit 130fc8
    MissingReplyHandlerException)
Packit 130fc8
Packit 130fc8
__docformat__ = 'restructuredtext'
Packit 130fc8
Packit 130fc8
Packit 130fc8
_logger = logging.getLogger('dbus.proxies')
Packit 130fc8
Packit 130fc8
from _dbus_bindings import (
Packit 130fc8
    BUS_DAEMON_IFACE, BUS_DAEMON_NAME, BUS_DAEMON_PATH, INTROSPECTABLE_IFACE,
Packit 130fc8
    LOCAL_PATH)
Packit 130fc8
from dbus._compat import is_py2
Packit 130fc8
Packit 130fc8
Packit 130fc8
class _DeferredMethod:
Packit 130fc8
    """A proxy method which will only get called once we have its
Packit 130fc8
    introspection reply.
Packit 130fc8
    """
Packit 130fc8
    def __init__(self, proxy_method, append, block):
Packit 130fc8
        self._proxy_method = proxy_method
Packit 130fc8
        # the test suite relies on the existence of this property
Packit 130fc8
        self._method_name = proxy_method._method_name
Packit 130fc8
        self._append = append
Packit 130fc8
        self._block = block
Packit 130fc8
Packit 130fc8
    def __call__(self, *args, **keywords):
Packit 130fc8
        if ('reply_handler' in keywords or
Packit 130fc8
            keywords.get('ignore_reply', False)):
Packit 130fc8
            # defer the async call til introspection finishes
Packit 130fc8
            self._append(self._proxy_method, args, keywords)
Packit 130fc8
            return None
Packit 130fc8
        else:
Packit 130fc8
            # we're being synchronous, so block
Packit 130fc8
            self._block()
Packit 130fc8
            return self._proxy_method(*args, **keywords)
Packit 130fc8
Packit 130fc8
    def call_async(self, *args, **keywords):
Packit 130fc8
        self._append(self._proxy_method, args, keywords)
Packit 130fc8
Packit 130fc8
Packit 130fc8
class _ProxyMethod:
Packit 130fc8
    """A proxy method.
Packit 130fc8
Packit 130fc8
    Typically a member of a ProxyObject. Calls to the
Packit 130fc8
    method produce messages that travel over the Bus and are routed
Packit 130fc8
    to a specific named Service.
Packit 130fc8
    """
Packit 130fc8
    def __init__(self, proxy, connection, bus_name, object_path, method_name,
Packit 130fc8
                 iface):
Packit 130fc8
        if object_path == LOCAL_PATH:
Packit 130fc8
            raise DBusException('Methods may not be called on the reserved '
Packit 130fc8
                                'path %s' % LOCAL_PATH)
Packit 130fc8
Packit 130fc8
        # trust that the proxy, and the properties it had, are OK
Packit 130fc8
        self._proxy          = proxy
Packit 130fc8
        self._connection     = connection
Packit 130fc8
        self._named_service  = bus_name
Packit 130fc8
        self._object_path    = object_path
Packit 130fc8
        # fail early if the method name is bad
Packit 130fc8
        _dbus_bindings.validate_member_name(method_name)
Packit 130fc8
        # the test suite relies on the existence of this property
Packit 130fc8
        self._method_name    = method_name
Packit 130fc8
        # fail early if the interface name is bad
Packit 130fc8
        if iface is not None:
Packit 130fc8
            _dbus_bindings.validate_interface_name(iface)
Packit 130fc8
        self._dbus_interface = iface
Packit 130fc8
Packit 130fc8
    def __call__(self, *args, **keywords):
Packit 130fc8
        reply_handler = keywords.pop('reply_handler', None)
Packit 130fc8
        error_handler = keywords.pop('error_handler', None)
Packit 130fc8
        ignore_reply = keywords.pop('ignore_reply', False)
Packit 130fc8
        signature = keywords.pop('signature', None)
Packit 130fc8
Packit 130fc8
        if reply_handler is not None or error_handler is not None:
Packit 130fc8
            if reply_handler is None:
Packit 130fc8
                raise MissingReplyHandlerException()
Packit 130fc8
            elif error_handler is None:
Packit 130fc8
                raise MissingErrorHandlerException()
Packit 130fc8
            elif ignore_reply:
Packit 130fc8
                raise TypeError('ignore_reply and reply_handler cannot be '
Packit 130fc8
                                'used together')
Packit 130fc8
Packit 130fc8
        dbus_interface = keywords.pop('dbus_interface', self._dbus_interface)
Packit 130fc8
Packit 130fc8
        if signature is None:
Packit 130fc8
            if dbus_interface is None:
Packit 130fc8
                key = self._method_name
Packit 130fc8
            else:
Packit 130fc8
                key = dbus_interface + '.' + self._method_name
Packit 130fc8
Packit 130fc8
            signature = self._proxy._introspect_method_map.get(key, None)
Packit 130fc8
Packit 130fc8
        if ignore_reply or reply_handler is not None:
Packit 130fc8
            self._connection.call_async(self._named_service,
Packit 130fc8
                                        self._object_path,
Packit 130fc8
                                        dbus_interface,
Packit 130fc8
                                        self._method_name,
Packit 130fc8
                                        signature,
Packit 130fc8
                                        args,
Packit 130fc8
                                        reply_handler,
Packit 130fc8
                                        error_handler,
Packit 130fc8
                                        **keywords)
Packit 130fc8
        else:
Packit 130fc8
            return self._connection.call_blocking(self._named_service,
Packit 130fc8
                                                  self._object_path,
Packit 130fc8
                                                  dbus_interface,
Packit 130fc8
                                                  self._method_name,
Packit 130fc8
                                                  signature,
Packit 130fc8
                                                  args,
Packit 130fc8
                                                  **keywords)
Packit 130fc8
Packit 130fc8
    def call_async(self, *args, **keywords):
Packit 130fc8
        reply_handler = keywords.pop('reply_handler', None)
Packit 130fc8
        error_handler = keywords.pop('error_handler', None)
Packit 130fc8
        signature = keywords.pop('signature', None)
Packit 130fc8
Packit 130fc8
        dbus_interface = keywords.pop('dbus_interface', self._dbus_interface)
Packit 130fc8
Packit 130fc8
        if signature is None:
Packit 130fc8
            if dbus_interface:
Packit 130fc8
                key = dbus_interface + '.' + self._method_name
Packit 130fc8
            else:
Packit 130fc8
                key = self._method_name
Packit 130fc8
            signature = self._proxy._introspect_method_map.get(key, None)
Packit 130fc8
Packit 130fc8
        self._connection.call_async(self._named_service,
Packit 130fc8
                                    self._object_path,
Packit 130fc8
                                    dbus_interface,
Packit 130fc8
                                    self._method_name,
Packit 130fc8
                                    signature,
Packit 130fc8
                                    args,
Packit 130fc8
                                    reply_handler,
Packit 130fc8
                                    error_handler,
Packit 130fc8
                                    **keywords)
Packit 130fc8
Packit 130fc8
Packit 130fc8
class ProxyObject(object):
Packit 130fc8
    """A proxy to the remote Object.
Packit 130fc8
Packit 130fc8
    A ProxyObject is provided by the Bus. ProxyObjects
Packit 130fc8
    have member functions, and can be called like normal Python objects.
Packit 130fc8
    """
Packit 130fc8
    ProxyMethodClass = _ProxyMethod
Packit 130fc8
    DeferredMethodClass = _DeferredMethod
Packit 130fc8
Packit 130fc8
    INTROSPECT_STATE_DONT_INTROSPECT = 0
Packit 130fc8
    INTROSPECT_STATE_INTROSPECT_IN_PROGRESS = 1
Packit 130fc8
    INTROSPECT_STATE_INTROSPECT_DONE = 2
Packit 130fc8
Packit 130fc8
    def __init__(self, conn=None, bus_name=None, object_path=None,
Packit 130fc8
                 introspect=True, follow_name_owner_changes=False, **kwargs):
Packit 130fc8
        """Initialize the proxy object.
Packit 130fc8
Packit 130fc8
        :Parameters:
Packit 130fc8
            `conn` : `dbus.connection.Connection`
Packit 130fc8
                The bus or connection on which to find this object.
Packit 130fc8
                The keyword argument `bus` is a deprecated alias for this.
Packit 130fc8
            `bus_name` : str
Packit 130fc8
                A bus name for the application owning the object, to be used
Packit 130fc8
                as the destination for method calls and the sender for
Packit 130fc8
                signal matches. The keyword argument ``named_service`` is a
Packit 130fc8
                deprecated alias for this.
Packit 130fc8
            `object_path` : str
Packit 130fc8
                The object path at which the application exports the object
Packit 130fc8
            `introspect` : bool
Packit 130fc8
                If true (default), attempt to introspect the remote
Packit 130fc8
                object to find out supported methods and their signatures
Packit 130fc8
            `follow_name_owner_changes` : bool
Packit 130fc8
                If true (default is false) and the `bus_name` is a
Packit 130fc8
                well-known name, follow ownership changes for that name
Packit 130fc8
        """
Packit 130fc8
        bus = kwargs.pop('bus', None)
Packit 130fc8
        if bus is not None:
Packit 130fc8
            if conn is not None:
Packit 130fc8
                raise TypeError('conn and bus cannot both be specified')
Packit 130fc8
            conn = bus
Packit 130fc8
            from warnings import warn
Packit 130fc8
            warn('Passing the bus parameter to ProxyObject by name is '
Packit 130fc8
                 'deprecated: please use positional parameters',
Packit 130fc8
                 DeprecationWarning, stacklevel=2)
Packit 130fc8
        named_service = kwargs.pop('named_service', None)
Packit 130fc8
        if named_service is not None:
Packit 130fc8
            if bus_name is not None:
Packit 130fc8
                raise TypeError('bus_name and named_service cannot both be '
Packit 130fc8
                                'specified')
Packit 130fc8
            bus_name = named_service
Packit 130fc8
            from warnings import warn
Packit 130fc8
            warn('Passing the named_service parameter to ProxyObject by name '
Packit 130fc8
                 'is deprecated: please use positional parameters',
Packit 130fc8
                 DeprecationWarning, stacklevel=2)
Packit 130fc8
        if kwargs:
Packit 130fc8
            raise TypeError('ProxyObject.__init__ does not take these '
Packit 130fc8
                            'keyword arguments: %s'
Packit 130fc8
                            % ', '.join(kwargs.keys()))
Packit 130fc8
Packit 130fc8
        if follow_name_owner_changes:
Packit 130fc8
            # we don't get the signals unless the Bus has a main loop
Packit 130fc8
            # XXX: using Bus internals
Packit 130fc8
            conn._require_main_loop()
Packit 130fc8
Packit 130fc8
        self._bus = conn
Packit 130fc8
Packit 130fc8
        if bus_name is not None:
Packit 130fc8
            _dbus_bindings.validate_bus_name(bus_name)
Packit 130fc8
        # the attribute is still called _named_service for the moment,
Packit 130fc8
        # for the benefit of telepathy-python
Packit 130fc8
        self._named_service = self._requested_bus_name = bus_name
Packit 130fc8
Packit 130fc8
        _dbus_bindings.validate_object_path(object_path)
Packit 130fc8
        self.__dbus_object_path__ = object_path
Packit 130fc8
Packit 130fc8
        if not follow_name_owner_changes:
Packit 130fc8
            self._named_service = conn.activate_name_owner(bus_name)
Packit 130fc8
Packit 130fc8
        #PendingCall object for Introspect call
Packit 130fc8
        self._pending_introspect = None
Packit 130fc8
        #queue of async calls waiting on the Introspect to return
Packit 130fc8
        self._pending_introspect_queue = []
Packit 130fc8
        #dictionary mapping method names to their input signatures
Packit 130fc8
        self._introspect_method_map = {}
Packit 130fc8
Packit 130fc8
        # must be a recursive lock because block() is called while locked,
Packit 130fc8
        # and calls the callback which re-takes the lock
Packit 130fc8
        self._introspect_lock = RLock()
Packit 130fc8
Packit 130fc8
        if not introspect or self.__dbus_object_path__ == LOCAL_PATH:
Packit 130fc8
            self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
Packit 130fc8
        else:
Packit 130fc8
            self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS
Packit 130fc8
Packit 130fc8
            self._pending_introspect = self._Introspect()
Packit 130fc8
Packit 130fc8
    bus_name = property(lambda self: self._named_service, None, None,
Packit 130fc8
            """The bus name to which this proxy is bound. (Read-only,
Packit 130fc8
            may change.)
Packit 130fc8
Packit 130fc8
            If the proxy was instantiated using a unique name, this property
Packit 130fc8
            is that unique name.
Packit 130fc8
Packit 130fc8
            If the proxy was instantiated with a well-known name and with
Packit 130fc8
            ``follow_name_owner_changes`` set false (the default), this
Packit 130fc8
            property is the unique name of the connection that owned that
Packit 130fc8
            well-known name when the proxy was instantiated, which might
Packit 130fc8
            not actually own the requested well-known name any more.
Packit 130fc8
Packit 130fc8
            If the proxy was instantiated with a well-known name and with
Packit 130fc8
            ``follow_name_owner_changes`` set true, this property is that
Packit 130fc8
            well-known name.
Packit 130fc8
            """)
Packit 130fc8
Packit 130fc8
    requested_bus_name = property(lambda self: self._requested_bus_name,
Packit 130fc8
            None, None,
Packit 130fc8
            """The bus name which was requested when this proxy was
Packit 130fc8
            instantiated.
Packit 130fc8
            """)
Packit 130fc8
Packit 130fc8
    object_path = property(lambda self: self.__dbus_object_path__,
Packit 130fc8
            None, None,
Packit 130fc8
            """The object-path of this proxy.""")
Packit 130fc8
Packit 130fc8
    # XXX: We don't currently support this because it's the signal receiver
Packit 130fc8
    # that's responsible for tracking name owner changes, but it
Packit 130fc8
    # seems a natural thing to add in future.
Packit 130fc8
    #unique_bus_name = property(lambda self: something, None, None,
Packit 130fc8
    #        """The unique name of the connection to which this proxy is
Packit 130fc8
    #        currently bound. (Read-only, may change.)
Packit 130fc8
    #        """)
Packit 130fc8
Packit 130fc8
    def connect_to_signal(self, signal_name, handler_function, dbus_interface=None, **keywords):
Packit 130fc8
        """Arrange for the given function to be called when the given signal
Packit 130fc8
        is received.
Packit 130fc8
Packit 130fc8
        :Parameters:
Packit 130fc8
            `signal_name` : str
Packit 130fc8
                The name of the signal
Packit 130fc8
            `handler_function` : callable
Packit 130fc8
                A function to be called when the signal is emitted by
Packit 130fc8
                the remote object. Its positional arguments will be the
Packit 130fc8
                arguments of the signal; optionally, it may be given
Packit 130fc8
                keyword arguments as described below.
Packit 130fc8
            `dbus_interface` : str
Packit 130fc8
                Optional interface with which to qualify the signal name.
Packit 130fc8
                If None (the default) the handler will be called whenever a
Packit 130fc8
                signal of the given member name is received, whatever
Packit 130fc8
                its interface.
Packit 130fc8
        :Keywords:
Packit 130fc8
            `utf8_strings` : bool
Packit 130fc8
                If True, the handler function will receive any string
Packit 130fc8
                arguments as dbus.UTF8String objects (a subclass of str
Packit 130fc8
                guaranteed to be UTF-8). If False (default) it will receive
Packit 130fc8
                any string arguments as dbus.String objects (a subclass of
Packit 130fc8
                unicode).
Packit 130fc8
            `byte_arrays` : bool
Packit 130fc8
                If True, the handler function will receive any byte-array
Packit 130fc8
                arguments as dbus.ByteArray objects (a subclass of str).
Packit 130fc8
                If False (default) it will receive any byte-array
Packit 130fc8
                arguments as a dbus.Array of dbus.Byte (subclasses of:
Packit 130fc8
                a list of ints).
Packit 130fc8
            `sender_keyword` : str
Packit 130fc8
                If not None (the default), the handler function will receive
Packit 130fc8
                the unique name of the sending endpoint as a keyword
Packit 130fc8
                argument with this name
Packit 130fc8
            `destination_keyword` : str
Packit 130fc8
                If not None (the default), the handler function will receive
Packit 130fc8
                the bus name of the destination (or None if the signal is a
Packit 130fc8
                broadcast, as is usual) as a keyword argument with this name.
Packit 130fc8
            `interface_keyword` : str
Packit 130fc8
                If not None (the default), the handler function will receive
Packit 130fc8
                the signal interface as a keyword argument with this name.
Packit 130fc8
            `member_keyword` : str
Packit 130fc8
                If not None (the default), the handler function will receive
Packit 130fc8
                the signal name as a keyword argument with this name.
Packit 130fc8
            `path_keyword` : str
Packit 130fc8
                If not None (the default), the handler function will receive
Packit 130fc8
                the object-path of the sending object as a keyword argument
Packit 130fc8
                with this name
Packit 130fc8
            `message_keyword` : str
Packit 130fc8
                If not None (the default), the handler function will receive
Packit 130fc8
                the `dbus.lowlevel.SignalMessage` as a keyword argument with
Packit 130fc8
                this name.
Packit 130fc8
            `arg...` : unicode or UTF-8 str
Packit 130fc8
                If there are additional keyword parameters of the form
Packit 130fc8
                ``arg``\ *n*, match only signals where the *n*\ th argument
Packit 130fc8
                is the value given for that keyword parameter. As of this time
Packit 130fc8
                only string arguments can be matched (in particular,
Packit 130fc8
                object paths and signatures can't).
Packit 130fc8
        """
Packit 130fc8
        return \
Packit 130fc8
        self._bus.add_signal_receiver(handler_function,
Packit 130fc8
                                      signal_name=signal_name,
Packit 130fc8
                                      dbus_interface=dbus_interface,
Packit 130fc8
                                      bus_name=self._named_service,
Packit 130fc8
                                      path=self.__dbus_object_path__,
Packit 130fc8
                                      **keywords)
Packit 130fc8
Packit 130fc8
    def _Introspect(self):
Packit 130fc8
        kwargs = {}
Packit 130fc8
        if is_py2:
Packit 130fc8
            kwargs['utf8_strings'] = True
Packit 130fc8
        return self._bus.call_async(self._named_service,
Packit 130fc8
                                    self.__dbus_object_path__,
Packit 130fc8
                                    INTROSPECTABLE_IFACE, 'Introspect', '', (),
Packit 130fc8
                                    self._introspect_reply_handler,
Packit 130fc8
                                    self._introspect_error_handler,
Packit 130fc8
                                    require_main_loop=False, **kwargs)
Packit 130fc8
Packit 130fc8
    def _introspect_execute_queue(self):
Packit 130fc8
        # FIXME: potential to flood the bus
Packit 130fc8
        # We should make sure mainloops all have idle handlers
Packit 130fc8
        # and do one message per idle
Packit 130fc8
        for (proxy_method, args, keywords) in self._pending_introspect_queue:
Packit 130fc8
            proxy_method(*args, **keywords)
Packit 130fc8
        self._pending_introspect_queue = []
Packit 130fc8
Packit 130fc8
    def _introspect_reply_handler(self, data):
Packit 130fc8
        self._introspect_lock.acquire()
Packit 130fc8
        try:
Packit 130fc8
            try:
Packit 130fc8
                self._introspect_method_map = process_introspection_data(data)
Packit 130fc8
            except IntrospectionParserException as e:
Packit 130fc8
                self._introspect_error_handler(e)
Packit 130fc8
                return
Packit 130fc8
Packit 130fc8
            self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_DONE
Packit 130fc8
            self._pending_introspect = None
Packit 130fc8
            self._introspect_execute_queue()
Packit 130fc8
        finally:
Packit 130fc8
            self._introspect_lock.release()
Packit 130fc8
Packit 130fc8
    def _introspect_error_handler(self, error):
Packit 130fc8
        logging.basicConfig()
Packit 130fc8
        _logger.error("Introspect error on %s:%s: %s.%s: %s",
Packit 130fc8
                      self._named_service, self.__dbus_object_path__,
Packit 130fc8
                      error.__class__.__module__, error.__class__.__name__,
Packit 130fc8
                      error)
Packit 130fc8
        self._introspect_lock.acquire()
Packit 130fc8
        try:
Packit 130fc8
            _logger.debug('Executing introspect queue due to error')
Packit 130fc8
            self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
Packit 130fc8
            self._pending_introspect = None
Packit 130fc8
            self._introspect_execute_queue()
Packit 130fc8
        finally:
Packit 130fc8
            self._introspect_lock.release()
Packit 130fc8
Packit 130fc8
    def _introspect_block(self):
Packit 130fc8
        self._introspect_lock.acquire()
Packit 130fc8
        try:
Packit 130fc8
            if self._pending_introspect is not None:
Packit 130fc8
                self._pending_introspect.block()
Packit 130fc8
            # else someone still has a _DeferredMethod from before we
Packit 130fc8
            # finished introspection: no need to do anything special any more
Packit 130fc8
        finally:
Packit 130fc8
            self._introspect_lock.release()
Packit 130fc8
Packit 130fc8
    def _introspect_add_to_queue(self, callback, args, kwargs):
Packit 130fc8
        self._introspect_lock.acquire()
Packit 130fc8
        try:
Packit 130fc8
            if self._introspect_state == self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS:
Packit 130fc8
                self._pending_introspect_queue.append((callback, args, kwargs))
Packit 130fc8
            else:
Packit 130fc8
                # someone still has a _DeferredMethod from before we
Packit 130fc8
                # finished introspection
Packit 130fc8
                callback(*args, **kwargs)
Packit 130fc8
        finally:
Packit 130fc8
            self._introspect_lock.release()
Packit 130fc8
Packit 130fc8
    def __getattr__(self, member):
Packit 130fc8
        if member.startswith('__') and member.endswith('__'):
Packit 130fc8
            raise AttributeError(member)
Packit 130fc8
        else:
Packit 130fc8
            return self.get_dbus_method(member)
Packit 130fc8
Packit 130fc8
    def get_dbus_method(self, member, dbus_interface=None):
Packit 130fc8
        """Return a proxy method representing the given D-Bus method. The
Packit 130fc8
        returned proxy method can be called in the usual way. For instance, ::
Packit 130fc8
Packit 130fc8
            proxy.get_dbus_method("Foo", dbus_interface='com.example.Bar')(123)
Packit 130fc8
Packit 130fc8
        is equivalent to::
Packit 130fc8
Packit 130fc8
            proxy.Foo(123, dbus_interface='com.example.Bar')
Packit 130fc8
Packit 130fc8
        or even::
Packit 130fc8
Packit 130fc8
            getattr(proxy, "Foo")(123, dbus_interface='com.example.Bar')
Packit 130fc8
Packit 130fc8
        However, using `get_dbus_method` is the only way to call D-Bus
Packit 130fc8
        methods with certain awkward names - if the author of a service
Packit 130fc8
        implements a method called ``connect_to_signal`` or even
Packit 130fc8
        ``__getattr__``, you'll need to use `get_dbus_method` to call them.
Packit 130fc8
Packit 130fc8
        For services which follow the D-Bus convention of CamelCaseMethodNames
Packit 130fc8
        this won't be a problem.
Packit 130fc8
        """
Packit 130fc8
Packit 130fc8
        ret = self.ProxyMethodClass(self, self._bus,
Packit 130fc8
                                    self._named_service,
Packit 130fc8
                                    self.__dbus_object_path__, member,
Packit 130fc8
                                    dbus_interface)
Packit 130fc8
Packit 130fc8
        # this can be done without taking the lock - the worst that can
Packit 130fc8
        # happen is that we accidentally return a _DeferredMethod just after
Packit 130fc8
        # finishing introspection, in which case _introspect_add_to_queue and
Packit 130fc8
        # _introspect_block will do the right thing anyway
Packit 130fc8
        if self._introspect_state == self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS:
Packit 130fc8
            ret = self.DeferredMethodClass(ret, self._introspect_add_to_queue,
Packit 130fc8
                                           self._introspect_block)
Packit 130fc8
Packit 130fc8
        return ret
Packit 130fc8
Packit 130fc8
    def __repr__(self):
Packit 130fc8
        return '<ProxyObject wrapping %s %s %s at %#x>'%(
Packit 130fc8
            self._bus, self._named_service, self.__dbus_object_path__, id(self))
Packit 130fc8
    __str__ = __repr__
Packit 130fc8
Packit 130fc8
Packit 130fc8
class Interface(object):
Packit 130fc8
    """An interface into a remote object.
Packit 130fc8
Packit 130fc8
    An Interface can be used to wrap ProxyObjects
Packit 130fc8
    so that calls can be routed to their correct
Packit 130fc8
    D-Bus interface.
Packit 130fc8
    """
Packit 130fc8
Packit 130fc8
    def __init__(self, object, dbus_interface):
Packit 130fc8
        """Construct a proxy for the given interface on the given object.
Packit 130fc8
Packit 130fc8
        :Parameters:
Packit 130fc8
            `object` : `dbus.proxies.ProxyObject` or `dbus.Interface`
Packit 130fc8
                The remote object or another of its interfaces
Packit 130fc8
            `dbus_interface` : str
Packit 130fc8
                An interface the `object` implements
Packit 130fc8
        """
Packit 130fc8
        if isinstance(object, Interface):
Packit 130fc8
            self._obj = object.proxy_object
Packit 130fc8
        else:
Packit 130fc8
            self._obj = object
Packit 130fc8
        self._dbus_interface = dbus_interface
Packit 130fc8
Packit 130fc8
    object_path = property (lambda self: self._obj.object_path, None, None,
Packit 130fc8
                            "The D-Bus object path of the underlying object")
Packit 130fc8
    __dbus_object_path__ = object_path
Packit 130fc8
    bus_name = property (lambda self: self._obj.bus_name, None, None,
Packit 130fc8
                         "The bus name to which the underlying proxy object "
Packit 130fc8
                         "is bound")
Packit 130fc8
    requested_bus_name = property (lambda self: self._obj.requested_bus_name,
Packit 130fc8
                                   None, None,
Packit 130fc8
                                   "The bus name which was requested when the "
Packit 130fc8
                                   "underlying object was created")
Packit 130fc8
    proxy_object = property (lambda self: self._obj, None, None,
Packit 130fc8
                             """The underlying proxy object""")
Packit 130fc8
    dbus_interface = property (lambda self: self._dbus_interface, None, None,
Packit 130fc8
                               """The D-Bus interface represented""")
Packit 130fc8
Packit 130fc8
    def connect_to_signal(self, signal_name, handler_function,
Packit 130fc8
                          dbus_interface=None, **keywords):
Packit 130fc8
        """Arrange for a function to be called when the given signal is
Packit 130fc8
        emitted.
Packit 130fc8
Packit 130fc8
        The parameters and keyword arguments are the same as for
Packit 130fc8
        `dbus.proxies.ProxyObject.connect_to_signal`, except that if
Packit 130fc8
        `dbus_interface` is None (the default), the D-Bus interface that
Packit 130fc8
        was passed to the `Interface` constructor is used.
Packit 130fc8
        """
Packit 130fc8
        if not dbus_interface:
Packit 130fc8
            dbus_interface = self._dbus_interface
Packit 130fc8
Packit 130fc8
        return self._obj.connect_to_signal(signal_name, handler_function,
Packit 130fc8
                                           dbus_interface, **keywords)
Packit 130fc8
Packit 130fc8
    def __getattr__(self, member):
Packit 130fc8
        if member.startswith('__') and member.endswith('__'):
Packit 130fc8
            raise AttributeError(member)
Packit 130fc8
        else:
Packit 130fc8
            return self._obj.get_dbus_method(member, self._dbus_interface)
Packit 130fc8
Packit 130fc8
    def get_dbus_method(self, member, dbus_interface=None):
Packit 130fc8
        """Return a proxy method representing the given D-Bus method.
Packit 130fc8
Packit 130fc8
        This is the same as `dbus.proxies.ProxyObject.get_dbus_method`
Packit 130fc8
        except that if `dbus_interface` is None (the default),
Packit 130fc8
        the D-Bus interface that was passed to the `Interface` constructor
Packit 130fc8
        is used.
Packit 130fc8
        """
Packit 130fc8
        if dbus_interface is None:
Packit 130fc8
            dbus_interface = self._dbus_interface
Packit 130fc8
        return self._obj.get_dbus_method(member, dbus_interface)
Packit 130fc8
Packit 130fc8
    def __repr__(self):
Packit 130fc8
        return '<Interface %r implementing %r at %#x>'%(
Packit 130fc8
        self._obj, self._dbus_interface, id(self))
Packit 130fc8
    __str__ = __repr__