Blame dbus/connection.py

Packit 130fc8
# Copyright (C) 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
__all__ = ('Connection', 'SignalMatch')
Packit 130fc8
__docformat__ = 'reStructuredText'
Packit 130fc8
Packit 130fc8
import logging
Packit 130fc8
import threading
Packit 130fc8
import weakref
Packit 130fc8
Packit 130fc8
from _dbus_bindings import (
Packit 130fc8
    Connection as _Connection, LOCAL_IFACE, LOCAL_PATH, validate_bus_name,
Packit 130fc8
    validate_interface_name, validate_member_name, validate_object_path)
Packit 130fc8
from dbus.exceptions import DBusException
Packit 130fc8
from dbus.lowlevel import (
Packit 130fc8
    ErrorMessage, HANDLER_RESULT_NOT_YET_HANDLED, MethodCallMessage,
Packit 130fc8
    MethodReturnMessage, SignalMessage)
Packit 130fc8
from dbus.proxies import ProxyObject
Packit 130fc8
from dbus._compat import is_py2, is_py3
Packit 130fc8
Packit 130fc8
if is_py3:
Packit 130fc8
    from _dbus_bindings import String
Packit 130fc8
else:
Packit 130fc8
    from _dbus_bindings import UTF8String
Packit 130fc8
Packit 130fc8
Packit 130fc8
_logger = logging.getLogger('dbus.connection')
Packit 130fc8
Packit 130fc8
Packit 130fc8
def _noop(*args, **kwargs):
Packit 130fc8
    pass
Packit 130fc8
Packit 130fc8
Packit 130fc8
class SignalMatch(object):
Packit 130fc8
    _slots = ['_sender_name_owner', '_member', '_interface', '_sender',
Packit 130fc8
              '_path', '_handler', '_args_match', '_rule',
Packit 130fc8
              '_byte_arrays', '_conn_weakref',
Packit 130fc8
              '_destination_keyword', '_interface_keyword',
Packit 130fc8
              '_message_keyword', '_member_keyword',
Packit 130fc8
              '_sender_keyword', '_path_keyword', '_int_args_match']
Packit 130fc8
    if is_py2:
Packit 130fc8
        _slots.append('_utf8_strings')
Packit 130fc8
Packit 130fc8
    __slots__ = tuple(_slots)
Packit 130fc8
Packit 130fc8
    def __init__(self, conn, sender, object_path, dbus_interface,
Packit 130fc8
                 member, handler, byte_arrays=False,
Packit 130fc8
                 sender_keyword=None, path_keyword=None,
Packit 130fc8
                 interface_keyword=None, member_keyword=None,
Packit 130fc8
                 message_keyword=None, destination_keyword=None,
Packit 130fc8
                 **kwargs):
Packit 130fc8
        if member is not None:
Packit 130fc8
            validate_member_name(member)
Packit 130fc8
        if dbus_interface is not None:
Packit 130fc8
            validate_interface_name(dbus_interface)
Packit 130fc8
        if sender is not None:
Packit 130fc8
            validate_bus_name(sender)
Packit 130fc8
        if object_path is not None:
Packit 130fc8
            validate_object_path(object_path)
Packit 130fc8
Packit 130fc8
        self._rule = None
Packit 130fc8
        self._conn_weakref = weakref.ref(conn)
Packit 130fc8
        self._sender = sender
Packit 130fc8
        self._interface = dbus_interface
Packit 130fc8
        self._member = member
Packit 130fc8
        self._path = object_path
Packit 130fc8
        self._handler = handler
Packit 130fc8
Packit 130fc8
        # if the connection is actually a bus, it's responsible for changing
Packit 130fc8
        # this later
Packit 130fc8
        self._sender_name_owner = sender
Packit 130fc8
Packit 130fc8
        if is_py2:
Packit 130fc8
            self._utf8_strings = kwargs.pop('utf8_strings', False)
Packit 130fc8
        elif 'utf8_strings' in kwargs:
Packit 130fc8
            raise TypeError("unexpected keyword argument 'utf8_strings'")
Packit 130fc8
Packit 130fc8
        self._byte_arrays = byte_arrays
Packit 130fc8
        self._sender_keyword = sender_keyword
Packit 130fc8
        self._path_keyword = path_keyword
Packit 130fc8
        self._member_keyword = member_keyword
Packit 130fc8
        self._interface_keyword = interface_keyword
Packit 130fc8
        self._message_keyword = message_keyword
Packit 130fc8
        self._destination_keyword = destination_keyword
Packit 130fc8
Packit 130fc8
        self._args_match = kwargs
Packit 130fc8
        if not kwargs:
Packit 130fc8
            self._int_args_match = None
Packit 130fc8
        else:
Packit 130fc8
            self._int_args_match = {}
Packit 130fc8
            for kwarg in kwargs:
Packit 130fc8
                if not kwarg.startswith('arg'):
Packit 130fc8
                    raise TypeError('SignalMatch: unknown keyword argument %s'
Packit 130fc8
                                    % kwarg)
Packit 130fc8
                try:
Packit 130fc8
                    index = int(kwarg[3:])
Packit 130fc8
                except ValueError:
Packit 130fc8
                    raise TypeError('SignalMatch: unknown keyword argument %s'
Packit 130fc8
                                    % kwarg)
Packit 130fc8
                if index < 0 or index > 63:
Packit 130fc8
                    raise TypeError('SignalMatch: arg match index must be in '
Packit 130fc8
                                    'range(64), not %d' % index)
Packit 130fc8
                self._int_args_match[index] = kwargs[kwarg]
Packit 130fc8
Packit 130fc8
    def __hash__(self):
Packit 130fc8
        """SignalMatch objects are compared by identity."""
Packit 130fc8
        return hash(id(self))
Packit 130fc8
Packit 130fc8
    def __eq__(self, other):
Packit 130fc8
        """SignalMatch objects are compared by identity."""
Packit 130fc8
        return self is other
Packit 130fc8
Packit 130fc8
    def __ne__(self, other):
Packit 130fc8
        """SignalMatch objects are compared by identity."""
Packit 130fc8
        return self is not other
Packit 130fc8
Packit 130fc8
    sender = property(lambda self: self._sender)
Packit 130fc8
Packit 130fc8
    def __str__(self):
Packit 130fc8
        if self._rule is None:
Packit 130fc8
            rule = ["type='signal'"]
Packit 130fc8
            if self._sender is not None:
Packit 130fc8
                rule.append("sender='%s'" % self._sender)
Packit 130fc8
            if self._path is not None:
Packit 130fc8
                rule.append("path='%s'" % self._path)
Packit 130fc8
            if self._interface is not None:
Packit 130fc8
                rule.append("interface='%s'" % self._interface)
Packit 130fc8
            if self._member is not None:
Packit 130fc8
                rule.append("member='%s'" % self._member)
Packit 130fc8
            if self._int_args_match is not None:
Packit 130fc8
                for index, value in self._int_args_match.items():
Packit 130fc8
                    rule.append("arg%d='%s'" % (index, value))
Packit 130fc8
Packit 130fc8
            self._rule = ','.join(rule)
Packit 130fc8
Packit 130fc8
        return self._rule
Packit 130fc8
Packit 130fc8
    def __repr__(self):
Packit 130fc8
        return ('<%s at %x "%s" on conn %r>'
Packit 130fc8
                % (self.__class__, id(self), self._rule, self._conn_weakref()))
Packit 130fc8
Packit 130fc8
    def set_sender_name_owner(self, new_name):
Packit 130fc8
        self._sender_name_owner = new_name
Packit 130fc8
Packit 130fc8
    def matches_removal_spec(self, sender, object_path,
Packit 130fc8
                             dbus_interface, member, handler, **kwargs):
Packit 130fc8
        if handler not in (None, self._handler):
Packit 130fc8
            return False
Packit 130fc8
        if sender != self._sender:
Packit 130fc8
            return False
Packit 130fc8
        if object_path != self._path:
Packit 130fc8
            return False
Packit 130fc8
        if dbus_interface != self._interface:
Packit 130fc8
            return False
Packit 130fc8
        if member != self._member:
Packit 130fc8
            return False
Packit 130fc8
        if kwargs != self._args_match:
Packit 130fc8
            return False
Packit 130fc8
        return True
Packit 130fc8
Packit 130fc8
    def maybe_handle_message(self, message):
Packit 130fc8
        args = None
Packit 130fc8
Packit 130fc8
        # these haven't been checked yet by the match tree
Packit 130fc8
        if self._sender_name_owner not in (None, message.get_sender()):
Packit 130fc8
            return False
Packit 130fc8
        if self._int_args_match is not None:
Packit 130fc8
            # extracting args with utf8_strings and byte_arrays is less work
Packit 130fc8
            kwargs = dict(byte_arrays=True)
Packit 130fc8
            arg_type = (String if is_py3 else UTF8String)
Packit 130fc8
            if is_py2:
Packit 130fc8
                kwargs['utf8_strings'] = True
Packit 130fc8
            args = message.get_args_list(**kwargs)
Packit 130fc8
            for index, value in self._int_args_match.items():
Packit 130fc8
                if (index >= len(args)
Packit 130fc8
                    or not isinstance(args[index], arg_type)
Packit 130fc8
                    or args[index] != value):
Packit 130fc8
                    return False
Packit 130fc8
Packit 130fc8
        # these have likely already been checked by the match tree
Packit 130fc8
        if self._member not in (None, message.get_member()):
Packit 130fc8
            return False
Packit 130fc8
        if self._interface not in (None, message.get_interface()):
Packit 130fc8
            return False
Packit 130fc8
        if self._path not in (None, message.get_path()):
Packit 130fc8
            return False
Packit 130fc8
Packit 130fc8
        try:
Packit 130fc8
            # minor optimization: if we already extracted the args with the
Packit 130fc8
            # right calling convention to do the args match, don't bother
Packit 130fc8
            # doing so again
Packit 130fc8
            utf8_strings = (is_py2 and self._utf8_strings)
Packit 130fc8
            if args is None or not utf8_strings or not self._byte_arrays:
Packit 130fc8
                kwargs = dict(byte_arrays=self._byte_arrays)
Packit 130fc8
                if is_py2:
Packit 130fc8
                    kwargs['utf8_strings'] = self._utf8_strings
Packit 130fc8
                args = message.get_args_list(**kwargs)
Packit 130fc8
            kwargs = {}
Packit 130fc8
            if self._sender_keyword is not None:
Packit 130fc8
                kwargs[self._sender_keyword] = message.get_sender()
Packit 130fc8
            if self._destination_keyword is not None:
Packit 130fc8
                kwargs[self._destination_keyword] = message.get_destination()
Packit 130fc8
            if self._path_keyword is not None:
Packit 130fc8
                kwargs[self._path_keyword] = message.get_path()
Packit 130fc8
            if self._member_keyword is not None:
Packit 130fc8
                kwargs[self._member_keyword] = message.get_member()
Packit 130fc8
            if self._interface_keyword is not None:
Packit 130fc8
                kwargs[self._interface_keyword] = message.get_interface()
Packit 130fc8
            if self._message_keyword is not None:
Packit 130fc8
                kwargs[self._message_keyword] = message
Packit 130fc8
            self._handler(*args, **kwargs)
Packit 130fc8
        except:
Packit 130fc8
            # basicConfig is a no-op if logging is already configured
Packit 130fc8
            logging.basicConfig()
Packit 130fc8
            _logger.error('Exception in handler for D-Bus signal:', exc_info=1)
Packit 130fc8
Packit 130fc8
        return True
Packit 130fc8
Packit 130fc8
    def remove(self):
Packit 130fc8
        conn = self._conn_weakref()
Packit 130fc8
        # do nothing if the connection has already vanished
Packit 130fc8
        if conn is not None:
Packit 130fc8
            conn.remove_signal_receiver(self, self._member,
Packit 130fc8
                                        self._interface, self._sender,
Packit 130fc8
                                        self._path,
Packit 130fc8
                                        **self._args_match)
Packit 130fc8
Packit 130fc8
Packit 130fc8
class Connection(_Connection):
Packit 130fc8
    """A connection to another application. In this base class there is
Packit 130fc8
    assumed to be no bus daemon.
Packit 130fc8
Packit 130fc8
    :Since: 0.81.0
Packit 130fc8
    """
Packit 130fc8
Packit 130fc8
    ProxyObjectClass = ProxyObject
Packit 130fc8
Packit 130fc8
    def __init__(self, *args, **kwargs):
Packit 130fc8
        super(Connection, self).__init__(*args, **kwargs)
Packit 130fc8
Packit 130fc8
        # this if-block is needed because shared bus connections can be
Packit 130fc8
        # __init__'ed more than once
Packit 130fc8
        if not hasattr(self, '_dbus_Connection_initialized'):
Packit 130fc8
            self._dbus_Connection_initialized = 1
Packit 130fc8
Packit 130fc8
            self.__call_on_disconnection = []
Packit 130fc8
Packit 130fc8
            self._signal_recipients_by_object_path = {}
Packit 130fc8
            """Map from object path to dict mapping dbus_interface to dict
Packit 130fc8
            mapping member to list of SignalMatch objects."""
Packit 130fc8
Packit 130fc8
            self._signals_lock = threading.Lock()
Packit 130fc8
            """Lock used to protect signal data structures"""
Packit 130fc8
Packit 130fc8
            self.add_message_filter(self.__class__._signal_func)
Packit 130fc8
Packit 130fc8
    def activate_name_owner(self, bus_name):
Packit 130fc8
        """Return the unique name for the given bus name, activating it
Packit 130fc8
        if necessary and possible.
Packit 130fc8
Packit 130fc8
        If the name is already unique or this connection is not to a
Packit 130fc8
        bus daemon, just return it.
Packit 130fc8
Packit 130fc8
        :Returns: a bus name. If the given `bus_name` exists, the returned
Packit 130fc8
            name identifies its current owner; otherwise the returned name
Packit 130fc8
            does not exist.
Packit 130fc8
        :Raises DBusException: if the implementation has failed
Packit 130fc8
            to activate the given bus name.
Packit 130fc8
        :Since: 0.81.0
Packit 130fc8
        """
Packit 130fc8
        return bus_name
Packit 130fc8
Packit 130fc8
    def get_object(self, bus_name=None, object_path=None, introspect=True,
Packit 130fc8
                   **kwargs):
Packit 130fc8
        """Return a local proxy for the given remote object.
Packit 130fc8
Packit 130fc8
        Method calls on the proxy are translated into method calls on the
Packit 130fc8
        remote object.
Packit 130fc8
Packit 130fc8
        :Parameters:
Packit 130fc8
            `bus_name` : str
Packit 130fc8
                A bus name (either the unique name or a well-known name)
Packit 130fc8
                of the application owning the object. The keyword argument
Packit 130fc8
                named_service is a deprecated alias for this.
Packit 130fc8
            `object_path` : str
Packit 130fc8
                The object path of the desired 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
Packit 130fc8
        :Returns: a `dbus.proxies.ProxyObject`
Packit 130fc8
        """
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 '
Packit 130fc8
                                'be specified')
Packit 130fc8
            from warnings import warn
Packit 130fc8
            warn('Passing the named_service parameter to get_object by name '
Packit 130fc8
                 'is deprecated: please use positional parameters',
Packit 130fc8
                 DeprecationWarning, stacklevel=2)
Packit 130fc8
            bus_name = named_service
Packit 130fc8
        if kwargs:
Packit 130fc8
            raise TypeError('get_object does not take these keyword '
Packit 130fc8
                            'arguments: %s' % ', '.join(kwargs.keys()))
Packit 130fc8
Packit 130fc8
        return self.ProxyObjectClass(self, bus_name, object_path,
Packit 130fc8
                                     introspect=introspect)
Packit 130fc8
Packit 130fc8
    def add_signal_receiver(self, handler_function,
Packit 130fc8
                                  signal_name=None,
Packit 130fc8
                                  dbus_interface=None,
Packit 130fc8
                                  bus_name=None,
Packit 130fc8
                                  path=None,
Packit 130fc8
                                  **keywords):
Packit 130fc8
        """Arrange for the given function to be called when a signal matching
Packit 130fc8
        the parameters is received.
Packit 130fc8
Packit 130fc8
        :Parameters:
Packit 130fc8
            `handler_function` : callable
Packit 130fc8
                The function to be called. Its positional arguments will
Packit 130fc8
                be the arguments of the signal. By default it will receive
Packit 130fc8
                no keyword arguments, but see the description of
Packit 130fc8
                the optional keyword arguments below.
Packit 130fc8
            `signal_name` : str
Packit 130fc8
                The signal name; None (the default) matches all names
Packit 130fc8
            `dbus_interface` : str
Packit 130fc8
                The D-Bus interface name with which to qualify the signal;
Packit 130fc8
                None (the default) matches all interface names
Packit 130fc8
            `bus_name` : str
Packit 130fc8
                A bus name for the sender, which will be resolved to a
Packit 130fc8
                unique name if it is not already; None (the default) matches
Packit 130fc8
                any sender.
Packit 130fc8
            `path` : str
Packit 130fc8
                The object path of the object which must have emitted the
Packit 130fc8
                signal; None (the default) matches any object path
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
Packit 130fc8
                time only string arguments can be matched (in particular,
Packit 130fc8
                object paths and signatures can't).
Packit 130fc8
            `named_service` : str
Packit 130fc8
                A deprecated alias for `bus_name`.
Packit 130fc8
        """
Packit 130fc8
        self._require_main_loop()
Packit 130fc8
Packit 130fc8
        named_service = keywords.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 add_signal_receiver '
Packit 130fc8
                 'by name is deprecated: please use positional parameters',
Packit 130fc8
                 DeprecationWarning, stacklevel=2)
Packit 130fc8
Packit 130fc8
        match = SignalMatch(self, bus_name, path, dbus_interface,
Packit 130fc8
                            signal_name, handler_function, **keywords)
Packit 130fc8
Packit 130fc8
        self._signals_lock.acquire()
Packit 130fc8
        try:
Packit 130fc8
            by_interface = self._signal_recipients_by_object_path.setdefault(
Packit 130fc8
                    path, {})
Packit 130fc8
            by_member = by_interface.setdefault(dbus_interface, {})
Packit 130fc8
            matches = by_member.setdefault(signal_name, [])
Packit 130fc8
Packit 130fc8
            matches.append(match)
Packit 130fc8
        finally:
Packit 130fc8
            self._signals_lock.release()
Packit 130fc8
Packit 130fc8
        return match
Packit 130fc8
Packit 130fc8
    def _iter_easy_matches(self, path, dbus_interface, member):
Packit 130fc8
        if path is not None:
Packit 130fc8
            path_keys = (None, path)
Packit 130fc8
        else:
Packit 130fc8
            path_keys = (None,)
Packit 130fc8
        if dbus_interface is not None:
Packit 130fc8
            interface_keys = (None, dbus_interface)
Packit 130fc8
        else:
Packit 130fc8
            interface_keys = (None,)
Packit 130fc8
        if member is not None:
Packit 130fc8
            member_keys = (None, member)
Packit 130fc8
        else:
Packit 130fc8
            member_keys = (None,)
Packit 130fc8
Packit 130fc8
        for path in path_keys:
Packit 130fc8
            by_interface = self._signal_recipients_by_object_path.get(path)
Packit 130fc8
            if by_interface is None:
Packit 130fc8
                continue
Packit 130fc8
            for dbus_interface in interface_keys:
Packit 130fc8
                by_member = by_interface.get(dbus_interface, None)
Packit 130fc8
                if by_member is None:
Packit 130fc8
                    continue
Packit 130fc8
                for member in member_keys:
Packit 130fc8
                    matches = by_member.get(member, None)
Packit 130fc8
                    if matches is None:
Packit 130fc8
                        continue
Packit 130fc8
                    for m in matches:
Packit 130fc8
                        yield m
Packit 130fc8
Packit 130fc8
    def remove_signal_receiver(self, handler_or_match,
Packit 130fc8
                               signal_name=None,
Packit 130fc8
                               dbus_interface=None,
Packit 130fc8
                               bus_name=None,
Packit 130fc8
                               path=None,
Packit 130fc8
                               **keywords):
Packit 130fc8
        named_service = keywords.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 '
Packit 130fc8
                 'remove_signal_receiver by name is deprecated: please use '
Packit 130fc8
                 'positional parameters',
Packit 130fc8
                 DeprecationWarning, stacklevel=2)
Packit 130fc8
Packit 130fc8
        new = []
Packit 130fc8
        deletions = []
Packit 130fc8
        self._signals_lock.acquire()
Packit 130fc8
        try:
Packit 130fc8
            by_interface = self._signal_recipients_by_object_path.get(path,
Packit 130fc8
                                                                      None)
Packit 130fc8
            if by_interface is None:
Packit 130fc8
                return
Packit 130fc8
            by_member = by_interface.get(dbus_interface, None)
Packit 130fc8
            if by_member is None:
Packit 130fc8
                return
Packit 130fc8
            matches = by_member.get(signal_name, None)
Packit 130fc8
            if matches is None:
Packit 130fc8
                return
Packit 130fc8
Packit 130fc8
            for match in matches:
Packit 130fc8
                if (handler_or_match is match
Packit 130fc8
                    or match.matches_removal_spec(bus_name,
Packit 130fc8
                                                  path,
Packit 130fc8
                                                  dbus_interface,
Packit 130fc8
                                                  signal_name,
Packit 130fc8
                                                  handler_or_match,
Packit 130fc8
                                                  **keywords)):
Packit 130fc8
                    deletions.append(match)
Packit 130fc8
                else:
Packit 130fc8
                    new.append(match)
Packit 130fc8
Packit 130fc8
            if new:
Packit 130fc8
                by_member[signal_name] = new
Packit 130fc8
            else:
Packit 130fc8
                del by_member[signal_name]
Packit 130fc8
                if not by_member:
Packit 130fc8
                    del by_interface[dbus_interface]
Packit 130fc8
                    if not by_interface:
Packit 130fc8
                        del self._signal_recipients_by_object_path[path]
Packit 130fc8
        finally:
Packit 130fc8
            self._signals_lock.release()
Packit 130fc8
Packit 130fc8
        for match in deletions:
Packit 130fc8
            self._clean_up_signal_match(match)
Packit 130fc8
Packit 130fc8
    def _clean_up_signal_match(self, match):
Packit 130fc8
        # Now called without the signals lock held (it was held in <= 0.81.0)
Packit 130fc8
        pass
Packit 130fc8
Packit 130fc8
    def _signal_func(self, message):
Packit 130fc8
        """D-Bus filter function. Handle signals by dispatching to Python
Packit 130fc8
        callbacks kept in the match-rule tree.
Packit 130fc8
        """
Packit 130fc8
Packit 130fc8
        if not isinstance(message, SignalMessage):
Packit 130fc8
            return HANDLER_RESULT_NOT_YET_HANDLED
Packit 130fc8
Packit 130fc8
        dbus_interface = message.get_interface()
Packit 130fc8
        path = message.get_path()
Packit 130fc8
        signal_name = message.get_member()
Packit 130fc8
Packit 130fc8
        for match in self._iter_easy_matches(path, dbus_interface,
Packit 130fc8
                                             signal_name):
Packit 130fc8
            match.maybe_handle_message(message)
Packit 130fc8
Packit 130fc8
        if (dbus_interface == LOCAL_IFACE and
Packit 130fc8
            path == LOCAL_PATH and
Packit 130fc8
            signal_name == 'Disconnected'):
Packit 130fc8
            for cb in self.__call_on_disconnection:
Packit 130fc8
                try:
Packit 130fc8
                    cb(self)
Packit 130fc8
                except Exception:
Packit 130fc8
                    # basicConfig is a no-op if logging is already configured
Packit 130fc8
                    logging.basicConfig()
Packit 130fc8
                    _logger.error('Exception in handler for Disconnected '
Packit 130fc8
                        'signal:', exc_info=1)
Packit 130fc8
Packit 130fc8
        return HANDLER_RESULT_NOT_YET_HANDLED
Packit 130fc8
Packit 130fc8
    def call_async(self, bus_name, object_path, dbus_interface, method,
Packit 130fc8
                   signature, args, reply_handler, error_handler,
Packit 130fc8
                   timeout=-1.0, byte_arrays=False,
Packit 130fc8
                   require_main_loop=True, **kwargs):
Packit 130fc8
        """Call the given method, asynchronously.
Packit 130fc8
Packit 130fc8
        If the reply_handler is None, successful replies will be ignored.
Packit 130fc8
        If the error_handler is None, failures will be ignored. If both
Packit 130fc8
        are None, the implementation may request that no reply is sent.
Packit 130fc8
Packit 130fc8
        :Returns: The dbus.lowlevel.PendingCall.
Packit 130fc8
        :Since: 0.81.0
Packit 130fc8
        """
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
        if dbus_interface == LOCAL_IFACE:
Packit 130fc8
            raise DBusException('Methods may not be called on the reserved '
Packit 130fc8
                                'interface %s' % LOCAL_IFACE)
Packit 130fc8
        # no need to validate other args - MethodCallMessage ctor will do
Packit 130fc8
Packit 130fc8
        get_args_opts = dict(byte_arrays=byte_arrays)
Packit 130fc8
        if is_py2:
Packit 130fc8
            get_args_opts['utf8_strings'] = kwargs.get('utf8_strings', False)
Packit 130fc8
        elif 'utf8_strings' in kwargs:
Packit 130fc8
            raise TypeError("unexpected keyword argument 'utf8_strings'")
Packit 130fc8
Packit 130fc8
        message = MethodCallMessage(destination=bus_name,
Packit 130fc8
                                    path=object_path,
Packit 130fc8
                                    interface=dbus_interface,
Packit 130fc8
                                    method=method)
Packit 130fc8
        # Add the arguments to the function
Packit 130fc8
        try:
Packit 130fc8
            message.append(signature=signature, *args)
Packit 130fc8
        except Exception as e:
Packit 130fc8
            logging.basicConfig()
Packit 130fc8
            _logger.error('Unable to set arguments %r according to '
Packit 130fc8
                          'signature %r: %s: %s',
Packit 130fc8
                          args, signature, e.__class__, e)
Packit 130fc8
            raise
Packit 130fc8
Packit 130fc8
        if reply_handler is None and error_handler is None:
Packit 130fc8
            # we don't care what happens, so just send it
Packit 130fc8
            self.send_message(message)
Packit 130fc8
            return
Packit 130fc8
Packit 130fc8
        if reply_handler is None:
Packit 130fc8
            reply_handler = _noop
Packit 130fc8
        if error_handler is None:
Packit 130fc8
            error_handler = _noop
Packit 130fc8
Packit 130fc8
        def msg_reply_handler(message):
Packit 130fc8
            if isinstance(message, MethodReturnMessage):
Packit 130fc8
                reply_handler(*message.get_args_list(**get_args_opts))
Packit 130fc8
            elif isinstance(message, ErrorMessage):
Packit 130fc8
                error_handler(DBusException(name=message.get_error_name(),
Packit 130fc8
                                            *message.get_args_list()))
Packit 130fc8
            else:
Packit 130fc8
                error_handler(TypeError('Unexpected type for reply '
Packit 130fc8
                                        'message: %r' % message))
Packit 130fc8
        return self.send_message_with_reply(message, msg_reply_handler,
Packit 130fc8
                                        timeout,
Packit 130fc8
                                        require_main_loop=require_main_loop)
Packit 130fc8
Packit 130fc8
    def call_blocking(self, bus_name, object_path, dbus_interface, method,
Packit 130fc8
                      signature, args, timeout=-1.0,
Packit 130fc8
                      byte_arrays=False, **kwargs):
Packit 130fc8
        """Call the given method, synchronously.
Packit 130fc8
        :Since: 0.81.0
Packit 130fc8
        """
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
        if dbus_interface == LOCAL_IFACE:
Packit 130fc8
            raise DBusException('Methods may not be called on the reserved '
Packit 130fc8
                                'interface %s' % LOCAL_IFACE)
Packit 130fc8
        # no need to validate other args - MethodCallMessage ctor will do
Packit 130fc8
Packit 130fc8
        get_args_opts = dict(byte_arrays=byte_arrays)
Packit 130fc8
        if is_py2:
Packit 130fc8
            get_args_opts['utf8_strings'] = kwargs.get('utf8_strings', False)
Packit 130fc8
        elif 'utf8_strings' in kwargs:
Packit 130fc8
            raise TypeError("unexpected keyword argument 'utf8_strings'")
Packit 130fc8
Packit 130fc8
        message = MethodCallMessage(destination=bus_name,
Packit 130fc8
                                    path=object_path,
Packit 130fc8
                                    interface=dbus_interface,
Packit 130fc8
                                    method=method)
Packit 130fc8
        # Add the arguments to the function
Packit 130fc8
        try:
Packit 130fc8
            message.append(signature=signature, *args)
Packit 130fc8
        except Exception as e:
Packit 130fc8
            logging.basicConfig()
Packit 130fc8
            _logger.error('Unable to set arguments %r according to '
Packit 130fc8
                          'signature %r: %s: %s',
Packit 130fc8
                          args, signature, e.__class__, e)
Packit 130fc8
            raise
Packit 130fc8
Packit 130fc8
        # make a blocking call
Packit 130fc8
        reply_message = self.send_message_with_reply_and_block(
Packit 130fc8
            message, timeout)
Packit 130fc8
        args_list = reply_message.get_args_list(**get_args_opts)
Packit 130fc8
        if len(args_list) == 0:
Packit 130fc8
            return None
Packit 130fc8
        elif len(args_list) == 1:
Packit 130fc8
            return args_list[0]
Packit 130fc8
        else:
Packit 130fc8
            return tuple(args_list)
Packit 130fc8
Packit 130fc8
    def call_on_disconnection(self, callable):
Packit 130fc8
        """Arrange for `callable` to be called with one argument (this
Packit 130fc8
        Connection object) when the Connection becomes
Packit 130fc8
        disconnected.
Packit 130fc8
Packit 130fc8
        :Since: 0.83.0
Packit 130fc8
        """
Packit 130fc8
        self.__call_on_disconnection.append(callable)