def visit_comprehension(self, node): new_node = gast.comprehension( target=self._visit(node.target), iter=self._visit(node.iter), ifs=self._visit(node.ifs), is_async=0, ) return ast.copy_location(new_node, node)
def visit_comprehension(self, node): new_node = gast.comprehension( target=self._visit(node.target), iter=self._visit(node.iter), ifs=self._visit(node.ifs), is_async=0, ) gast.copy_location(new_node, node) new_node.end_lineno = new_node.end_col_offset = None return new_node
def do_transform(ifelse_ast: AST) -> AST: # filter out non-ifelse statements if not isinstance(ifelse_ast, If): return ifelse_ast # convert ifelse condition branches into functions with the arguments # set to the names of the base input symbols and return values set to output symbols. # base symbols are use to generate the arguments as the full symbol might be qualified # ie A.x which is not a valid argument name: https://github.com/joeltio/bento-box/issues/37 args = list(ifelse_ast.base_in_syms.keys()) returns = list(ifelse_ast.output_syms.keys()) fn_asts = [ wrap_func_ast( name=name, args=args, block=block, returns=returns, # zip() requires the returned outputs to be iterable return_tuple=True, ) for name, block in zip(["__if_block", "__else_block"], [ifelse_ast.body, ifelse_ast.orelse]) ] # deepcopy the condition before tracing the if/else block functions to # prevent side effects tracing from interfering with the condition. condition_ast = name_ast("__if_condition") eval_condition_ast = assign_ast( targets=[condition_ast], values=[call_func_ast("deepcopy", args=[ifelse_ast.test])], ) # call if/else block functions to trace results of evaluating each branch # of the conditional if/else block functions have arguments with the same # names as symbols we have to pass in. # deepcopy to prevent input symbols from being passed by reference and # causing interference between branches https://github.com/joeltio/bento-box/issues/39 import_deepcopy_ast = import_from_ast(module="copy", names=["deepcopy"]) call_args = { a: call_func_ast( fn_name="deepcopy", args=[name_ast(a)], ) for a in args } branch_outputs = [ name_ast(n) for n in ["__if_outputs", "__else_outputs"] ] call_fn_asts = [ assign_ast( targets=[target], values=[call_func_ast(fn_ast.name, args=call_args)], ) for target, fn_ast in zip(branch_outputs, fn_asts) ] # create switch nodes for each output symbol via list comprehension plot_switch_fn = parse_ast(Plotter.switch).body[0] # g.switch(test, if_out, else_out) call_switch_ast = call_func_ast( fn_name=plot_switch_fn.name, args={ "condition": condition_ast, "true": name_ast("if_out"), "false": name_ast("else_out"), }, attr_parent=ast.convert_fn.plotter_name, ) # (symbol, ...) = [g.switch(...) for if_out, else_out in zip(if_outputs, else_outputs)] switch_asts = assign_ast( targets=[name_ast(r, ctx=Store()) for r in returns], values=[ ListComp( elt=call_switch_ast, generators=[ comprehension( target=Tuple( elts=[ name_ast("if_out"), name_ast("else_out"), ], ctx=Load(), ), iter=call_func_ast( fn_name="zip", args=branch_outputs, ), ifs=[], is_async=False, ) ], ) ], force_tuple=True, ) # wrap transformed code block as single AST node return wrap_block_ast(block=fn_asts + [import_deepcopy_ast, eval_condition_ast] + call_fn_asts + [switch_asts], )