def test_to_file(self) -> None: """ Tests whether `file` constructs a file, and fills it with the right content """ with TemporaryDirectory() as tempdir: filename = os.path.join(tempdir, "delete_me.py") try: emit.file(class_ast, filename, skip_black=True) with open(filename, "rt") as f: ugly = f.read() os.remove(filename) emit.file(class_ast, filename, skip_black=False) with open(filename, "rt") as f: blacked = f.read() self.assertNotEqual(ugly, blacked) # if PY3_8: self.assertTrue( cmp_ast(ast.parse(ugly), ast.parse(blacked)), "Ugly AST doesn't match blacked AST", ) finally: if os.path.isfile(filename): os.remove(filename)
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__conform_filename_unchanged(self) -> None: """ Tests that _conform_filename returns the right result """ with TemporaryDirectory() as tempdir: argparse_function_filename = os.path.realpath( os.path.join(tempdir, "do_not_touch_this.py")) emit.file(argparse_func_ast, argparse_function_filename, mode="wt") self.assertTupleEqual( _conform_filename( filename=argparse_function_filename, search=["set_cli_args"], emit_func=emit.argparse_function, replacement_node_ir=deepcopy(intermediate_repr), type_wanted=FunctionDef, ), (argparse_function_filename, False), )
def test__conform_filename_filled(self) -> None: """ Tests that _conform_filename returns the right result """ with TemporaryDirectory() as tempdir: argparse_function_filename = os.path.realpath( os.path.join(tempdir, "correct_contents.py")) emit.file( argparse_func_ast, argparse_function_filename, mode="wt", ) self.assertTupleEqual( _conform_filename( filename=argparse_function_filename, search=["impossibru"], emit_func=emit.argparse_function, replacement_node_ir=deepcopy(intermediate_repr), type_wanted=FunctionDef, ), (argparse_function_filename, True), )
def ground_truth_tester( tempdir, _argparse_func_ast=argparse_func_ast, _class_ast=class_ast_no_default_doc, _class_with_method_ast=class_with_method_types_ast, ): """ Helper for ground_truth tests :param tempdir: temporary directory :type tempdir: ```str``` :param _argparse_func_ast: AST node :type _argparse_func_ast: ```FunctionDef``` :param _class_ast: AST node :type _class_ast: ```ClassDef``` :param _class_with_method_ast: AST node :type _class_with_method_ast: ```ClassDef``` :returns: OrderedDict of filenames and whether they were changed, Args :rtype: ```Tuple[OrderedDict, Namespace]``` """ argparse_function = os.path.join(tempdir, "argparse.py") emit.file(_argparse_func_ast, argparse_function, mode="wt") class_ = os.path.join(tempdir, "classes.py") emit.file(_class_ast, class_, mode="wt") function = os.path.join(tempdir, "methods.py") emit.file(_class_with_method_ast, function, mode="wt") args = Namespace( **{ "argparse_functions": (argparse_function, ), "argparse_function_names": ("set_cli_args", ), "classes": (class_, ), "class_names": ("ConfigClass", ), "functions": (function, ), "function_names": ("C.function_name", ), "truth": "argparse_function", }) with patch("sys.stdout", new_callable=StringIO), patch("sys.stderr", new_callable=StringIO): return ( ground_truth( args, argparse_function, ), args, )
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
def test_ground_truths(self) -> None: """ My truth is being tested. """ with TemporaryDirectory() as tempdir: tempdir_join = partial(path.join, tempdir) argparse_functions = list( map( lambda i: (lambda argparse_function: emit.file( argparse_func_ast, argparse_function, mode="wt") or argparse_function) (tempdir_join("argparse{i}.py".format(i=i))), range(10), )) # Test if can create missing file argparse_functions.append(tempdir_join("argparse_missing.py")) # Test if can fill in empty file argparse_functions.append(tempdir_join("argparse_empty.py")) class_ = tempdir_join("classes.py") emit.file(class_ast_no_default_doc, class_, mode="wt") function = tempdir_join("methods.py") emit.file(class_with_method_ast, function, mode="wt") args = Namespace( **{ "argparse_functions": argparse_functions, "argparse_function_names": ("set_cli_args", ), "classes": (class_, ), "class_names": ("ConfigClass", ), "functions": (function, ), "function_names": ("C.function_name", ), "truth": "argparse_function", }) with patch("sys.stdout", new_callable=StringIO), patch("sys.stderr", new_callable=StringIO): res = ground_truth( args, argparse_functions[0], ) self.assertTupleEqual( tuple( map( lambda filename_unmodified: ( os.path.basename(filename_unmodified[0]), filename_unmodified[1], ), res.items(), )), ( ("argparse0.py", False), ("argparse1.py", False), ("argparse2.py", False), ("argparse3.py", False), ("argparse4.py", False), ("argparse5.py", False), ("argparse6.py", False), ("argparse7.py", False), ("argparse8.py", False), ("argparse9.py", False), ("argparse_missing.py", True), ("argparse_empty.py", True), ("classes.py", False), ("methods.py", False), ), )