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, )
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, )
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, )
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)
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
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, )
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, )
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", }, )
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, )
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, )
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, )
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