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")))
Example #2
0
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)
Example #3
0
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
Example #4
0
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)
Example #5
0
    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"
                )
            ),
        )
Example #6
0
    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)
Example #7
0
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