|
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)
|