示例#1
0
    def _iterate_in_parameters(cls, member, signature):
        """Iterate over input parameters."""
        # Get type hints for parameters.
        direction = DBusSpecification.DIRECTION_IN
        type_hints = get_type_hints(member)

        # Iterate over method parameters, skip cls.
        for name in list(signature.parameters)[1:]:
            # Check the kind of the parameter
            kind = signature.parameters[name].kind

            # Ignore **kwargs and all arguments after * and *args
            # if the method supports additional arguments.
            if kind in (Parameter.VAR_KEYWORD, Parameter.KEYWORD_ONLY) \
                    and are_additional_arguments_supported(member):
                continue

            if kind != Parameter.POSITIONAL_OR_KEYWORD:
                raise DBusSpecificationError(
                    "Only positional or keyword arguments are allowed."
                )

            # Check if the type is defined.
            if name not in type_hints:
                raise DBusSpecificationError(
                    "Undefined type of parameter '{}'.".format(name)
                )

            yield name, type_hints[name], direction
示例#2
0
    def _generate_signal(cls, member, member_name):
        """Generate signal defined by a class member.

        :param member: a dbus_signal object.
        :param member_name: a name of the signal
        :return: a signal element

        raises DBusSpecificationError: if signal has defined return type
        """
        element = cls.xml_generator.create_signal(member_name)
        method = member.definition

        if not method:
            return element

        for name, type_hint, direction in cls._iterate_parameters(method):
            # Only input parameters can be defined.
            if direction == DBusSpecification.DIRECTION_OUT:
                raise DBusSpecificationError(
                    "Signal {} has defined return type.".format(member_name))

            # All parameters are exported as output parameters
            # (see specification).
            direction = DBusSpecification.DIRECTION_OUT
            parameter = cls.xml_generator.create_parameter(
                name, get_dbus_type(type_hint), direction)
            cls.xml_generator.add_child(element, parameter)

        return element
示例#3
0
    def register_object(cls, connection, object_path, object_xml,
                        callback, callback_args=()):
        """Register an object on DBus."""
        node_info = Gio.DBusNodeInfo.new_for_xml(
            object_xml
        )
        method_call_closure = partial(
            cls._object_callback,
            user_data=(callback, callback_args)
        )
        registrations = []

        if not node_info.interfaces:
            raise DBusSpecificationError(
                "No interfaces for registration."
            )

        for interface_info in node_info.interfaces:
            registration_id = connection.register_object(
                object_path,
                interface_info,
                method_call_closure,
                None,
                None
            )
            registrations.append(registration_id)

        return partial(
            cls._unregister_object,
            connection,
            registrations
        )
示例#4
0
    def _generate_property(cls, member, member_name):
        """Generate DBus property defined by class member.

        :param member: a property object
        :param member_name: a property name
        :return: a property element

        raises DBusSpecificationError: if the property is invalid
        """
        access = None
        type_hint = None

        try:
            # Process the setter.
            if member.fset:
                [(_, type_hint, _)] = cls._iterate_parameters(member.fset)
                access = DBusSpecification.ACCESS_WRITE

            # Process the getter.
            if member.fget:
                [(_, type_hint, _)] = cls._iterate_parameters(member.fget)
                access = DBusSpecification.ACCESS_READ

        except ValueError:
            raise DBusSpecificationError(
                "Undefined type of DBus property '{}'.".format(member_name)
            ) from None

        # Property has both.
        if member.fget and member.fset:
            access = DBusSpecification.ACCESS_READWRITE

        if access is None:
            raise DBusSpecificationError(
                "DBus property '{}' is not accessible.".format(member_name)
            )

        return cls.xml_generator.create_property(
            member_name,
            get_dbus_type(type_hint),
            access
        )
示例#5
0
    def _iterate_parameters(cls, member):
        """Iterate over method parameters.

        For every parameter returns its name, a type hint and a direction.

        :param member: a method object
        :return: an iterator

        raises DBusSpecificationError: if parameters are invalid
        """
        # Get type hints for parameters.
        type_hints = get_type_hints(member)

        # Get method signature.
        signature = inspect.signature(member)

        # Iterate over method parameters, skip cls.
        for name in list(signature.parameters)[1:]:
            # Check the kind of the parameter
            kind = signature.parameters[name].kind
            if kind != Parameter.POSITIONAL_OR_KEYWORD:
                raise DBusSpecificationError(
                    "Only positional or keyword arguments are allowed.")

            # Check if the type is defined.
            if name not in type_hints:
                raise DBusSpecificationError(
                    "Parameter {} doesn't have defined type.".format(name))

            yield name, type_hints[name], DBusSpecification.DIRECTION_IN

        # Is the return type defined?
        if signature.return_annotation is signature.empty:
            return

        # Is the return type other than None?
        if signature.return_annotation is None:
            return

        yield (DBusSpecification.RETURN_PARAMETER, signature.return_annotation,
               DBusSpecification.DIRECTION_OUT)
示例#6
0
    def _iterate_out_parameters(cls, member, signature):
        """Iterate over output parameters."""
        name = DBusSpecification.RETURN_PARAMETER
        direction = DBusSpecification.DIRECTION_OUT
        type_hint = signature.return_annotation

        # Is the return type defined?
        if type_hint is signature.empty:
            return

        # Is the return type other than None?
        if type_hint is None:
            return

        # Generate multiple output arguments if requested.
        if getattr(member, RETURNS_MULTIPLE_ARGUMENTS_ATTRIBUTE, False):
            # The return type has to be a tuple.
            if not is_base_type(type_hint, Tuple):
                raise DBusSpecificationError(
                    "Expected a tuple of multiple arguments."
                )

            # The return type has to contain multiple arguments.
            type_args = get_type_arguments(type_hint)

            if len(type_args) < 2:
                raise DBusSpecificationError(
                    "Expected a tuple of more than one argument."
                )

            # Iterate over types in the tuple
            for i, type_arg in enumerate(type_args):
                yield "{}_{}".format(name, i), type_arg, direction

            return

        # Otherwise, return only one output argument.
        yield name, type_hint, direction
示例#7
0
def get_xml(obj):
    """Return XML specification of an object.

    :param obj: an object decorated with @dbus_interface or @dbus_class
    :return: a string with XML specification
    """
    xml_specification = getattr(obj, DBUS_XML_ATTRIBUTE, None)

    if xml_specification is None:
        raise DBusSpecificationError(
            "XML specification is not defined at '{}'.".format(
                DBUS_XML_ATTRIBUTE))

    return xml_specification
示例#8
0
    def _find_properties_specs(self, obj):
        """Find specifications of DBus properties.

        :param obj: an object with DBus properties
        :return: a map of property names and their specifications
        """
        specification = DBusSpecification.from_xml(get_xml(obj))
        properties_specs = {}

        for member in specification.members:
            if not isinstance(member, DBusSpecification.Property):
                continue

            if member.name in properties_specs:
                raise DBusSpecificationError(
                    "DBus property '{}' is defined in more than "
                    "one interface.".format(member.name))

            properties_specs[member.name] = member

        return properties_specs
示例#9
0
    def _generate_interface(cls, interface_cls, interfaces, interface_name):
        """Generate interface defined by given class.

        :param interface_cls: a class object that defines the interface
        :param interfaces: a dictionary of implemented interfaces
        :param interface_name: a name of the new interface
        :return: a new interface element

        :raises DBusSpecificationError: if a class member cannot be exported
        """
        interface = cls.xml_generator.create_interface(interface_name)

        # Search class members.
        for member_name, member in inspect.getmembers(interface_cls):
            # Check it the name is exportable.
            if not cls._is_exportable(member_name):
                continue

            # Skip names already defined in implemented interfaces.
            if cls._is_defined(interfaces, member_name):
                continue

            # Generate XML element for exportable member.
            if cls._is_signal(member):
                element = cls._generate_signal(member, member_name)
            elif cls._is_property(member):
                element = cls._generate_property(member, member_name)
            elif cls._is_method(member):
                element = cls._generate_method(member, member_name)
            else:
                raise DBusSpecificationError(
                    "Unsupported definition of DBus member '{}'.".format(
                        member_name
                    )
                )

            # Add generated element to the interface.
            cls.xml_generator.add_child(interface, element)

        return interface