From 8221e7638f40701c5eb8c4d8220ace9d2f305942 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Fri, 12 Mar 2021 10:01:44 +0000 Subject: [PATCH] merge test_qualified_name_provider into test_name_provider --- libcst/metadata/tests/test_name_provider.py | 283 ++++++++++++++++- .../tests/test_qualified_name_provider.py | 289 ------------------ 2 files changed, 278 insertions(+), 294 deletions(-) delete mode 100644 libcst/metadata/tests/test_qualified_name_provider.py diff --git a/libcst/metadata/tests/test_name_provider.py b/libcst/metadata/tests/test_name_provider.py index c134806b5..f995b65a9 100644 --- a/libcst/metadata/tests/test_name_provider.py +++ b/libcst/metadata/tests/test_name_provider.py @@ -4,17 +4,28 @@ # LICENSE file in the root directory of this source tree. from textwrap import dedent -from typing import Set +from typing import Collection, Mapping, Optional, Set, Tuple import libcst as cst -from libcst.metadata.name_provider import QualifiedNameProvider -from libcst.metadata.scope_provider import QualifiedName, QualifiedNameSource +from libcst import ensure_type +from libcst.metadata import ( + MetadataWrapper, + QualifiedName, + QualifiedNameProvider, + QualifiedNameSource, +) from libcst.testing.utils import UnitTest +def get_qualified_name_metadata_provider( + module_str: str, +) -> Tuple[cst.Module, Mapping[cst.CSTNode, Collection[QualifiedName]]]: + wrapper = MetadataWrapper(cst.parse_module(dedent(module_str))) + return wrapper.module, wrapper.resolve(QualifiedNameProvider) + + def get_qualified_names(module_str: str) -> Set[QualifiedName]: - wrapper = cst.MetadataWrapper(cst.parse_module(dedent(module_str))) - qnames = wrapper.resolve(QualifiedNameProvider) + _, qnames = get_qualified_name_metadata_provider(module_str) return set().union(*qnames.values()) @@ -52,3 +63,265 @@ class X: self.assertEqual({"X", "X.a"}, {qname.name for qname in qnames}) for qname in qnames: self.assertEqual(qname.source, QualifiedNameSource.LOCAL, msg=f"{qname}") + + def test_simple_qualified_names(self) -> None: + m, names = get_qualified_name_metadata_provider( + """ + from a.b import c + class Cls: + def f(self) -> "c": + c() + d = {} + d['key'] = 0 + def g(): + pass + g() + """ + ) + cls = ensure_type(m.body[1], cst.ClassDef) + f = ensure_type(cls.body.body[0], cst.FunctionDef) + self.assertEqual( + names[ensure_type(f.returns, cst.Annotation).annotation], set() + ) + + c_call = ensure_type( + ensure_type(f.body.body[0], cst.SimpleStatementLine).body[0], cst.Expr + ).value + self.assertEqual( + names[c_call], {QualifiedName("a.b.c", QualifiedNameSource.IMPORT)} + ) + self.assertEqual( + names[c_call], {QualifiedName("a.b.c", QualifiedNameSource.IMPORT)} + ) + + g_call = ensure_type( + ensure_type(m.body[3], cst.SimpleStatementLine).body[0], cst.Expr + ).value + self.assertEqual(names[g_call], {QualifiedName("g", QualifiedNameSource.LOCAL)}) + d_name = ( + ensure_type( + ensure_type(f.body.body[1], cst.SimpleStatementLine).body[0], cst.Assign + ) + .targets[0] + .target + ) + self.assertEqual( + names[d_name], + {QualifiedName("Cls.f..d", QualifiedNameSource.LOCAL)}, + ) + d_subscript = ( + ensure_type( + ensure_type(f.body.body[2], cst.SimpleStatementLine).body[0], cst.Assign + ) + .targets[0] + .target + ) + self.assertEqual( + names[d_subscript], + {QualifiedName("Cls.f..d", QualifiedNameSource.LOCAL)}, + ) + + def test_nested_qualified_names(self) -> None: + m, names = get_qualified_name_metadata_provider( + """ + class A: + def f1(self): + def f2(): + pass + f2() + + def f3(self): + class B(): + ... + B() + def f4(): + def f5(): + class C: + pass + C() + f5() + """ + ) + + cls_a = ensure_type(m.body[0], cst.ClassDef) + self.assertEqual(names[cls_a], {QualifiedName("A", QualifiedNameSource.LOCAL)}) + func_f1 = ensure_type(cls_a.body.body[0], cst.FunctionDef) + self.assertEqual( + names[func_f1], {QualifiedName("A.f1", QualifiedNameSource.LOCAL)} + ) + func_f2_call = ensure_type( + ensure_type(func_f1.body.body[1], cst.SimpleStatementLine).body[0], cst.Expr + ).value + self.assertEqual( + names[func_f2_call], + {QualifiedName("A.f1..f2", QualifiedNameSource.LOCAL)}, + ) + func_f3 = ensure_type(cls_a.body.body[1], cst.FunctionDef) + self.assertEqual( + names[func_f3], {QualifiedName("A.f3", QualifiedNameSource.LOCAL)} + ) + call_b = ensure_type( + ensure_type(func_f3.body.body[1], cst.SimpleStatementLine).body[0], cst.Expr + ).value + self.assertEqual( + names[call_b], {QualifiedName("A.f3..B", QualifiedNameSource.LOCAL)} + ) + func_f4 = ensure_type(m.body[1], cst.FunctionDef) + self.assertEqual( + names[func_f4], {QualifiedName("f4", QualifiedNameSource.LOCAL)} + ) + func_f5 = ensure_type(func_f4.body.body[0], cst.FunctionDef) + self.assertEqual( + names[func_f5], {QualifiedName("f4..f5", QualifiedNameSource.LOCAL)} + ) + cls_c = func_f5.body.body[0] + self.assertEqual( + names[cls_c], + {QualifiedName("f4..f5..C", QualifiedNameSource.LOCAL)}, + ) + + def test_multiple_assignments(self) -> None: + m, names = get_qualified_name_metadata_provider( + """ + if 1: + from a import b as c + elif 2: + from d import e as c + c() + """ + ) + call = ensure_type( + ensure_type(m.body[1], cst.SimpleStatementLine).body[0], cst.Expr + ).value + self.assertEqual( + names[call], + { + QualifiedName(name="a.b", source=QualifiedNameSource.IMPORT), + QualifiedName(name="d.e", source=QualifiedNameSource.IMPORT), + }, + ) + + def test_comprehension(self) -> None: + m, names = get_qualified_name_metadata_provider( + """ + class C: + def fn(self) -> None: + [[k for k in i] for i in [j for j in range(10)]] + # Note: + # The qualified name of i is straightforward to be "C.fn...i". + # ListComp j is evaluated outside of the ListComp i. + # so j has qualified name "C.fn...j". + # ListComp k is evaluated inside ListComp i. + # so k has qualified name "C.fn....k". + """ + ) + cls_def = ensure_type(m.body[0], cst.ClassDef) + fn_def = ensure_type(cls_def.body.body[0], cst.FunctionDef) + outer_comp = ensure_type( + ensure_type( + ensure_type(fn_def.body.body[0], cst.SimpleStatementLine).body[0], + cst.Expr, + ).value, + cst.ListComp, + ) + i = outer_comp.for_in.target + self.assertEqual( + names[i], + { + QualifiedName( + name="C.fn...i", + source=QualifiedNameSource.LOCAL, + ) + }, + ) + inner_comp_j = ensure_type(outer_comp.for_in.iter, cst.ListComp) + j = inner_comp_j.for_in.target + self.assertEqual( + names[j], + { + QualifiedName( + name="C.fn...j", + source=QualifiedNameSource.LOCAL, + ) + }, + ) + inner_comp_k = ensure_type(outer_comp.elt, cst.ListComp) + k = inner_comp_k.for_in.target + self.assertEqual( + names[k], + { + QualifiedName( + name="C.fn....k", + source=QualifiedNameSource.LOCAL, + ) + }, + ) + + def test_has_name_helper(self) -> None: + class TestVisitor(cst.CSTVisitor): + METADATA_DEPENDENCIES = (QualifiedNameProvider,) + + def __init__(self, test: UnitTest) -> None: + self.test = test + + def visit_Call(self, node: cst.Call) -> Optional[bool]: + self.test.assertTrue( + QualifiedNameProvider.has_name(self, node, "a.b.c") + ) + self.test.assertFalse(QualifiedNameProvider.has_name(self, node, "a.b")) + self.test.assertTrue( + QualifiedNameProvider.has_name( + self, node, QualifiedName("a.b.c", QualifiedNameSource.IMPORT) + ) + ) + self.test.assertFalse( + QualifiedNameProvider.has_name( + self, node, QualifiedName("a.b.c", QualifiedNameSource.LOCAL) + ) + ) + + MetadataWrapper(cst.parse_module("import a;a.b.c()")).visit(TestVisitor(self)) + + def test_name_in_attribute(self) -> None: + m, names = get_qualified_name_metadata_provider( + """ + obj = object() + obj.eval + """ + ) + attr = ensure_type( + ensure_type( + ensure_type(m.body[1], cst.SimpleStatementLine).body[0], cst.Expr + ).value, + cst.Attribute, + ) + self.assertEqual( + names[attr], + {QualifiedName(name="obj.eval", source=QualifiedNameSource.LOCAL)}, + ) + eval = attr.attr + self.assertEqual(names[eval], set()) + + def test_repeated_values_in_qualified_name(self) -> None: + m, names = get_qualified_name_metadata_provider( + """ + import a + class Foo: + bar: a.aa.aaa + """ + ) + foo = ensure_type(m.body[1], cst.ClassDef) + bar = ensure_type( + ensure_type( + ensure_type(foo.body, cst.IndentedBlock).body[0], + cst.SimpleStatementLine, + ).body[0], + cst.AnnAssign, + ) + + annotation = ensure_type(bar.annotation, cst.Annotation) + attribute = ensure_type(annotation.annotation, cst.Attribute) + + self.assertEqual( + names[attribute], {QualifiedName("a.aa.aaa", QualifiedNameSource.IMPORT)} + ) diff --git a/libcst/metadata/tests/test_qualified_name_provider.py b/libcst/metadata/tests/test_qualified_name_provider.py deleted file mode 100644 index cf7fa68b4..000000000 --- a/libcst/metadata/tests/test_qualified_name_provider.py +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - - -from textwrap import dedent -from typing import Collection, Mapping, Optional, Tuple - -import libcst as cst -from libcst import ensure_type -from libcst.metadata import ( - MetadataWrapper, - QualifiedName, - QualifiedNameProvider, - QualifiedNameSource, -) -from libcst.testing.utils import UnitTest - - -def get_qualified_name_metadata_provider( - module_str: str, -) -> Tuple[cst.Module, Mapping[cst.CSTNode, Collection[QualifiedName]]]: - wrapper = MetadataWrapper(cst.parse_module(dedent(module_str))) - return wrapper.module, wrapper.resolve(QualifiedNameProvider) - - -class ScopeProviderTest(UnitTest): - def test_simple_qualified_names(self) -> None: - m, names = get_qualified_name_metadata_provider( - """ - from a.b import c - class Cls: - def f(self) -> "c": - c() - d = {} - d['key'] = 0 - def g(): - pass - g() - """ - ) - cls = ensure_type(m.body[1], cst.ClassDef) - f = ensure_type(cls.body.body[0], cst.FunctionDef) - self.assertEqual( - names[ensure_type(f.returns, cst.Annotation).annotation], set() - ) - - c_call = ensure_type( - ensure_type(f.body.body[0], cst.SimpleStatementLine).body[0], cst.Expr - ).value - self.assertEqual( - names[c_call], {QualifiedName("a.b.c", QualifiedNameSource.IMPORT)} - ) - self.assertEqual( - names[c_call], {QualifiedName("a.b.c", QualifiedNameSource.IMPORT)} - ) - - g_call = ensure_type( - ensure_type(m.body[3], cst.SimpleStatementLine).body[0], cst.Expr - ).value - self.assertEqual(names[g_call], {QualifiedName("g", QualifiedNameSource.LOCAL)}) - d_name = ( - ensure_type( - ensure_type(f.body.body[1], cst.SimpleStatementLine).body[0], cst.Assign - ) - .targets[0] - .target - ) - self.assertEqual( - names[d_name], - {QualifiedName("Cls.f..d", QualifiedNameSource.LOCAL)}, - ) - d_subscript = ( - ensure_type( - ensure_type(f.body.body[2], cst.SimpleStatementLine).body[0], cst.Assign - ) - .targets[0] - .target - ) - self.assertEqual( - names[d_subscript], - {QualifiedName("Cls.f..d", QualifiedNameSource.LOCAL)}, - ) - - def test_nested_qualified_names(self) -> None: - m, names = get_qualified_name_metadata_provider( - """ - class A: - def f1(self): - def f2(): - pass - f2() - - def f3(self): - class B(): - ... - B() - def f4(): - def f5(): - class C: - pass - C() - f5() - """ - ) - - cls_a = ensure_type(m.body[0], cst.ClassDef) - self.assertEqual(names[cls_a], {QualifiedName("A", QualifiedNameSource.LOCAL)}) - func_f1 = ensure_type(cls_a.body.body[0], cst.FunctionDef) - self.assertEqual( - names[func_f1], {QualifiedName("A.f1", QualifiedNameSource.LOCAL)} - ) - func_f2_call = ensure_type( - ensure_type(func_f1.body.body[1], cst.SimpleStatementLine).body[0], cst.Expr - ).value - self.assertEqual( - names[func_f2_call], - {QualifiedName("A.f1..f2", QualifiedNameSource.LOCAL)}, - ) - func_f3 = ensure_type(cls_a.body.body[1], cst.FunctionDef) - self.assertEqual( - names[func_f3], {QualifiedName("A.f3", QualifiedNameSource.LOCAL)} - ) - call_b = ensure_type( - ensure_type(func_f3.body.body[1], cst.SimpleStatementLine).body[0], cst.Expr - ).value - self.assertEqual( - names[call_b], {QualifiedName("A.f3..B", QualifiedNameSource.LOCAL)} - ) - func_f4 = ensure_type(m.body[1], cst.FunctionDef) - self.assertEqual( - names[func_f4], {QualifiedName("f4", QualifiedNameSource.LOCAL)} - ) - func_f5 = ensure_type(func_f4.body.body[0], cst.FunctionDef) - self.assertEqual( - names[func_f5], {QualifiedName("f4..f5", QualifiedNameSource.LOCAL)} - ) - cls_c = func_f5.body.body[0] - self.assertEqual( - names[cls_c], - {QualifiedName("f4..f5..C", QualifiedNameSource.LOCAL)}, - ) - - def test_multiple_assignments(self) -> None: - m, names = get_qualified_name_metadata_provider( - """ - if 1: - from a import b as c - elif 2: - from d import e as c - c() - """ - ) - call = ensure_type( - ensure_type(m.body[1], cst.SimpleStatementLine).body[0], cst.Expr - ).value - self.assertEqual( - names[call], - { - QualifiedName(name="a.b", source=QualifiedNameSource.IMPORT), - QualifiedName(name="d.e", source=QualifiedNameSource.IMPORT), - }, - ) - - def test_comprehension(self) -> None: - m, names = get_qualified_name_metadata_provider( - """ - class C: - def fn(self) -> None: - [[k for k in i] for i in [j for j in range(10)]] - # Note: - # The qualified name of i is straightforward to be "C.fn...i". - # ListComp j is evaluated outside of the ListComp i. - # so j has qualified name "C.fn...j". - # ListComp k is evaluated inside ListComp i. - # so k has qualified name "C.fn....k". - """ - ) - cls_def = ensure_type(m.body[0], cst.ClassDef) - fn_def = ensure_type(cls_def.body.body[0], cst.FunctionDef) - outer_comp = ensure_type( - ensure_type( - ensure_type(fn_def.body.body[0], cst.SimpleStatementLine).body[0], - cst.Expr, - ).value, - cst.ListComp, - ) - i = outer_comp.for_in.target - self.assertEqual( - names[i], - { - QualifiedName( - name="C.fn...i", - source=QualifiedNameSource.LOCAL, - ) - }, - ) - inner_comp_j = ensure_type(outer_comp.for_in.iter, cst.ListComp) - j = inner_comp_j.for_in.target - self.assertEqual( - names[j], - { - QualifiedName( - name="C.fn...j", - source=QualifiedNameSource.LOCAL, - ) - }, - ) - inner_comp_k = ensure_type(outer_comp.elt, cst.ListComp) - k = inner_comp_k.for_in.target - self.assertEqual( - names[k], - { - QualifiedName( - name="C.fn....k", - source=QualifiedNameSource.LOCAL, - ) - }, - ) - - def test_has_name_helper(self) -> None: - class TestVisitor(cst.CSTVisitor): - METADATA_DEPENDENCIES = (QualifiedNameProvider,) - - def __init__(self, test: UnitTest) -> None: - self.test = test - - def visit_Call(self, node: cst.Call) -> Optional[bool]: - self.test.assertTrue( - QualifiedNameProvider.has_name(self, node, "a.b.c") - ) - self.test.assertFalse(QualifiedNameProvider.has_name(self, node, "a.b")) - self.test.assertTrue( - QualifiedNameProvider.has_name( - self, node, QualifiedName("a.b.c", QualifiedNameSource.IMPORT) - ) - ) - self.test.assertFalse( - QualifiedNameProvider.has_name( - self, node, QualifiedName("a.b.c", QualifiedNameSource.LOCAL) - ) - ) - - MetadataWrapper(cst.parse_module("import a;a.b.c()")).visit(TestVisitor(self)) - - def test_name_in_attribute(self) -> None: - m, names = get_qualified_name_metadata_provider( - """ - obj = object() - obj.eval - """ - ) - attr = ensure_type( - ensure_type( - ensure_type(m.body[1], cst.SimpleStatementLine).body[0], cst.Expr - ).value, - cst.Attribute, - ) - self.assertEqual( - names[attr], - {QualifiedName(name="obj.eval", source=QualifiedNameSource.LOCAL)}, - ) - eval = attr.attr - self.assertEqual(names[eval], set()) - - def test_repeated_values_in_qualified_name(self) -> None: - m, names = get_qualified_name_metadata_provider( - """ - import a - class Foo: - bar: a.aa.aaa - """ - ) - foo = ensure_type(m.body[1], cst.ClassDef) - bar = ensure_type( - ensure_type( - ensure_type(foo.body, cst.IndentedBlock).body[0], - cst.SimpleStatementLine, - ).body[0], - cst.AnnAssign, - ) - - annotation = ensure_type(bar.annotation, cst.Annotation) - attribute = ensure_type(annotation.annotation, cst.Attribute) - - self.assertEqual( - names[attribute], {QualifiedName("a.aa.aaa", QualifiedNameSource.IMPORT)} - )