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
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]
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
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
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
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]
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
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
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)
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