def _fill_in_default_arguments(func: Callable,
                               call: ast.Call) -> Tuple[ast.Call, Type]:
    """Given a call and the function definition:

    * Defaults are filled in
    * A keyword argument that is positional is moved to the end
    * A follower is left to the original to help with recovering modifications to
      nested expressions.

    # TODO: Use python's signature bind to do this:
    #   https://docs.python.org/3/library/inspect.html#inspect.Signature.bind)

    Args:
        func (Callable): The function definition
        call (ast.Call): The ast call site to be modified

    Raises:
        ValueError: Missing arguments, etc.

    Returns:
        Tuple[ast.Call, Type]: The modified call site and return type.
    """
    sig = inspect.signature(func)
    i_arg = 0
    arg_array = list(call.args)
    keywords = list(call.keywords)
    for param in sig.parameters.values():
        if param.name != "self":
            if len(arg_array) <= i_arg:
                # See if they specified it as a keyword
                a, keywords = _find_keyword(keywords, param.name)
                if a is not None:
                    arg_array.append(a)  # type: ignore
                elif param.default is not param.empty:
                    a = as_literal(param.default)
                    arg_array.append(a)
                else:
                    raise ValueError(f"Argument {param.name} is required")

    # If we are making a change to the call, put in a reference back to the
    # original call.
    if len(arg_array) != len(call.args):
        old_call_ast = call
        call = copy.copy(call)
        call.args = arg_array
        call.keywords = keywords
        call._old_ast = old_call_ast  # type: ignore

    # Mark the return type - especially if it is missing
    t_info = get_type_hints(func)
    return_type = Any
    if "return" in t_info:
        return_type = t_info["return"]
    else:
        logging.getLogger(__name__).warning(
            f"Missing return annotation for {func.__name__}"
            " - assuming Any")
        return_type = Any

    return call, return_type
示例#2
0
    def visit_Call(self, node: ast.Call) -> ast.Call:
        self.found = False
        self.generic_visit(node)
        if self.found:
            node.args = [self.do_placeholder(x) for x in node.args]
            node.keywords = [(arg, self.do_placeholder(value))
                             for arg, value in node.keywords]

        return node
示例#3
0
    def visit_Call(self, node: ast.Call):
        orig_node_id = id(node)

        with self.attrsub_context(node):
            if isinstance(node.func, ast.Attribute):
                node.func = self.visit_Attribute(node.func, call_context=True)
            elif isinstance(node.func, ast.Subscript):
                node.func = self.visit_Subscript(node.func, call_context=True)
            else:
                node.func = self.visit(node.func)

        # TODO: need a way to rewrite ast of subscript args,
        #  and to process these separately from outer rewrite

        node.args = self._get_replacement_args(node.args, False)
        node.keywords = self._get_replacement_args(node.keywords, True)

        # in order to ensure that the args are processed with appropriate active scope,
        # we need to make sure not to use the active namespace scope on args (in the case
        # of a function call on an ast.Attribute).
        #
        # We do so by emitting an "enter argument list", whose handler pushes the current active
        # scope while we process each argument. The "end argument list" event will then restore
        # the active scope.
        #
        # This effectively rewrites function calls as follows:
        # f(a, b, ..., c) -> trace(f, 'enter argument list')(a, b, ..., c)
        with fast.location_of(node.func):
            node.func = fast.Call(
                func=self._emitter_ast(),
                args=[TraceEvent.before_call.to_ast(), self._get_copy_id_ast(orig_node_id)],
                keywords=fast.kwargs(
                    ret=node.func,
                    call_node_id=self._get_copy_id_ast(orig_node_id),
                ),
            )

        # f(a, b, ..., c) -> trace(f(a, b, ..., c), 'exit argument list')
        with fast.location_of(node):
            node = fast.Call(
                func=self._emitter_ast(),
                args=[TraceEvent.after_call.to_ast(), self._get_copy_id_ast(orig_node_id)],
                keywords=fast.kwargs(
                    ret=node,
                    call_node_id=self._get_copy_id_ast(orig_node_id),
                ),
            )

        return self._maybe_wrap_symbol_in_before_after_tracing(node, call_context=True, orig_node_id=orig_node_id)
示例#4
0
    def visit_Call(self, node: ast.Call):
        is_attrsub = False
        if isinstance(node.func, (ast.Attribute, ast.Subscript)):
            is_attrsub = True
            with self.attrsub_load_context():
                node.func = self.visit_Attribute_or_Subscript(
                    node.func, call_context=True)

            # TODO: need a way to rewrite ast of attribute and subscript args,
            #  and to process these separately from outer rewrite

        node.args = self._get_replacement_args(node.args, is_attrsub, False)
        node.keywords = self._get_replacement_args(node.keywords, is_attrsub,
                                                   True)

        # in order to ensure that the args are processed with appropriate active scope,
        # we need to push current active scope before processing the args and pop after
        # (pop happens on function return as opposed to in tracer)
        node.func = ast.Call(
            func=ast.Name(self.scope_pusher, ast.Load()),
            args=[node.func],
            keywords=[],
        )

        node = ast.Call(func=ast.Name(self.scope_popper, ast.Load()),
                        args=[node, ast.NameConstant(is_attrsub)],
                        keywords=[])

        if self.inside_attrsub_load_chain or not is_attrsub:
            return node

        replacement_node = ast.Call(func=ast.Name(self.end_tracer, ast.Load()),
                                    args=[node, ast.NameConstant(True)],
                                    keywords=[])
        ast.copy_location(replacement_node, node)
        return replacement_node
示例#5
0
 def visit_Call(self, node: ast.Call) -> None:
     self.generic_visit(node)
     if (getattr(node.func, "id", "") == "setup"
             or getattr(node.func, "attr", "") == "setup"):
         node.keywords = [n for n in node.keywords if not self.store(n)]
         self.setup_function = node