Example #1
0
    def visit_Call(self, node):
        positional_final_wildcard = False
        for i, n in enumerate(node.args):
            if astcheck.is_ast_like(n, ast.Name(id=MULTIWILDCARD_NAME)):
                if i + 1 == len(node.args):
                    # Last positional argument - wildcard may extend to kwargs
                    positional_final_wildcard = True

                node.args = (self._visit_list(node.args[:i]) +
                             astcheck.listmiddle() +
                             self._visit_list(node.args[i + 1:]))

                # Don't try to handle multiple multiwildcards
                break

        kwargs_are_subset = False
        if positional_final_wildcard and node.starargs is None:
            del node.starargs  # Accept any (or none) *args
            # f(a, ??) -> wildcarded kwargs as well
            kwargs_are_subset = True

        if kwargs_are_subset or any(k.arg == MULTIWILDCARD_NAME
                                    for k in node.keywords):
            template_keywords = [
                self.visit(k) for k in node.keywords
                if k.arg != MULTIWILDCARD_NAME
            ]

            def kwargs_checker(sample_keywords, path):
                sample_kwargs = {k.arg: k.value for k in sample_keywords}

                for k in template_keywords:
                    if k.arg == MULTIWILDCARD_NAME:
                        continue
                    if k.arg in sample_kwargs:
                        astcheck.assert_ast_like(sample_kwargs[k.arg], k.value,
                                                 path + [k.arg])
                    else:
                        raise astcheck.ASTMismatch(path, "(missing)",
                                                   "keyword arg %s" % k.arg)

            if template_keywords:
                node.keywords = kwargs_checker
            else:
                # Shortcut if there are no keywords to check
                del node.keywords

            # Accepting arbitrary keywords, so don't check absence of **kwargs
            if node.kwargs is None:
                del node.kwargs

        # In block contexts, we want to avoid checking empty lists (for optional
        # nodes), but here, an empty list should mean that there are no
        # arguments in that group. So we need to override the behaviour in
        # generic_visit
        if node.args == []:
            node.args = must_not_exist_checker
        if getattr(node, "keywords", None) == []:
            node.keywords = must_not_exist_checker
        return self.generic_visit(node)
Example #2
0
    def visit_arguments(self, node):
        positional_final_wildcard = False
        for i, a in enumerate(node.args):
            if a.arg == MULTIWILDCARD_NAME:
                from_end = len(node.args) - (i + 1)
                if from_end == 0:
                    # Last positional argument - wildcard may extend to other groups
                    positional_final_wildcard = True

                args = (self._visit_list(node.args[:i]) +
                        astcheck.listmiddle() +
                        self._visit_list(node.args[i + 1:]))
                break
        else:
            if node.args:
                args = self._visit_list(node.args)
            else:
                args = must_not_exist_checker

        defaults = [
            (a.arg, self.visit(d))
            for a, d in zip(node.args[-len(node.defaults):], node.defaults)
            if a.arg not in {WILDCARD_NAME, MULTIWILDCARD_NAME}
        ]

        if node.vararg is None:
            if positional_final_wildcard:
                vararg = None
            else:
                vararg = must_not_exist_checker
        else:
            vararg = self.visit(node.vararg)

        kwonly_args_dflts = [(self.visit(a),
                              (d if d is None else self.visit(d)))
                             for a, d in zip(node.kwonlyargs, node.kw_defaults)
                             if a.arg != MULTIWILDCARD_NAME]

        koa_subset = (positional_final_wildcard and vararg is None and
                      (not node.kwonlyargs)) or any(a.arg == MULTIWILDCARD_NAME
                                                    for a in node.kwonlyargs)

        if node.kwarg is None:
            if koa_subset:
                kwarg = None
            else:
                kwarg = must_not_exist_checker
        else:
            kwarg = self.visit(node.kwarg)

        return ArgsDefChecker(
            args=args,
            defaults=defaults,
            vararg=vararg,
            kwonly_args_dflts=kwonly_args_dflts,
            koa_subset=koa_subset,
            kwarg=kwarg,
        )
Example #3
0
    def prune_wildcard_body(self, node, attrname, must_exist=False):
        """Prunes a code block (e.g. function body) if it is a wildcard"""
        body = getattr(node, attrname, [])

        def _is_multiwildcard(n):
            return astcheck.is_ast_like(
                n, ast.Expr(value=ast.Name(id=MULTIWILDCARD_NAME)))

        if len(body) == 1 and _is_multiwildcard(body[0]):
            setattr(node, attrname, must_exist_checker)
            return

        # Find a ?? node within the block, and replace it with listmiddle
        for i, n in enumerate(body):
            if _is_multiwildcard(n):
                newbody = body[:i] + astcheck.listmiddle() + body[i + 1:]
                setattr(node, attrname, newbody)