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
Example #2
0
 def _convert_newtype_args(self, node: ast3.Call):
   if len(node.args) != 2:
     msg = "Wrong args: expected NewType(name, [(field, type), ...])"
     raise ParseError(msg)
   name, typ = node.args
   typ = self.convert_node(typ)
   node.args = [name.s, typ]
Example #3
0
    def visit_Call(self, node: ast.Call) -> Union[ast.Call, ast.Constant]:
        """Evaluate function and return ast.Constant if all arguments are bound.

        Args:
            node: Function to evaluate.

        Returns:
            Evaluated value.

        Raises:
            PulseError: When unsupported or unsafe function is specified.
        """
        if not isinstance(node.func, ast.Name):
            raise PulseError("Unsafe expression is detected.")
        node.args = [self.visit(arg) for arg in node.args]
        if all(isinstance(arg, (ast.Constant, ast.Num)) for arg in node.args):
            if node.func.id not in self._math_ops.keys():
                raise PulseError("Function %s is not supported." % node.func.id)
            _args = [arg.n for arg in node.args]
            _val = self._math_ops[node.func.id](*_args)
            if not _val.imag:
                _val = _val.real
            val = ast.Constant(n=_val)
            return ast.copy_location(val, node)
        return node
Example #4
0
    def validate_callable_arguments(self, call: ast.Call, callable_target: Callable) -> bool:
        if (callable_target.allow_starred_argument
                and not hasattr(call, 'checked_starred_args')
                and len(call.args) > len(callable_target.args_without_default)):
            args = self.parse_to_node(str(Type.sequence.default_value), call)
            args.elts = call.args
            call.args = [args]
            call.checked_starred_args = True

        len_call_args = len(call.args)
        callable_required_args = len(callable_target.args_without_default)

        if len_call_args > len(callable_target.args):
            unexpected_arg = call.args[len(callable_target.args)]
            self._log_error(
                CompilerError.UnexpectedArgument(unexpected_arg.lineno, unexpected_arg.col_offset)
            )
            return False
        elif len_call_args < callable_required_args:
            missed_arg = list(callable_target.args)[len(call.args)]
            self._log_error(
                CompilerError.UnfilledArgument(call.lineno, call.col_offset, missed_arg)
            )
            return False

        if isinstance(callable_target, IBuiltinMethod) and callable_target.requires_reordering:
            if not hasattr(call, 'was_reordered') or not call.was_reordered:
                callable_target.reorder(call.args)
                call.was_reordered = True

        if callable_required_args <= len_call_args < len(callable_target.args):
            included_args = len_call_args - callable_required_args
            call.args.extend(callable_target.defaults[included_args:])
        return True
    def _lift_call_args(self, node: ast.Call):
        # lift each arg
        newargs = []
        for arg in node.args:
            if isinstance(arg, ast.Name):  # just a name, no need to transform
                newargs.append(arg)
                continue

            # Don't lift constants.
            #
            # TODO -- fix other passes that depend on this being a var
            #
            # if isinstance(node.func, ast.Attribute):
            #     if node.func.attr == 'sleep':
            #         if isinstance(arg, ast.Num):
            #             newargs.append(arg.n)
            #             continue

            # lift anything that's not a name
            argVar = gensym.gensym("a")
            # important to visit the arg node, since it could itself
            # have a call
            lifted = ast.Assign(targets=[ast.Name(id=argVar, ctx=ast.Store())],
                                value=self.visit(arg))
            lifted.color = True  # TODO: need to figure out the right color for this node
            ast.copy_location(lifted, node)
            self.pre_statements.append(lifted)
            replacement = ast.copy_location(
                ast.Name(id=argVar, ctx=ast.Load()), node)
            newargs.append(replacement)

        # replace args
        node.args = newargs
Example #6
0
 def _convert_collections_namedtuple_args(self, node: ast3.Call):
   if len(node.args) != 2:
     msg = "Wrong args: expected namedtuple(name, [field, ...])"
     raise ParseError(msg)
   name, fields = node.args
   fields = self.convert_node(fields)
   fields = [(types.string_value(n), pytd.AnythingType()) for n in fields]
   node.args = [name.s, fields]  # pytype: disable=attribute-error
Example #7
0
 def _convert_typing_namedtuple_args(self, node: ast3.Call):
   # TODO(mdemello): handle NamedTuple("X", a=int, b=str, ...)
   if len(node.args) != 2:
     msg = "Wrong args: expected NamedTuple(name, [(field, type), ...])"
     raise ParseError(msg)
   name, fields = node.args
   fields = self.convert_node(fields)
   fields = [(types.string_value(n), t) for (n, t) in fields]
   node.args = [name.s, fields]
Example #8
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
Example #9
0
    def visit_Call(self, node: ast.Call) -> Any:
        if type(node.func) == ast.Attribute and node.func.attr == "get":
            return node

        fun_name = node.func.id
        if len(
                node.args
        ) > 1 and fun_name not in self.defined_class_names and fun_name != "transaction":
            node.args = [
                ast.Call(func=ast.Name(id=self.env[fun_name], ctx=ast.Load()),
                         args=[self.visit(arg) for arg in node.args],
                         keywords=[])
            ]
        return node
Example #10
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)
Example #11
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