def bind_partial(self, *args, **kwds): """ Binds the provided positional and keyword arguments and returns a new ``Function`` object with an updated signature. """ # We only need the signature, so clean the function body before eval'ing. empty_func = self.replace(tree=replace_fields(self.tree, body=[ast.Pass()])) signature = inspect.signature(empty_func.eval()) bargs = signature.bind_partial(*args, **kwds) # Remove the bound arguments from the function AST bound_argnames = set(bargs.arguments.keys()) new_tree = filter_function_def(self.tree, bound_argnames) # Add assignments for bound parameters assignments = [] gen_sym = GenSym.for_tree(new_tree) new_bindings = {} for name, value in bargs.arguments.items(): node, gen_sym, binding = reify_unwrapped(value, gen_sym) new_bindings.update(binding) assignments.append(ast.Assign(targets=[ast.Name(id=name, ctx=ast.Store())], value=node)) new_globals = dict(self.globals) new_globals.update(new_bindings) new_tree = replace_fields(new_tree, body=assignments + new_tree.body) return Function(new_tree, new_globals, self.closure_vals, self._compiler_flags)
def test_replace_fields(): node = ast.Name(id='x', ctx=ast.Load()) new_node = replace_fields(node, id='y') assert new_node is not node assert new_node.id == 'y' and type(new_node.ctx) == ast.Load new_node = replace_fields(node, id='x') # no new object is created if the new value is the same as the old value assert new_node is node assert new_node.id == 'x' and type(new_node.ctx) == ast.Load
def handle_YieldFrom(state, node, ctx): state, result = _peval_expression(state, node.value, ctx) # We cannot evaluate a yield expression, # so just wrap whatever we've got in a node and return. state, new_value = map_reify(state, result) return state, replace_fields(node, value=new_value)
def increment(node, walk_field, **kwds): if isinstance(node, ast.Assign): return replace_fields(node, targets=node.targets, value=walk_field(node.value)) elif isinstance(node, ast.Num): return ast.Num(n=node.n + 1) else: return node
def _handle_loop(node, state, ctx, visit_after, visiting_after, walk_field, **_): if not visiting_after: # Need to traverse fields explicitly since for the purposes of _replace_returns(), # the body of `orelse` field is not inside a loop. state = state.update(loop_nesting_ctr=state.loop_nesting_ctr + 1) state, new_body = walk_field(state, node.body, block_context=True) state = state.update(loop_nesting_ctr=state.loop_nesting_ctr - 1) state, new_orelse = walk_field(state, node.orelse, block_context=True) visit_after() return state, replace_fields(node, body=new_body, orelse=new_orelse) else: # If there was a return inside a loop, append a conditional break # to propagate the return otside all nested loops if state.return_inside_a_loop: new_nodes = [ node, ast.If( test=ast.Name(id=ctx.return_flag_var), body=[ast.Break()], orelse=[])] else: new_nodes = node # if we are at root level, reset the return-inside-a-loop flag if state.loop_nesting_ctr == 0: state = state.update(return_inside_a_loop=False) return state, new_nodes
def remove_simple_assignments(node): """ Remove one assigment of the form `<variable> = <variable>` at a time, touching only the top level statements of the block. """ remaining_nodes = list(node.body) new_nodes = [] while len(remaining_nodes) > 0: cur_node = remaining_nodes.pop(0) if type(cur_node) == ast.Assign: can_remove, dest_name, src_name = _can_remove_assignment(cur_node, remaining_nodes) if can_remove: remaining_nodes = replace_name( remaining_nodes, ctx=dict(dest_name=dest_name, src_name=src_name)) else: new_nodes.append(cur_node) else: new_nodes.append(cur_node) if len(new_nodes) == len(node.body): return node return replace_fields(node, body=new_nodes)
def peval_comprehension(state, node, ctx): accum_cls = { ast.ListComp: ListAccumulator, ast.GeneratorExp: GeneratorExpAccumulator, ast.SetComp: SetAccumulator, ast.DictComp: DictAccumulator, } # variables from generators temporary mask bindings target_names = set() for generator in node.generators: if type(generator.target) == ast.Name: target_names.add(generator.target.id) else: target_names.update([elt.id for elt in generator.target.elts]) # pre-evaluate the expression elt_bindings = dict(ctx.bindings) for name in target_names: if name in elt_bindings: del elt_bindings[name] elt_ctx = ctx.update(bindings=elt_bindings) if type(node) == ast.DictComp: elt = ast.Tuple(elts=[node.key, node.value]) else: elt = node.elt state, new_elt = _peval_expression(state, elt, elt_ctx) try: state, container = _peval_comprehension( state, accum_cls[type(node)], new_elt, node.generators, ctx) evaluated = True except CannotEvaluateComprehension: evaluated = False if evaluated: return state, KnownValue(value=container) else: state, new_elt = map_reify(state, new_elt) state, new_generators = _peval_comprehension_generators(state, node.generators, ctx) if type(node) == ast.DictComp: key, value = new_elt.elts return state, replace_fields(node, key=key, value=value, generators=new_generators) else: return state, replace_fields(node, elt=new_elt, generators=new_generators)
def handle_Attribute(state, node, ctx): state, result = _peval_expression(state, node.value, ctx) if is_known_value(result): success, attr = try_get_attribute(result.value, node.attr) if success: return state, KnownValue(value=attr) state, new_value = map_reify(state, result) return state, replace_fields(node, value=new_value)
def handle_Slice(state, node, ctx): state, results = map_peval_expression(state, (node.lower, node.upper, node.step), ctx) # how do we handle None values in nodes? Technically, they are known values if all_known_values_or_none(results): lower, upper, step = [result if result is None else result.value for result in results] return state, KnownValue(value=slice(lower, upper, step)) state, new_nodes = map_reify(state, results) new_node = replace_fields(node, lower=new_nodes[0], upper=new_nodes[1], step=new_nodes[2]) return state, new_node
def remove_unreachable_statements(node, walk_field, **kwds): for attr in ('body', 'orelse'): if hasattr(node, attr): old_list = getattr(node, attr) new_list = filter_block(old_list) if new_list is not old_list: new_list = walk_field(new_list, block_context=True) kwds = {attr: new_list} node = replace_fields(node, **kwds) return node
def names_and_incremented_nums(node, state, walk_field, **kwds): if isinstance(node, ast.Assign): value_node, state = walk_field(node.value, state) new_node = replace_fields(node, targets=node.targets, value=value_node) new_state = state.update(objs=state.objs.add(node.targets[0].id)) return new_node, new_state elif isinstance(node, ast.Num): return ast.Num(n=node.n + 1), state.update(objs=state.objs.add(node.n)) else: return node, state
def handle_Set(state, node, ctx): state, elts = map_peval_expression(state, node.elts, ctx) can_eval = all_known_values(elts) if can_eval: new_set = set(elt.value for elt in elts) return state, KnownValue(value=new_set) else: state, new_elts = map_reify(state, elts) return state, replace_fields(node, elts=new_elts)
def handle_Dict(state, node, ctx): state, pairs = map_peval_expression(state, zip(node.keys, node.values), ctx) can_eval = all_known_values(pairs) if can_eval: new_dict = dict((key.value, value.value) for key, value in pairs) return state, KnownValue(value=new_dict) else: state, keys_values = map_reify(state, zip(*pairs)) new_node = replace_fields(node, keys=list(keys_values[0]), values=list(keys_values[1])) return state, new_node
def replace_by_path(obj, path, new_value): if len(path) > 1: if isinstance(ptr, str): sub_obj = getattr(obj, ptr) elif isinstance(ptr, int): sub_obj = obj[ptr] new_value = replace_by_path(sub_obj, path[1:], new_value) ptr = path[0] if isinstance(ptr, str): return replace_fields(obj, **{ptr: new_value}) elif isinstance(ptr, int): return obj[:ptr] + [new_value] + obj[ptr+1:]
def handle_Subscript(state, node, ctx): state, value_result = _peval_expression(state, node.value, ctx) state, slice_result = _peval_expression(state, node.slice, ctx) if is_known_value(value_result) and is_known_value(slice_result): success, elem = try_call_method( value_result.value, '__getitem__', args=(slice_result.value,)) if success: return state, KnownValue(value=elem) state, new_value = map_reify(state, value_result) state, new_slice = map_reify(state, slice_result) if type(new_slice) not in (ast.Index, ast.Slice, ast.ExtSlice): new_slice = ast.Index(value=new_slice) return state, replace_fields(node, value=new_value, slice=new_slice)
def handle_IfExp(state, node, ctx): state, test_value = _peval_expression(state, node.test, ctx) if is_known_value(test_value): success, bool_value = try_call(bool, args=(test_value.value,)) if success: taken_node = node.body if bool_value else node.orelse return _peval_expression(state, taken_node, ctx) state, new_body = _peval_expression(state, node.body, ctx) state, new_orelse = _peval_expression(state, node.orelse, ctx) state, new_body_node = map_reify(state, new_body) state, new_orelse_node = map_reify(state, new_orelse) return state, replace_fields( node, test=test_value, body=new_body_node, orelse=new_orelse_node)
def from_object(cls, func, ignore_decorators=False): """ Creates a ``Function`` object from an evaluated function. """ src = getsource(func) tree = ast.parse(src).body[0] if ignore_decorators: tree = replace_fields(tree, decorator_list=[]) global_values = func.__globals__ closure_vals = get_closure(func) scope = analyze_scope(tree) func_name = func.__name__ # Builtins can be either a dict or a module builtins = global_values["__builtins__"] if not isinstance(builtins, dict): builtins = dict(vars(builtins)) globals_ = {} for name in scope.globals: if name == func_name: globals_[name] = func elif name in global_values: globals_[name] = global_values[name] elif name in builtins: globals_[name] = builtins[name] elif name in closure_vals: continue else: raise NameError(name) compiler_flags = func.__code__.co_flags # We only need the flags corresponding to future features. # Also, these are the only ones supported by compile(). compiler_flags = compiler_flags & FUTURE_FLAGS return cls(tree, globals_, closure_vals, compiler_flags)
def handle_Name(node, ctx, **_): if type(node.ctx) == ast.Load and node.id == ctx.dest_name: return replace_fields(node, id=ctx.src_name) else: return node
def handle_ExtSlice(state, node, ctx): state, results = map_peval_expression(state, node.dims, ctx) if all_known_values(results): return state, KnownValue(value=tuple(result.value for result in results)) state, new_nodes = map_reify(state, results) return state, replace_fields(node, dims=new_nodes)
def mangle_outer_functions(node, **kwds): if isinstance(node, ast.FunctionDef): return replace_fields(node, name='__' + node.name) else: return node
def mangle_all_functions(node, walk_field, **kwds): if isinstance(node, ast.FunctionDef): return replace_fields( node, name='__' + node.name, body=walk_field(node.body, block_context=True)) else: return node