def test_has_type_annotations(self) -> None: """Tests has_type_annotations""" self.assertTrue(has_type_annotations(ast_parse("a: int = 5"))) self.assertFalse(has_type_annotations(ast_parse("a = 5"))) self.assertTrue( has_type_annotations(ast_parse("def a() -> None: pass"))) self.assertFalse(has_type_annotations(ast_parse("def a(): pass")))
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 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``` :returns: 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 {type_name!r}".format( type_name=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 doctrans(filename, docstring_format, type_annotations): """ Transform the docstrings found within provided filename to intended docstring_format :param filename: Python file to convert docstrings within. Edited in place. :type filename: ```str``` :param docstring_format: Format of docstring :type docstring_format: ```Literal['rest', 'numpydoc', 'google']``` :param type_annotations: True to have type annotations (3.6+), False to place in docstring :type type_annotations: ```bool``` """ with open(filename, "rt") as f: node = ast_parse(f.read(), skip_docstring_remit=False) orig_node = deepcopy(node) node = DocTrans( docstring_format=docstring_format, type_annotations=type_annotations, existing_type_annotations=has_type_annotations(node), whole_ast=orig_node, ).visit(node) if not cmp_ast(node, orig_node): emit.file(node, filename, mode="wt", skip_black=True)
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 check_emission(self, tempdir, dry_run=False): """ Confirm whether emission conforms to gen by verifying their IRs are equivalent :param tempdir: Temporary directory :type tempdir: ```str``` :param dry_run: Show what would be created; don't actually write to the filesystem :type dry_run: ```bool``` """ new_module_name = path.basename(tempdir) for name, folder in self.module_hierarchy: gen_folder = path.join(tempdir, new_module_name, folder) gold_folder = path.join(self.gold_dir, self.module_name, folder) def _open(_folder): """ :param _folder: Folder to join on :type _folder: ```str`` :returns: Open IO :rtype: ```open``` """ return open( path.join( _folder, "{name}{extsep}py".format(name=name, extsep=extsep) ), "rt", ) self.assertTrue(path.isdir(gold_folder)) gen_is_dir = path.isdir(gen_folder) if dry_run: self.assertFalse(gen_is_dir) else: self.assertTrue(gen_is_dir) with _open(gen_folder) as gen, _open(gold_folder) as gold: gen_ir, gold_ir = map( lambda node: parse.class_( next( filter( rpartial(isinstance, ClassDef), ast_parse(node.read()).body, ) ) ), (gen, gold), ) self.assertDictEqual(gold_ir, gen_ir)
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``` :returns: 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 {type_wanted!r} got {type_replacement_node!r}".format( type_wanted=type_wanted, type_replacement_node=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