コード例 #1
0
def _verify_decorator_correctness(
    sig: Signature, parameter_selector: List[Union[int, str]],
    exception_factory: Union[Type[BaseException], Callable[..., BaseException]]
) -> None:
    """
    Verifies that all selected parameters actually exist for a method.

    :param sig: A method signature
    :param parameter_selector: a selector that indicates parameter(s) of the method. This is a list with a combination
    of ints for positional parameters or strings for keyword parameters
    :raises MalformedDecoratorError: If one or more of the selected parameters does not exist in the method
    """
    for parameter in parameter_selector:
        if isinstance(parameter, int):
            if parameter < 0:
                raise MalformedDecoratorError(
                    f'Positional parameter {parameter} out of range')
            try:
                sig.bind_partial(*([None] * (parameter + 1)))
            except TypeError:
                raise MalformedDecoratorError(
                    f'Positional parameter {parameter} out of range')
        elif isinstance(parameter, str):
            try:
                sig.bind_partial(**{parameter: None})
            except TypeError:
                raise MalformedDecoratorError(
                    f'Unknown keyword parameter {parameter}')
        else:
            raise MalformedDecoratorError(
                f'Parameter selector {parameter} has unknown type {type(parameter)}'
            )

    if isinstance(exception_factory, type) and issubclass(
            exception_factory, BaseException):
        pass
    elif isinstance(exception_factory, FunctionType):
        pass
    else:
        raise MalformedDecoratorError(
            f'Incorrect type for exception_factory: {type(exception_factory)}')
コード例 #2
0
ファイル: yaml.py プロジェクト: kosiokarchev/clipppy
    def get_args_kwargs(cls, loader: yaml.Loader, node: yaml.Node,
                        signature: Signature = free_signature) -> tp.Optional[BoundArguments]:
        if isinstance(node, yaml.ScalarNode):
            try:
                # This sometimes fails because of ruamel/yaml/resolver.py line 370
                node.tag = loader.resolver.resolve(yaml.ScalarNode, node.value, [True, False])
            except IndexError:
                node.tag = loader.DEFAULT_SCALAR_TAG
            val = loader.construct_object(node)
            if val is None:
                val = loader.yaml_constructors[node.tag](loader, node)
            return None if val is None else signature.bind(val)
        elif isinstance(node, yaml.SequenceNode):
            bound = signature.bind(*node.value)
            args = []
            subnodes = node.value
        elif isinstance(node, yaml.MappingNode):
            # Construct the keys
            kwargs = {loader.construct_object(key, deep=True): val for key, val in node.value}

            args = kwargs.setdefault('__args', yaml.SequenceNode('tag:yaml.org,2002:seq', []))
            args_is_seq = isinstance(args, yaml.SequenceNode) and args.tag == 'tag:yaml.org,2002:seq'
            if args_is_seq:
                kwargs['__args'] = args.value

            # Extract nodes in order (nodes are not iterable, so only "flattens" __args)
            subnodes = list(flatten(kwargs.values()))

            __args = kwargs.pop('__args')
            bound = signature.bind_partial(*(__args if args_is_seq else ()), **kwargs)
        else:
            raise ValueError(f'Invalid node type, {node}')

        # Experimental
        cls.fix_signature(bound)

        # Construct nodes in yaml order
        subnode_values = {n: loader.construct_object(n, deep=True)
                          for n in subnodes}

        for key, val in bound.arguments.items():
            bound.arguments[key] = (
                signature.parameters[key].kind == Parameter.VAR_POSITIONAL
                and (subnode_values[n] for n in val)
                or signature.parameters[key].kind == Parameter.VAR_KEYWORD
                and {name: subnode_values[n] for name, n in val.items()}
                or subnode_values[val]
            )

        if args and args in subnode_values:
            return bound.signature.bind(*subnode_values[args], **bound.kwargs)

        return bound
コード例 #3
0
def gen_args(sig: inspect.Signature) -> inspect.BoundArguments:
    args = sig.bind_partial()
    space = context_statespace()
    for param in sig.parameters.values():
        smt_name = param.name + space.uniq()
        proxy_maker = lambda typ, **kw: proxy_for_type(
            typ, smt_name, allow_subtypes=True, **kw)
        has_annotation = (param.annotation != inspect.Parameter.empty)
        value: object
        if param.kind == inspect.Parameter.VAR_POSITIONAL:
            if has_annotation:
                varargs_type = List[param.annotation]  # type: ignore
                value = proxy_maker(varargs_type)
            else:
                value = proxy_maker(List[Any])
        elif param.kind == inspect.Parameter.VAR_KEYWORD:
            if has_annotation:
                varargs_type = Dict[str, param.annotation]  # type: ignore
                value = cast(dict, proxy_maker(varargs_type))
                # Using ** on a dict requires concrete string keys. Force
                # instiantiation of keys here:
                value = {k.__str__(): v for (k, v) in value.items()}
            else:
                value = proxy_maker(Dict[str, Any])
        else:
            is_self = param.name == 'self'
            # Object parameters should meet thier invariants iff they are not the
            # class under test ("self").
            meet_class_invariants = not is_self
            allow_subtypes = not is_self
            if has_annotation:
                value = proxy_for_type(param.annotation, smt_name,
                                       meet_class_invariants, allow_subtypes)
            else:
                value = proxy_for_type(cast(type, Any), smt_name,
                                       meet_class_invariants, allow_subtypes)
        debug('created proxy for', param.name, 'as type:', type(value))
        args.arguments[param.name] = value
    return args
コード例 #4
0
    def to_bound_arguments(
        self,
        signature: inspect.Signature,
        partial: bool = False,
    ) -> inspect.BoundArguments:
        """
        Generates an instance of :class:inspect.BoundArguments` for a given
        :class:`inspect.Signature`.
        Does not raise if invalid or incomplete arguments are provided, as the
        underlying implementation uses :meth:`inspect.Signature.bind_partial`.

        :param signature: an instance of :class:`inspect.Signature` to which
            :paramref:`.CallArguments.args` and
            :paramref:`.CallArguments.kwargs` will be bound.
        :param partial: does not raise if invalid or incomplete arguments are
            provided, as the underlying implementation uses
            :meth:`inspect.Signature.bind_partial`
        :returns: an instance of :class:`inspect.BoundArguments` to which
            :paramref:`.CallArguments.args` and
            :paramref:`.CallArguments.kwargs` are bound.
        """
        return signature.bind_partial(*self.args, **self.kwargs) \
            if partial \
            else signature.bind(*self.args, **self.kwargs)
コード例 #5
0
    def __init__(self,
                 sig: Signature,
                 args,
                 kwargs,
                 has_returns_from_previous_task=True):
        args = tuple(args)
        kwargs = dict(kwargs)
        self.sig = sig

        params = sig.parameters
        # Check if the method signature contains
        # any VAR_KEYWORD (i.e., **kwargs)
        self.varkeyword = False
        for param in params.values():
            if param.kind == param.VAR_KEYWORD:
                self.varkeyword = True

        add_later = {}
        try:
            # If the first positional argument is a
            # dict (i.e., result of a previous task), we need to process it.
            if isinstance(args[0], dict) and has_returns_from_previous_task:
                original_args = args[0]
                # Remove the RETURN_KEYS_KEY entry
                if RETURN_KEYS_KEY in original_args:
                    del original_args[RETURN_KEYS_KEY]
                # Partially bind the remaining arguments
                ba = sig.bind_partial(*args[1:]).arguments
                for k, v in original_args.items():
                    # Add k,v pairs from the dictionary that are not in the
                    # partially bound args
                    if k not in ba:
                        # But only if the keys exist in the arguments of the
                        # method signature, or if a varkeyword(e.g. **kwargs)
                        # appeared in the signature
                        if (k in params) or self.varkeyword:
                            if k not in kwargs:
                                kwargs[k] = v
                        else:
                            # Otherwise, we still need to add this parameter
                            # later, adter the method is called
                            add_later[k] = v
                # Remove the dict from the positional arguments
                args = args[1:]
        except IndexError:
            pass

        remove_from_kwargs = {}
        if not self.varkeyword:
            remove_from_kwargs = {
                k: v
                for k, v in kwargs.items() if k not in params.keys()
            }

        # Pass in the kwargs that don't appear in the original app signature
        # to be later used possibly by other apps
        add_later.update(remove_from_kwargs)
        # and remove them from the kwargs
        for k in remove_from_kwargs.keys():
            del kwargs[k]

        # remove keys from kwargs that are bound by the positional args
        bound_args = sig.bind_partial(*args).arguments
        for k in bound_args.keys():
            if k in kwargs:
                del kwargs[k]

        self.args = args
        self.kwargs = kwargs

        self.return_args = dict(kwargs)
        self.return_args.update(bound_args)
        self.return_args.update(add_later)

        self._apply_indirect()
コード例 #6
0
    def fill_signature(self, sig: inspect.Signature) -> inspect.BoundArguments:
        # TODO: fill in **options arg
        # TODO: log more about untyped values, mismatch types, etc.
        # TODO: support automatically convert str to Path.
        params_type_args = []
        kwargs = {}
        missing_params = []
        var_positional_name = None
        for name, param in sig.parameters.items():
            if param.annotation == Args:
                params_type_args.append(name)
            elif name in self.named:
                # Matching arg name and param name: try to fill in this param
                dtype = None
                as_list = False
                if param.annotation in [int, float, bool]:
                    dtype = param.annotation
                elif param.annotation == list or get_origin(
                        param.annotation) == list:
                    as_list = True
                    if get_args(param.annotation) is not None and len(
                            get_args(param.annotation)) > 0:
                        if get_args(param.annotation)[0] in [int, float, bool]:
                            dtype = get_args(param.annotation)[0]

                if not as_list:
                    kwargs[name] = self.named[name].as_dtype(dtype)
                else:
                    kwargs[name] = self.named[name].as_list(dtype)
            elif param.kind == inspect.Parameter.VAR_POSITIONAL:
                var_positional_name = name
            elif param.default == inspect.Parameter.empty:
                missing_params.append(name)

        if len(params_type_args) == 1:
            # If exactly one parameter has the type of Args, bound this object to that parameter
            logger.info(f"Binding the entire args to {params_type_args[0]}")
            ret = sig.bind_partial(**{params_type_args[0]: self})
        else:
            # Otherwise, try to assign fields from arguments to the parameters with appropriate types
            # Warn about missing params
            if len(missing_params) > 0:
                logger.warning(
                    f"The following parameters may be missing: {missing_params}"
                )

            # Debug about unused args
            unused_args = [k for k in self.named if k not in kwargs]
            if len(unused_args) > 0:
                logger.info(
                    f"The following named arguments are not used: {unused_args}"
                )

            # If there is a *param parameter not already filled, put the free arguments in
            if var_positional_name is not None:
                kwargs[var_positional_name] = [arg.auto_ for arg in self.free]
            else:
                logger.info(
                    f"The following free arguments are not used: {self.free}")

            ret = sig.bind_partial(**kwargs)
        ret.apply_defaults()
        return ret