예제 #1
0
    def test_from_class_with_body_in_method_to_method_with_body(self) -> None:
        """Tests if this can make the roundtrip from a full function to a full function"""
        annotate_ancestry(class_with_method_and_body_types_ast)

        function_def = reindent_docstring(
            next(
                filter(
                    rpartial(isinstance, FunctionDef),
                    class_with_method_and_body_types_ast.body,
                )))

        ir = parse.function(
            find_in_ast(
                "C.function_name".split("."),
                class_with_method_and_body_types_ast,
            ), )
        gen_ast = emit.function(
            ir,
            emit_default_doc=False,
            function_name="function_name",
            function_type="self",
            indent_level=1,
            emit_separating_tab=True,
            emit_as_kwonlyargs=False,
        )

        # emit.file(gen_ast, os.path.join(os.path.dirname(__file__),
        #           "delme{extsep}py".format(extsep=extsep), mode="wt")

        run_ast_test(
            self,
            gen_ast=gen_ast,
            gold=function_def,
        )
예제 #2
0
    def test_from_method_in_memory_return_complex(self) -> None:
        """
        Tests that `parse.function` produces properly from a function in memory of current interpreter with:
        - complex return type
        - kwonly args
        """

        method_complex_args_variety_with_imports_str = (
            "from sys import stdout\n"
            "from {package} import Literal\n"
            "{body}".format(
                package="typing" if PY_GTE_3_8 else "typing_extensions",
                body=function_adder_str,
            ))
        add_6_5 = getattr(
            inspectable_compile(method_complex_args_variety_with_imports_str),
            "add_6_5",
        )

        ir = parse.function(add_6_5)
        del ir["_internal"]  # Not needed for this test

        self.assertDictEqual(
            ir,
            function_adder_ir,
        )
예제 #3
0
    def test_to_function_with_type_annotations(self) -> None:
        """
        Tests that `function` can generate a function_def with inline types
        """
        function_def = deepcopy(
            next(
                filter(rpartial(isinstance, FunctionDef),
                       class_with_method_types_ast.body)))
        function_name = function_def.name
        function_type = get_function_type(function_def)
        reindent_docstring(function_def)

        gen_ast = emit.function(
            parse.function(
                function_def,
                function_name=function_name,
                function_type=function_type,
            ),
            function_name=function_name,
            function_type=function_type,
            emit_default_doc=False,
            type_annotations=True,
            emit_separating_tab=True,
            indent_level=1,
            emit_as_kwonlyargs=False,
        )
        # emit.file(gen_ast, os.path.join(os.path.dirname(__file__), 'delme.py'), mode='wt')
        run_ast_test(
            self,
            gen_ast=gen_ast,
            gold=function_def,
        )
예제 #4
0
    def test_to_function_with_docstring_types(self) -> None:
        """
        Tests that `function` can generate a function_def with types in docstring
        """

        # Sanity check
        run_ast_test(
            self,
            class_with_method_ast,
            gold=ast.parse(class_with_method_str).body[0],
        )

        function_def = reindent_docstring(
            deepcopy(
                next(
                    filter(rpartial(isinstance, FunctionDef),
                           class_with_method_ast.body))))

        ir = parse.function(function_def)
        gen_ast = reindent_docstring(
            emit.function(
                ir,
                function_name=function_def.name,
                function_type=get_function_type(function_def),
                emit_default_doc=False,
                type_annotations=False,
                indent_level=1,
                emit_separating_tab=True,
                emit_as_kwonlyargs=False,
                word_wrap=False,
            ))

        run_ast_test(self, gen_ast=gen_ast, gold=function_def)
예제 #5
0
    def _handle_function(self, node, doc_str):
        """
        Handle functions

        :param node: AsyncFunctionDef | FunctionDef
        :type node: ```Union[AsyncFunctionDef, FunctionDef]```

        :param doc_str: The docstring
        :type doc_str: ```Optional[str]```

        :returns: Same type as input with args, returns, and docstring potentially modified
        :rtype: ```Union[AsyncFunctionDef, FunctionDef]```
        """
        ir = parse_docstring(doc_str)
        ir_merge(ir, parse.function(node))
        ir["name"] = node.name
        indent_level = max(
            len(node._location) - 1,
            1)  # function docstrings always have at least 1 indent level
        _doc_str = emit.docstring(
            ir,
            emit_types=not self.type_annotations,
            emit_default_doc=False,
            docstring_format=self.docstring_format,
            indent_level=indent_level,
        )
        if _doc_str.isspace():
            if doc_str is not None:
                del node.body[0]
        else:
            set_docstring(_doc_str, False, node)
        if self.type_annotations:
            # Add annotations
            if ir["params"]:
                node.args.args = list(
                    map(
                        lambda _arg: set_arg(
                            _arg.arg,
                            annotation=to_annotation(
                                _arg.annotation
                                if _arg.annotation is not None else ir[
                                    "params"][_arg.arg].get("typ")),
                        ),
                        node.args.args,
                    ))
            if ("return_type" in (ir.get("returns") or iter(()))
                    and ir["returns"]["return_type"].get("typ") is not None):
                node.returns = to_annotation(
                    ir["returns"]["return_type"]["typ"])
        else:
            # Remove annotations
            node.args.args = list(
                map(set_arg, map(attrgetter("arg"), node.args.args)))
            node.returns = None

        node.body = list(map(self.visit, node.body))
        return node
예제 #6
0
 def test_from_function_kw_only(self) -> None:
     """
     Tests that parse.function produces properly from function with only keyword arguments
     """
     gen_ir = parse.function(function_adder_ast, function_type="static")
     del gen_ir["_internal"]  # Not needed for this test
     self.assertDictEqual(
         gen_ir,
         function_adder_ir,
     )
예제 #7
0
 def test_from_method_complex_args_variety(self) -> None:
     """
     Tests that `parse.function` produces correctly with:
     - kw only args;
     - default args;
     - annotations
     - required;
     - unannotated;
     - splat
     """
     gen_ir = parse.function(method_complex_args_variety_ast)
     del gen_ir["_internal"]  # Not needed for this test
     self.assertDictEqual(
         gen_ir,
         method_complex_args_variety_ir,
     )
예제 #8
0
    def test_from_function_in_memory(self) -> None:
        """
        Tests that parse.function produces properly from a function in memory of current interpreter
        """
        def foo(a=5, b=6):
            """
            the foo function

            :param a: the a value
            :param b: the b value

            """

        self.assertIsNone(foo(5, 6))

        ir = parse.function(foo)
        del ir["_internal"]  # Not needed for this test
        self.assertDictEqual(
            ir,
            {
                "doc":
                "the foo function",
                "name":
                "TestParsers.test_from_function_in_memory.<locals>.foo",
                "params":
                OrderedDict((
                    ("a", {
                        "default": 5,
                        "doc": "the a value",
                        "typ": "int"
                    }),
                    ("b", {
                        "default": 6,
                        "doc": "the b value",
                        "typ": "int"
                    }),
                )),
                "returns":
                None,
                "type":
                "static",
            },
        )
예제 #9
0
    def test_from_method_in_memory(self) -> None:
        """
        Tests that `parse.function` produces properly from a function in memory of current interpreter with:
        - kw only args;
        - default args;
        - annotations
        - required;
        - unannotated;
        - splat
        """

        method_complex_args_variety_with_imports_str = (
            "from sys import stdout\n"
            "from {package} import Literal\n"
            "{body}".format(
                package="typing" if PY_GTE_3_8 else "typing_extensions",
                body=method_complex_args_variety_str,
            ))
        call_cliff = getattr(
            inspectable_compile(method_complex_args_variety_with_imports_str),
            "call_cliff",
        )

        ir = parse.function(call_cliff)
        del ir["_internal"]  # Not needed for this test

        # This is a hack because JetBrains wraps stdout
        self.assertIn(
            type(ir["params"]["writer"]["default"]).__name__,
            frozenset(("FlushingStringIO", "TextIOWrapper")),
        )

        # This extra typ is copied, for now. TODO: Update AST-level parser to set types when defaults are given.

        del ir["params"]["writer"]["typ"]
        ir["params"]["writer"]["default"] = "```{}```".format(
            paren_wrap_code("stdout"))

        self.assertDictEqual(
            ir,
            method_complex_args_variety_ir,
        )
예제 #10
0
    def test_from_function(self) -> None:
        """
        Tests that parse.function produces properly
        """
        gen_ir = parse.function(function_default_complex_default_arg_ast)
        gold_ir = {
            "name":
            "call_peril",
            "params":
            OrderedDict((
                (
                    "dataset_name",
                    {
                        "default": "mnist",
                        "typ": "str"
                    },
                ),
                (
                    "writer",
                    {
                        "default":
                        "```{}```".format(
                            paren_wrap_code(
                                get_value(
                                    function_default_complex_default_arg_ast.
                                    args.defaults[1]))),
                    },
                ),
            )),
            "returns":
            None,
            "type":
            "static",
        }

        del gen_ir["_internal"]  # Not needed for this test
        self.assertDictEqual(
            gen_ir,
            gold_ir,
        )
예제 #11
0
    def test_from_function_google_tf_squared_hinge_str_to_class(self) -> None:
        """
        Tests that `emit.function` produces correctly with:
        - __call__
        """

        run_ast_test(
            self,
            gen_ast=emit.class_(
                parse.function(
                    ast.parse(function_google_tf_squared_hinge_str).body[0],
                    infer_type=True,
                    word_wrap=False,
                ),
                class_name="SquaredHingeConfig",
                emit_call=True,
                emit_default_doc=True,
                emit_original_whitespace=True,
                word_wrap=False,
            ),
            gold=class_squared_hinge_config_ast,
        )
예제 #12
0
def populate_files(tempdir, input_module_str=None):
    """
    Populate files in the tempdir

    :param tempdir: Temporary directory
    :type tempdir: ```str```

    :param input_module_str: Input string to write to the input_filename. If None, uses preset mock module.
    :type input_module_str: ```Optional[str]```

    :returns: input filename, input str, expected_output
    :rtype: ```Tuple[str, str, str, Module]```
    """
    input_filename = os.path.join(tempdir,
                                  "input{extsep}py".format(extsep=extsep))
    input_class_name = "Foo"
    input_class_ast = emit.class_(
        parse.function(deepcopy(method_adder_ast)),
        emit_call=False,
        class_name=input_class_name,
    )

    input_module_ast = Module(
        body=[
            input_class_ast,
            Assign(targets=[Name("input_map", Store())],
                   value=Dict(
                       keys=[set_value(input_class_name)],
                       values=[Name(input_class_name, Load())],
                       expr=None,
                   ),
                   expr=None,
                   lineno=None,
                   **maybe_type_comment),
            Assign(
                targets=[Name("__all__", Store())],
                value=List(
                    ctx=Load(),
                    elts=[set_value(input_class_name),
                          set_value("input_map")],
                    expr=None,
                ),
                expr=None,
                lineno=None,
                **maybe_type_comment),
        ],
        type_ignores=[],
        stmt=None,
    )

    input_module_str = input_module_str or to_code(input_module_ast)
    # expected_output_class_str = (
    #     "class FooConfig(object):\n"
    #     '    """\n'
    #     "    The amazing Foo\n\n"
    #     "    :cvar a: An a. Defaults to 5\n"
    #     '    :cvar b: A b. Defaults to 16"""\n'
    #     "    a = 5\n"
    #     "    b = 16\n\n"
    #     "    def __call__(self):\n"
    #     "        self.a = 5\n"
    #     "        self.b = 16\n"
    # )
    expected_class_ast = emit.class_(
        parse.function(deepcopy(method_adder_ast)),
        emit_call=True,
        class_name="{input_class_name}Config".format(
            input_class_name=input_class_name),
    )

    with open(input_filename, "wt") as f:
        f.write(input_module_str)

    return input_filename, input_module_ast, input_class_ast, expected_class_ast