def sync_properties( input_eval, input_filename, input_params, output_filename, output_params, output_param_wrap=None, ): """ Sync one property, inline to a file :param input_eval: Whether to evaluate the `param`, or just leave it :type input_eval: ```bool``` :param input_filename: Filename to find `param` from :type input_filename: ```str``` :param input_params: Locations within file of properties. Can be top level like `['a']` for `a=5` or with the `.` syntax as in `output_params`. :type input_params: ```List[str]``` :param output_filename: Filename that will be edited in place, the property within this file (to update) is selected by `output_param` :type output_filename: ```str``` :param output_params: Parameters to update. E.g., `['A.F']` for `class A: F = None`, `['f.g']` for `def f(g): pass` :type output_params: ```List[str]``` :param output_param_wrap: Wrap all input_str params with this. E.g., `Optional[Union[{output_param}, str]]` :param output_param_wrap: ```Optional[str]``` """ with open(path.realpath(path.expanduser(input_filename)), "rt") as f: input_ast = ast_parse(f.read(), filename=input_filename) with open(path.realpath(path.expanduser(output_filename)), "rt") as f: output_ast = ast_parse(f.read(), filename=output_filename) assert len(input_params) == len(output_params) for (input_param, output_param) in zip(input_params, output_params): output_ast = sync_property( input_eval, input_param, input_ast, input_filename, output_param, output_param_wrap, output_ast, ) emit.file(output_ast, output_filename, mode="wt", skip_black=False)
def test_replace_in_ast_with_val_on_non_function(self) -> None: """ Tests that `RewriteAtQuery` can actually replace a node at given location """ parsed_ast = ast_parse(class_str) rewrite_at_query = RewriteAtQuery( search="ConfigClass.dataset_name".split("."), replacement_node=AnnAssign( annotation=Name("int", Load()), simple=1, target=Name("dataset_name", Store()), value=set_value(15), expr=None, expr_target=None, expr_annotation=None, ), ) gen_ast = rewrite_at_query.visit(parsed_ast) self.assertTrue(rewrite_at_query.replaced, True) run_ast_test( self, gen_ast, ast.parse( class_str.replace('dataset_name: str = "mnist"', "dataset_name: int = 15")), )
def ground_truth(args, truth_file): """ There is but one truth. Conform. :param args: Namespace with the values of the CLI arguments :type args: ```Namespace``` :param truth_file: contains the filename of the one true source :type truth_file: ```str``` :return: Filenames and whether they were changed :rtype: ```OrderedDict``` """ arg2parse_emit_type = { "argparse_function": (parse.argparse_ast, emit.argparse_function, FunctionDef), "class": (parse.class_, emit.class_, ClassDef), "function": (parse.function, emit.function, FunctionDef), } parse_func, emit_func, type_wanted = arg2parse_emit_type[args.truth] search = _get_name_from_namespace(args, args.truth).split(".") with open(truth_file, "rt") as f: true_ast = ast_parse(f.read(), filename=truth_file) original_node = find_in_ast(search, true_ast) gold_ir = parse_func( original_node, **_default_options(node=original_node, search=search, type_wanted=type_wanted)()) effect = OrderedDict() # filter(lambda arg: arg != args.truth, arg2parse_emit_type.keys()): for fun_name, (parse_func, emit_func, type_wanted) in arg2parse_emit_type.items(): search = list( strip_split(_get_name_from_namespace(args, fun_name), ".")) filenames = getattr(args, pluralise(fun_name)) assert isinstance( filenames, (list, tuple)), "Expected Union[list, tuple] got {!r}".format( type(filenames).__name__) effect.update( map( lambda filename: _conform_filename( filename=filename, search=search, emit_func=emit_func, replacement_node_ir=gold_ir, type_wanted=type_wanted, ), filenames, )) return effect
def _conform_filename( filename, search, emit_func, replacement_node_ir, type_wanted, ): """ Conform the given file to the `intermediate_repr` :param filename: Location of file :type filename: ```str``` :param search: Search query, e.g., ['node_name', 'function_name', 'arg_name'] :type search: ```List[str]``` :param replacement_node_ir: Replace what is found with the contents of this param :type replacement_node_ir: ```dict``` :param type_wanted: AST instance :type type_wanted: ```AST``` :return: filename, whether the file was modified :rtype: ```Tuple[str, bool]``` """ filename = path.realpath(path.expanduser(filename)) if not path.isfile(filename): emit.file( emit_func( replacement_node_ir, emit_default_doc=False, # emit_func.__name__ == "class_" ), filename=filename, mode="wt", skip_black=False, ) return filename, True with open(filename, "rt") as f: parsed_ast = ast_parse(f.read(), filename=filename) assert isinstance(parsed_ast, Module) original_node = find_in_ast(search, parsed_ast) replacement_node = emit_func( replacement_node_ir, **_default_options(node=original_node, search=search, type_wanted=type_wanted)()) if original_node is None: emit.file(replacement_node, filename=filename, mode="a", skip_black=False) return filename, True assert len(search) > 0 assert type( replacement_node) == type_wanted, "Expected {!r} got {!r}".format( type_wanted, type(replacement_node).__name__) replaced = False if not cmp_ast(original_node, replacement_node): rewrite_at_query = RewriteAtQuery( search=search, replacement_node=replacement_node, ) rewrite_at_query.visit(parsed_ast) print("modified" if rewrite_at_query.replaced else "unchanged", filename, sep="\t") if rewrite_at_query.replaced: emit.file(parsed_ast, filename, mode="wt", skip_black=False) replaced = rewrite_at_query.replaced return filename, replaced