def get_unused_import_aliases(tree, sc=None): """Get the import aliases that aren't used. Arguments: tree: (ast.AST) An ast to find imports in. sc: A scope.Scope representing tree (generated from scratch if not provided). Returns: A list of ast.alias representing imported aliases that aren't referenced in the given tree. """ if sc is None: sc = scope.analyze(tree) unused_aliases = set() for node in ast.walk(tree): if isinstance(node, ast.alias): str_name = node.asname if node.asname is not None else node.name if str_name in sc.names: name = sc.names[str_name] if not name.reads: unused_aliases.add(node) else: # This happens because of https://github.com/google/pasta/issues/32 logging.warning( 'Imported name %s not found in scope (perhaps it\'s ' 'imported dynamically)', str_name) return unused_aliases
def test_rename_reads_noop(self): src = 'aaa.bbb.ccc()' t = ast.parse(src) sc = scope.analyze(t) rename._rename_reads(sc, t, 'aaa.bbb.ccc.ddd', 'xxx.yyy') rename._rename_reads(sc, t, 'bbb.aaa', 'xxx.yyy') self.checkAstsEqual(t, ast.parse(src))
def test_split_nested_imports(self): test_cases = ( 'def foo():\n {import_stmt}\n', 'class Foo(object):\n {import_stmt}\n', 'if foo:\n {import_stmt}\nelse:\n pass\n', 'if foo:\n pass\nelse:\n {import_stmt}\n', 'if foo:\n pass\nelif bar:\n {import_stmt}\n', 'try:\n {import_stmt}\nexcept:\n pass\n', 'try:\n pass\nexcept:\n {import_stmt}\n', 'try:\n pass\nfinally:\n {import_stmt}\n', 'for i in foo:\n {import_stmt}\n', 'for i in foo:\n pass\nelse:\n {import_stmt}\n', 'while foo:\n {import_stmt}\n', ) for template in test_cases: try: src = template.format(import_stmt='import aaa, bbb, ccc') t = ast.parse(src) sc = scope.analyze(t) import_node = ast_utils.find_nodes_by_type(t, ast.Import)[0] import_utils.split_import(sc, import_node, import_node.names[1]) split_import_nodes = ast_utils.find_nodes_by_type(t, ast.Import) self.assertEqual(1, len(t.body)) self.assertEqual(2, len(split_import_nodes)) self.assertEqual([alias.name for alias in split_import_nodes[0].names], ['aaa', 'ccc']) self.assertEqual([alias.name for alias in split_import_nodes[1].names], ['bbb']) except: self.fail('Failed while executing case:\n%s\nCaused by:\n%s' % (src, traceback.format_exc()))
def test_if_nested_imports(self): source = textwrap.dedent("""\ if a: import aaa elif b: import bbb else: import ccc """) tree = ast.parse(source) nodes = tree.body node_aaa, node_bbb, node_ccc = ast_utils.find_nodes_by_type( tree, ast.alias) s = scope.analyze(tree) self.assertItemsEqual(s.names.keys(), {'aaa', 'bbb', 'ccc', 'a', 'b'}) self.assertItemsEqual(s.external_references.keys(), {'aaa', 'bbb', 'ccc'}) self.assertEqual(s.names['aaa'].definition, node_aaa) self.assertEqual(s.names['bbb'].definition, node_bbb) self.assertEqual(s.names['ccc'].definition, node_ccc) self.assertIsNone(s.names['a'].definition) self.assertIsNone(s.names['b'].definition) for ref in {'aaa', 'bbb', 'ccc'}: self.assertEqual(s.names[ref].reads, [], 'Expected no reads for %s' % ref)
def test_import_attribute_references(self): source = textwrap.dedent("""\ import aaa.bbb.ccc, ddd.eee aaa.x() aaa.bbb.y() aaa.bbb.ccc.z() """) tree = ast.parse(source) nodes = tree.body call1 = nodes[1].value.func.value call2 = nodes[2].value.func.value call3 = nodes[3].value.func.value s = scope.analyze(tree) self.assertItemsEqual(s.names.keys(), {'aaa', 'ddd'}) self.assertItemsEqual( s.external_references.keys(), {'aaa', 'aaa.bbb', 'aaa.bbb.ccc', 'ddd', 'ddd.eee'}) self.assertItemsEqual(s.names['aaa'].reads, [call1, call2.value, call3.value.value]) self.assertItemsEqual(s.names['aaa'].attrs['bbb'].reads, [call2, call3.value]) self.assertItemsEqual(s.names['aaa'].attrs['bbb'].attrs['ccc'].reads, [call3])
def test_rename_reads_name(self): src = 'aaa.bbb()' t = pasta.ast_parse(src, py_ver) sc = scope.analyze(t, py_ver) self.assertTrue(rename._rename_reads(sc, t, 'aaa', 'xxx', py_ver)) self.checkAstsEqual(t, pasta.ast_parse('xxx.bbb()', py_ver), py_ver)
def test_top_level_imports(self): self.maxDiff = None source = textwrap.dedent("""\ import aaa import bbb, ccc.ddd import aaa.bbb.ccc from eee import fff from ggg.hhh import iii, jjj """) tree = ast.parse(source) nodes = tree.body node_1_aaa = nodes[0].names[0] node_2_bbb = nodes[1].names[0] node_2_ccc = nodes[1].names[1] node_2_ccc_ddd = nodes[1].names[1] node_3_aaa_bbb_ccc = nodes[2].names[0] node_4_fff = nodes[3].names[0] node_5_iii = nodes[4].names[0] node_5_jjj = nodes[4].names[1] s = scope.analyze(tree) self.assertItemsEqual(s.names.keys(), {'aaa', 'bbb', 'ccc', 'fff', 'iii', 'jjj'}) self.assertItemsEqual( s.external_references.keys(), { 'aaa', 'bbb', 'ccc', 'ccc.ddd', 'aaa.bbb', 'aaa.bbb.ccc', 'eee', 'eee.fff', 'ggg', 'ggg.hhh', 'ggg.hhh.iii', 'ggg.hhh.jjj' }) self.assertItemsEqual(s.external_references['aaa'], [s.names['aaa'].definition] + s.names['aaa'].reads) self.assertItemsEqual(s.external_references['bbb'], [s.names['bbb'].definition]) self.assertItemsEqual(s.external_references['ccc.ddd'], [s.names['ccc'].attrs['ddd'].definition]) self.assertItemsEqual( s.external_references['aaa.bbb.ccc'], [s.names['aaa'].attrs['bbb'].attrs['ccc'].definition]) self.assertItemsEqual(s.external_references['eee.fff'], [s.names['fff'].definition]) self.assertItemsEqual(s.external_references['ggg.hhh.iii'], [s.names['iii'].definition]) self.assertItemsEqual(s.external_references['ggg.hhh.jjj'], [s.names['jjj'].definition]) self.assertEqual(s.names['aaa'].definition, node_1_aaa) self.assertEqual(s.names['bbb'].definition, node_2_bbb) self.assertEqual(s.names['ccc'].definition, node_2_ccc) self.assertEqual(s.names['fff'].definition, node_4_fff) self.assertEqual(s.names['iii'].definition, node_5_iii) self.assertEqual(s.names['jjj'].definition, node_5_jjj) self.assertItemsEqual(s.names['aaa'].reads, [node_3_aaa_bbb_ccc]) for ref in {'bbb', 'ccc', 'fff', 'iii', 'jjj'}: self.assertEqual(s.names[ref].reads, [], 'Expected no reads for %s' % ref)
def test_remove_full_importfrom(self): src = 'from m import a' tree = pasta.ast_parse(src, py_ver) sc = scope.analyze(tree, py_ver) a_node = tree.body[0].names[0] import_utils.remove_import_alias_node(sc, a_node, py_ver=py_ver) self.assertEqual(len(tree.body), 0)
def test_remove_full_importfrom(self): src = "from m import a" tree = ast.parse(src) sc = scope.analyze(tree) a_node = tree.body[0].names[0] import_utils.remove_import_alias_node(sc, a_node) self.assertEqual(len(tree.body), 0)
def test_split_imports_with_alias(self): src = 'import aaa as a, bbb as b, ccc as c\n' t = ast.parse(src) import_node = t.body[0] sc = scope.analyze(t) import_utils.split_import(sc, import_node, import_node.names[1]) self.assertEqual(2, len(t.body)) self.assertEqual([alias.name for alias in t.body[0].names], ['aaa', 'ccc']) self.assertEqual([alias.name for alias in t.body[1].names], ['bbb']) self.assertEqual(t.body[1].names[0].asname, 'b')
def test_split_normal_import(self): src = 'import aaa, bbb, ccc\n' t = ast.parse(src) import_node = t.body[0] sc = scope.analyze(t) import_utils.split_import(sc, import_node, import_node.names[1]) self.assertEqual(2, len(t.body)) self.assertEqual(ast.Import, type(t.body[1])) self.assertEqual([alias.name for alias in t.body[0].names], ['aaa', 'ccc']) self.assertEqual([alias.name for alias in t.body[1].names], ['bbb'])
def test_split_from_import(self): src = 'from aaa import bbb, ccc, ddd\n' t = ast.parse(src) import_node = t.body[0] sc = scope.analyze(t) import_utils.split_import(sc, import_node, import_node.names[1]) self.assertEqual(2, len(t.body)) self.assertEqual(ast.ImportFrom, type(t.body[1])) self.assertEqual(t.body[0].module, 'aaa') self.assertEqual(t.body[1].module, 'aaa') self.assertEqual([alias.name for alias in t.body[0].names], ['bbb', 'ddd'])
def test_remove_just_alias_import_from(self): src = "from m import a, b" tree = ast.parse(src) sc = scope.analyze(tree) unused_b_node = tree.body[0].names[1] import_utils.remove_import_alias_node(sc, unused_b_node) self.assertEqual(len(tree.body), 1) self.assertEqual(type(tree.body[0]), ast.ImportFrom) self.assertEqual(len(tree.body[0].names), 1) self.assertEqual(tree.body[0].names[0].name, 'a')
def test_classdef_nested_imports(self): source = textwrap.dedent("""\ class Foo(): import aaa """) tree = ast.parse(source) nodes = tree.body node_aaa = nodes[0].body[0].names[0] s = scope.analyze(tree) self.assertItemsEqual(s.names.keys(), {'Foo'}) self.assertItemsEqual(s.external_references.keys(), {'aaa'})
def test_split_imports_multiple(self): src = 'import aaa, bbb, ccc\n' t = ast.parse(src) import_node = t.body[0] alias_bbb = import_node.names[1] alias_ccc = import_node.names[2] sc = scope.analyze(t) import_utils.split_import(sc, import_node, alias_bbb) import_utils.split_import(sc, import_node, alias_ccc) self.assertEqual(3, len(t.body)) self.assertEqual([alias.name for alias in t.body[0].names], ['aaa']) self.assertEqual([alias.name for alias in t.body[1].names], ['ccc']) self.assertEqual([alias.name for alias in t.body[2].names], ['bbb'])
def process(self, ast_tree): """ Start to analyze the code. Args: ast_tree (AST): The root node of the source code. """ self.__init__() self._root_scope = scope.analyze(ast_tree) self._pre_process() self.visit(ast_tree) if not self._network_classes: msg = "model definition not be found." raise ScriptNotSupport(msg)
def test_functiondef_nested_imports(self): source = textwrap.dedent("""\ def foo(bar): import aaa """) tree = ast.parse(source) nodes = tree.body node_aaa = ast_utils.find_nodes_by_type(tree, ast.alias)[0] s = scope.analyze(tree) self.assertItemsEqual(s.names.keys(), {'foo'}) self.assertItemsEqual(s.external_references.keys(), {'aaa'})
def test_rename_reads_type_annotation(self): src = textwrap.dedent("""\ def foo(bar: 'aaa.bbb.ccc.Bar'): pass """) t = ast.parse(src) sc = scope.analyze(t) rename._rename_reads(sc, t, 'aaa.bbb', 'xxx.yyy') self.checkAstsEqual( t, ast.parse( textwrap.dedent("""\ def foo(bar: 'xxx.yyy.ccc.Bar'): pass """)))
def test_remove_just_alias_import_from(self): src = 'from m import a, b' tree = pasta.ast_parse(src, py_ver) sc = scope.analyze(tree, py_ver) unused_b_node = tree.body[0].names[1] import_utils.remove_import_alias_node(sc, unused_b_node, py_ver=py_ver) self.assertEqual(len(tree.body), 1) self.assertEqual(type(tree.body[0]), pasta.ast(py_ver).ImportFrom) self.assertEqual(len(tree.body[0].names), 1) self.assertEqual(tree.body[0].names[0].name, 'a')
def test_forward_type_reference(self): source = textwrap.dedent("""\ class A(): def foo(self, a: 'A'): pass """) tree = ast.parse(source) nodes = tree.body classdef = nodes[0] s = scope.analyze(tree) self.assertItemsEqual(s.names.keys(), {'A'}) self.assertItemsEqual(s.names['A'].reads, [classdef.body[0].args.args[1].annotation])
def test_import_in_return_type(self): source = textwrap.dedent("""\ import aaa def foo() -> aaa.Foo: pass """) tree = ast.parse(source) nodes = tree.body func = nodes[1] s = scope.analyze(tree) self.assertItemsEqual(s.names.keys(), {'aaa', 'foo'}) self.assertItemsEqual(s.external_references.keys(), {'aaa'}) self.assertItemsEqual(s.names['aaa'].reads, [func.returns.value])
def test_import_masked_by_function_arg(self): source = textwrap.dedent("""\ import aaa def foo(aaa=aaa): return aaa """) tree = ast.parse(source) nodes = tree.body argval = nodes[1].args.defaults[0] s = scope.analyze(tree) self.assertItemsEqual(s.names.keys(), {'aaa', 'foo'}) self.assertItemsEqual(s.external_references.keys(), {'aaa'}) self.assertItemsEqual(s.names['aaa'].reads, [argval])
def test_import_in_argument_type(self): source = textwrap.dedent("""\ import aaa def foo(bar: aaa.Bar): pass """) tree = ast.parse(source) nodes = tree.body func = nodes[1] s = scope.analyze(tree) self.assertItemsEqual(s.names.keys(), {'aaa', 'foo'}) self.assertItemsEqual(s.external_references.keys(), {'aaa'}) self.assertItemsEqual(s.names['aaa'].reads, [func.args.args[0].annotation.value])
def test_import_in_decortator(self): source = textwrap.dedent("""\ import aaa @aaa.wrapper def foo(aaa=1): pass """) tree = ast.parse(source) nodes = tree.body decorator = nodes[1].decorator_list[0].value s = scope.analyze(tree) self.assertItemsEqual(s.names.keys(), {'aaa', 'foo'}) self.assertItemsEqual(s.external_references.keys(), {'aaa'}) self.assertItemsEqual(s.names['aaa'].reads, [decorator])
def test_vararg_kwarg_references_in_function_body(self): source = textwrap.dedent("""\ def aaa(bbb, *ccc, **ddd): ccc ddd eee(ccc, ddd) """) tree = ast.parse(source) funcdef, call = tree.body ccc_expr, ddd_expr = funcdef.body sc = scope.analyze(tree) func_scope = sc.lookup_scope(funcdef) self.assertIn('ccc', func_scope.names) self.assertItemsEqual(func_scope.names['ccc'].reads, [ccc_expr.value]) self.assertIn('ddd', func_scope.names) self.assertItemsEqual(func_scope.names['ddd'].reads, [ddd_expr.value])
def test_import_masked_by_assign(self): source = textwrap.dedent("""\ import aaa def foo(): aaa = 123 return aaa aaa """) tree = ast.parse(source) nodes = tree.body node_aaa = nodes[2].value s = scope.analyze(tree) self.assertItemsEqual(s.names.keys(), {'aaa', 'foo'}) self.assertItemsEqual(s.external_references.keys(), {'aaa'}) self.assertItemsEqual(s.names['aaa'].reads, [node_aaa])
def test_import_reads_in_functiondef(self): source = textwrap.dedent("""\ import aaa @aaa.x def foo(bar): return aaa """) tree = ast.parse(source) nodes = tree.body return_value = nodes[1].body[0].value decorator = nodes[1].decorator_list[0].value s = scope.analyze(tree) self.assertItemsEqual(s.names.keys(), {'aaa', 'foo'}) self.assertItemsEqual(s.external_references.keys(), {'aaa'}) self.assertItemsEqual(s.names['aaa'].reads, [decorator, return_value])
def test_import_reads_in_classdef(self): source = textwrap.dedent("""\ import aaa @aaa.x class Foo(aaa.Bar): pass """) tree = ast.parse(source) nodes = tree.body node_aaa = nodes[0].names[0] decorator = nodes[1].decorator_list[0].value base = nodes[1].bases[0].value s = scope.analyze(tree) self.assertItemsEqual(s.names.keys(), {'aaa', 'Foo'}) self.assertItemsEqual(s.external_references.keys(), {'aaa'}) self.assertItemsEqual(s.names['aaa'].reads, [decorator, base])
def test_multilevel_import_reads(self): source = textwrap.dedent("""\ import aaa.bbb.ccc aaa.bbb.ccc.foo() """) tree = pasta.ast_parse(source, py_ver) nodes = tree.body node_ref = nodes[1].value.func.value s = scope.analyze(tree, py_ver) self.assertItemsEqual(s.names.keys(), {'aaa'}) self.assertItemsEqual(s.external_references.keys(), {'aaa', 'aaa.bbb', 'aaa.bbb.ccc'}) self.assertItemsEqual(s.names['aaa'].reads, [node_ref.value.value]) self.assertItemsEqual(s.names['aaa'].attrs['bbb'].reads, [node_ref.value]) self.assertItemsEqual(s.names['aaa'].attrs['bbb'].attrs['ccc'].reads, [node_ref])
def test_import_in_argument_type_string(self): source = textwrap.dedent("""\ import aaa def foo(bar: 'aaa.Bar'): pass """) tree = pasta.ast_parse(source, py_ver) nodes = tree.body func = nodes[1] s = scope.analyze(tree, py_ver) self.assertItemsEqual(s.names.keys(), {'aaa', 'foo'}) self.assertItemsEqual(s.external_references.keys(), {'aaa'}) self.assertItemsEqual(s.names['aaa'].reads, [func.args.args[0].annotation]) self.assertItemsEqual(s.names['aaa'].lookup_name('Bar').reads, [func.args.args[0].annotation])