Source code for dasbus.client.proxy

#
# Client support for DBus proxies
#
# Copyright (C) 2019  Red Hat, Inc.  All rights reserved.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
# USA
#
from abc import ABCMeta, abstractmethod
from threading import Lock

from dasbus.client.handler import ClientObjectHandler
from dasbus.client.property import PropertyProxy
from dasbus.specification import DBusSpecificationError

__all__ = [
    "AbstractObjectProxy",
    "ObjectProxy",
    "InterfaceProxy",
    "get_object_path",
    "disconnect_proxy"
]


def get_object_handler(proxy):
    """Get an object handler of the DBus proxy.

    :param proxy: a DBus proxy
    :return: a DBus proxy handler
    """
    if not isinstance(proxy, AbstractObjectProxy):
        raise TypeError("Invalid type '{}'.".format(type(proxy).__name__))

    return getattr(proxy, "_handler")


[docs]def get_object_path(proxy): """Get an object path of the remote DBus object. :param proxy: a DBus proxy :return: a DBus path """ handler = get_object_handler(proxy) return handler.object_path
[docs]def disconnect_proxy(proxy): """Disconnect the DBus proxy from the remote object. :param proxy: a DBus proxy """ handler = get_object_handler(proxy) handler.disconnect_members()
[docs]class AbstractObjectProxy(metaclass=ABCMeta): """Abstract proxy of a remote DBus object.""" __slots__ = [ "_handler", "_members", "_lock", "__weakref__" ] # Set of local instance attributes. _locals = {*__slots__} def __init__(self, message_bus, service_name, object_path, handler_factory=ClientObjectHandler, **handler_arguments): """Create a new proxy. :param message_bus: a message bus :param service_name: a DBus name of the service :param object_path: a DBus path the object :param handler_factory: a factory of a DBus client object handler :param handler_arguments: additional arguments for the handler factory """ self._handler = handler_factory( message_bus, service_name, object_path, **handler_arguments ) self._members = {} self._lock = Lock() @abstractmethod def _get_interface(self, member_name): """Get the DBus interface of the member. :param member_name: a member name :return: an interface name """ pass def _get_member(self, *key): """Find a member of the DBus object. If the member doesn't exist, we will acquire a lock and ask a handler to create it. This method is thread-safe. :param key: a member key :return: a member :raise: AttributeError if invalid """ try: return self._members[key] except KeyError: pass return self._create_member(*key) def _create_member(self, *key): """Create a member of the DBus object. If the member doesn't exist, ask a handler to create it. This method is thread-safe. :param key: a member key :return: a member :raise: DBusSpecificationError if invalid """ with self._lock: try: return self._members[key] except KeyError: pass try: member = self._handler.create_member(*key) except DBusSpecificationError as e: raise AttributeError(str(e)) from None self._members[key] = member return member def __getattr__(self, name): """Get the attribute. Called when an attribute lookup has not found the attribute in the usual places. Always call the DBus handler in this case. """ member = self._get_member(self._get_interface(name), name) if isinstance(member, PropertyProxy): return member.get() return member def __setattr__(self, name, value): """Set the attribute. Called when an attribute assignment is attempted. Call the DBus handler if the name is not a name of an instance attribute defined in _locals. """ if name in self._locals: return super().__setattr__(name, value) member = self._get_member(self._get_interface(name), name) if isinstance(member, PropertyProxy): return member.set(value) raise AttributeError( "Can't set DBus attribute '{}'.".format(name) )
[docs]class ObjectProxy(AbstractObjectProxy): """Proxy of a remote DBus object.""" __slots__ = ["_interface_names"] # Set of instance attributes. _locals = {*AbstractObjectProxy._locals, *__slots__} def __init__(self, *args, **kwargs): """Create a new proxy. :param handler: a DBus client object handler """ super().__init__(*args, **kwargs) self._interface_names = None def _get_interface(self, member_name): """Get the DBus interface of the member. The members of standard interfaces have a priority. """ if self._interface_names is None: members = reversed( self._handler.specification.members ) self._interface_names = { m.name: m.interface_name for m in members } try: return self._interface_names[member_name] except KeyError: pass raise AttributeError( "DBus object has no attribute '{}'.".format(member_name) )
[docs]class InterfaceProxy(AbstractObjectProxy): """Proxy of a remote DBus interface.""" __slots__ = ["_interface_name"] # Set of instance attributes. _locals = {*AbstractObjectProxy._locals, *__slots__} def __init__(self, message_bus, service_name, object_path, interface_name, *args, **kwargs): """Create a new proxy. :param message_bus: a message bus :param service_name: a DBus name of the service :param object_path: a DBus path the object :param handler: a DBus client object handler """ super().__init__(message_bus, service_name, object_path, *args, **kwargs) self._interface_name = interface_name def _get_interface(self, member_name): """Get the DBus interface of the member.""" return self._interface_name