def test_very_mutual_recursion(self): ast = self._import(a=""" from typing import List X = List[Y] Y = List[Z] Z = List[X] """) actual_x = ast.Lookup("a.X") expected_x = pytd.Alias(name="a.X", type=pytd.GenericType( base_type=pytd.ClassType("builtins.list"), parameters=(pytd.LateType( "a.Y", recursive=True), ))) self.assertEqual(actual_x, expected_x) actual_y = ast.Lookup("a.Y") expected_y = pytd.Alias(name="a.Y", type=pytd.GenericType( base_type=pytd.ClassType("builtins.list"), parameters=(pytd.LateType( "a.Z", recursive=True), ))) self.assertEqual(actual_y, expected_y) actual_z = ast.Lookup("a.Z") expected_z = pytd.Alias( name="a.Z", type=pytd.GenericType( base_type=pytd.ClassType("builtins.list"), parameters=(pytd.GenericType( base_type=pytd.ClassType("builtins.list"), parameters=(pytd.LateType("a.Y", recursive=True), )), ))) self.assertEqual(actual_z, expected_z)
def testClassSanity(self): ty = self.Infer(""" class A(object): def __init__(self): self.x = 1 def get_x(self): return self.x def set_x(self, x): self.x = x a = A() y = a.x x1 = a.get_x() a.set_x(1.2) x2 = a.get_x() """, deep=False, solve_unknowns=False, extract_locals=False) self.assertHasSignature( ty.Lookup("A").Lookup("set_x"), (pytd.ClassType("A"), self.float), self.none_type) self.assertHasSignature( ty.Lookup("A").Lookup("get_x"), (pytd.ClassType("A"), ), self.intorfloat)
def match_Generic_against_Generic(self, t1, t2, subst): # pylint: disable=invalid-name """Match a pytd.GenericType against another pytd.GenericType.""" assert isinstance(t1.base_type, pytd.ClassType), type(t1.base_type) assert isinstance(t2.base_type, pytd.ClassType), type(t2.base_type) base1 = pytd.ClassType(t1.base_type.cls.name, t1.base_type.cls) base2 = pytd.ClassType(t2.base_type.cls.name, t2.base_type.cls) base_type_cmp = self.match_type_against_type(base1, base2, subst) if base_type_cmp is booleq.FALSE: return booleq.FALSE if not isinstance(t1, pytd.TupleType) and isinstance( t2, pytd.TupleType): p1, = t1.parameters param_cmp = [ self.match_type_against_type(p1, p2, subst) for p2 in t2.parameters ] else: t1_parameters = t1.parameters if isinstance(t1, pytd.TupleType): if isinstance(t2, pytd.TupleType): if len(t1_parameters) != len(t2.parameters): return booleq.FALSE else: t1_parameters = (pytd.UnionType(type_list=t1_parameters), ) # Matching, e.g., Dict[str, int] against Iterable[K] is legitimate. assert len(t1_parameters) >= len( t2.parameters), t1.base_type.cls.name # Type parameters are covariant: # E.g. passing list[int] as argument for list[object] succeeds. param_cmp = [ self.match_type_against_type(p1, p2, subst) for p1, p2 in zip(t1_parameters, t2.parameters) ] return booleq.And([base_type_cmp] + param_cmp)
def testAdjustTypeParametersWithBuiltins(self): ast = self.ParseWithBuiltins(""" T = TypeVar("T") K = TypeVar("K") V = TypeVar("V") class Foo(List[int]): pass class Bar(Dict[T, int]): pass class Baz(Generic[K, V]): pass class Qux(Baz[str, int]): pass """) foo = ast.Lookup("Foo") bar = ast.Lookup("Bar") qux = ast.Lookup("Qux") foo_parent, = foo.parents bar_parent, = bar.parents qux_parent, = qux.parents # Expected: # Class(Foo, parent=GenericType(List, parameters=(int,)), template=()) # Class(Bar, parent=GenericType(Dict, parameters=(T, int)), template=(T)) # Class(Qux, parent=GenericType(Baz, parameters=(str, int)), template=()) self.assertEqual((pytd.ClassType("int"),), foo_parent.parameters) self.assertEqual((), foo.template) self.assertEqual( (pytd.TypeParameter("T", scope="Bar"), pytd.ClassType("int")), bar_parent.parameters) self.assertEqual( (pytd.TemplateItem(pytd.TypeParameter("T", scope="Bar")),), bar.template) self.assertEqual((pytd.ClassType("str"), pytd.ClassType("int")), qux_parent.parameters) self.assertEqual((), qux.template)
def testMaybeInPlaceFillInClasses(self): src = textwrap.dedent(""" class A(object): def a(self, a: A, b: B) -> A or B raises A, B """) tree = self.Parse(src) ty_a = pytd.ClassType("A") visitors.InPlaceFillInClasses(ty_a, tree) self.assertIsNotNone(ty_a.cls) ty_b = pytd.ClassType("B") visitors.InPlaceFillInClasses(ty_b, tree) self.assertIsNone(ty_b.cls)
def testMaybeFillInLocalPointers(self): src = textwrap.dedent(""" class A(object): def a(self, a: A, b: B) -> A or B: raise A() raise B() """) tree = self.Parse(src) ty_a = pytd.ClassType("A") ty_a.Visit(visitors.FillInLocalPointers({"": tree})) self.assertIsNotNone(ty_a.cls) ty_b = pytd.ClassType("B") ty_b.Visit(visitors.FillInLocalPointers({"": tree})) self.assertIsNone(ty_b.cls)
def test_maybe_fill_in_local_pointers(self): src = textwrap.dedent(""" from typing import Union class A: def a(self, a: A, b: B) -> Union[A, B]: raise A() raise B() """) tree = self.Parse(src) ty_a = pytd.ClassType("A") ty_a.Visit(visitors.FillInLocalPointers({"": tree})) self.assertIsNotNone(ty_a.cls) ty_b = pytd.ClassType("B") ty_b.Visit(visitors.FillInLocalPointers({"": tree})) self.assertIsNone(ty_b.cls)
def _load_late_type(self, late_type): """Resolve a late type, possibly by loading a module.""" if late_type.name not in self._resolved_late_types: module, dot, _ = late_type.name.rpartition(".") assert dot ast = self.vm.loader.import_name(module) if ast is not None: try: # TODO(kramm): Should this use visitor.py:ToType? cls = ast.Lookup(late_type.name) except KeyError: try: ast.Lookup("__getattr__") except KeyError: log.warning("Couldn't resolve %s", late_type.name) t = pytd.AnythingType() else: assert isinstance(cls, pytd.Class) t = pytd.ClassType(late_type.name, cls) else: # A pickle file refers to a module that went away in the mean time. log.error("During dependency resolution, couldn't import %r", module) t = pytd.AnythingType() self._resolved_late_types[late_type.name] = t return self._resolved_late_types[late_type.name]
def match_Generic_against_Generic(self, t1, t2, subst): # pylint: disable=invalid-name """Match a pytd.GenericType against another pytd.GenericType.""" assert isinstance(t1.base_type, pytd.ClassType), type(t1.base_type) assert isinstance(t2.base_type, pytd.ClassType), type(t2.base_type) base1 = pytd.ClassType(t1.base_type.cls.name, t1.base_type.cls) base2 = pytd.ClassType(t2.base_type.cls.name, t2.base_type.cls) base_type_cmp = self.match_type_against_type(base1, base2, subst) if base_type_cmp is booleq.FALSE: return booleq.FALSE assert len(t1.parameters) == len(t2.parameters), t1.base_type.cls.name # Type parameters are covariant: # E.g. passing list[int] as argument for list[object] succeeds. param_cmp = [ self.match_type_against_type(p1, p2, subst) for p1, p2 in zip(t1.parameters, t2.parameters) ] return booleq.And([base_type_cmp] + param_cmp)
def testParsePyTD(self): """Test ParsePyTD().""" ast = utils.ParsePyTD("a = ... # type: int", "<inline>", python_version=(2, 7, 6)) a = ast.Lookup("a").type self.assertItemsEqual(a, pytd.ClassType("int")) self.assertIsNotNone(a.cls) # verify that the lookup succeeded
def test_star_import_in_circular_dep(self): stub3_ast = self._import(stub1=""" from stub2 import Foo from typing import Mapping as Mapping """, stub2=""" from stub3 import Mapping class Foo: ... """, stub3=""" from stub1 import * """) self.assertEqual( stub3_ast.Lookup("stub3.Foo").type, pytd.ClassType("stub2.Foo")) self.assertEqual( stub3_ast.Lookup("stub3.Mapping").type, pytd.ClassType("typing.Mapping"))
def testParsePyTD(self): """Test ParsePyTD().""" ast = builtins.ParsePyTD("a = ... # type: int", "<inline>", python_version=self.PYTHON_VERSION, lookup_classes=True) a = ast.Lookup("a").type self.assertItemsEqual(a, pytd.ClassType("int")) self.assertIsNotNone(a.cls) # verify that the lookup succeeded
def _create_call_arg(self, name, t, node): if t == pytd.ClassType("object"): # As an arg, "object" means: we can use anything for this argument, # because everything inherits from object. # TODO(kramm): Maybe we should use AnythingType for params without type. return self.create_new_unsolvable(node, name) else: return self.create_pytd_instance(name, t, {}, node)
def __init__(self, replace_unknown=False, force=False): super(AdjustSelf, self).__init__() self.class_types = [] # allow nested classes self.force = force self.replaced_self_types = (pytd.NamedType("object"), pytd.ClassType("object")) if replace_unknown: self.replaced_self_types += (pytd.AnythingType(), )
def setUp(self): self.options = config.Options.create( python_version=self.PYTHON_VERSION, python_exe=self.PYTHON_EXE) def t(name): # pylint: disable=invalid-name return pytd.ClassType("__builtin__." + name) self.bool = t("bool") self.dict = t("dict") self.float = t("float") self.complex = t("complex") self.int = t("int") self.list = t("list") self.none_type = t("NoneType") self.object = t("object") self.set = t("set") self.frozenset = t("frozenset") self.str = t("str") self.bytearray = t("bytearray") self.tuple = t("tuple") self.unicode = t("unicode") self.generator = t("generator") self.function = pytd.ClassType("typing.Callable") self.anything = pytd.AnythingType() self.nothing = pytd.NothingType() self.module = t("module") self.file = t("file") # The various union types use pytd_utils.CanonicalOrdering()'s ordering: self.intorstr = pytd.UnionType((self.int, self.str)) self.strorunicode = pytd.UnionType((self.str, self.unicode)) self.intorfloat = pytd.UnionType((self.float, self.int)) self.intorfloatorstr = pytd.UnionType((self.float, self.int, self.str)) self.complexorstr = pytd.UnionType((self.complex, self.str)) self.intorfloatorcomplex = pytd.UnionType( (self.int, self.float, self.complex)) self.int_tuple = pytd.GenericType(self.tuple, (self.int, )) self.nothing_tuple = pytd.GenericType(self.tuple, (self.nothing, )) self.intorfloat_tuple = pytd.GenericType(self.tuple, (self.intorfloat, )) self.int_set = pytd.GenericType(self.set, (self.int, )) self.intorfloat_set = pytd.GenericType(self.set, (self.intorfloat, )) self.unknown_frozenset = pytd.GenericType(self.frozenset, (self.anything, )) self.float_frozenset = pytd.GenericType(self.frozenset, (self.float, )) self.empty_frozenset = pytd.GenericType(self.frozenset, (self.nothing, )) self.int_list = pytd.GenericType(self.list, (self.int, )) self.str_list = pytd.GenericType(self.list, (self.str, )) self.intorfloat_list = pytd.GenericType(self.list, (self.intorfloat, )) self.intorstr_list = pytd.GenericType(self.list, (self.intorstr, )) self.anything_list = pytd.GenericType(self.list, (self.anything, )) self.nothing_list = pytd.GenericType(self.list, (self.nothing, )) self.int_int_dict = pytd.GenericType(self.dict, (self.int, self.int)) self.int_str_dict = pytd.GenericType(self.dict, (self.int, self.str)) self.str_int_dict = pytd.GenericType(self.dict, (self.str, self.int)) self.nothing_nothing_dict = pytd.GenericType( self.dict, (self.nothing, self.nothing))
def setUpClass(cls): super().setUpClass() # We use class-wide loader to avoid creating a new loader for every test # method if not required. cls._loader = None def t(name): # pylint: disable=invalid-name return pytd.ClassType("builtins." + name) cls.bool = t("bool") cls.dict = t("dict") cls.float = t("float") cls.complex = t("complex") cls.int = t("int") cls.list = t("list") cls.none_type = t("NoneType") cls.object = t("object") cls.set = t("set") cls.frozenset = t("frozenset") cls.str = t("str") cls.bytearray = t("bytearray") cls.tuple = t("tuple") cls.unicode = t("unicode") cls.generator = t("generator") cls.function = pytd.ClassType("typing.Callable") cls.anything = pytd.AnythingType() cls.nothing = pytd.NothingType() cls.module = t("module") cls.file = t("file") # The various union types use pytd_utils.CanonicalOrdering()'s ordering: cls.intorstr = pytd.UnionType((cls.int, cls.str)) cls.strorunicode = pytd.UnionType((cls.str, cls.unicode)) cls.intorfloat = pytd.UnionType((cls.float, cls.int)) cls.intorfloatorstr = pytd.UnionType((cls.float, cls.int, cls.str)) cls.complexorstr = pytd.UnionType((cls.complex, cls.str)) cls.intorfloatorcomplex = pytd.UnionType( (cls.int, cls.float, cls.complex)) cls.int_tuple = pytd.GenericType(cls.tuple, (cls.int, )) cls.nothing_tuple = pytd.TupleType(cls.tuple, ()) cls.intorfloat_tuple = pytd.GenericType(cls.tuple, (cls.intorfloat, )) cls.int_set = pytd.GenericType(cls.set, (cls.int, )) cls.intorfloat_set = pytd.GenericType(cls.set, (cls.intorfloat, )) cls.unknown_frozenset = pytd.GenericType(cls.frozenset, (cls.anything, )) cls.float_frozenset = pytd.GenericType(cls.frozenset, (cls.float, )) cls.empty_frozenset = pytd.GenericType(cls.frozenset, (cls.nothing, )) cls.int_list = pytd.GenericType(cls.list, (cls.int, )) cls.str_list = pytd.GenericType(cls.list, (cls.str, )) cls.intorfloat_list = pytd.GenericType(cls.list, (cls.intorfloat, )) cls.intorstr_list = pytd.GenericType(cls.list, (cls.intorstr, )) cls.anything_list = pytd.GenericType(cls.list, (cls.anything, )) cls.nothing_list = pytd.GenericType(cls.list, (cls.nothing, )) cls.int_int_dict = pytd.GenericType(cls.dict, (cls.int, cls.int)) cls.int_str_dict = pytd.GenericType(cls.dict, (cls.int, cls.str)) cls.str_int_dict = pytd.GenericType(cls.dict, (cls.str, cls.int)) cls.nothing_nothing_dict = pytd.GenericType(cls.dict, (cls.nothing, cls.nothing))
def ExternalOrNamedOrClassType(name, cls): """Create Classtype / NamedType / ExternalType.""" if "." in name: module, name = name.rsplit(".", 1) return pytd.ExternalType(name, module, cls) elif cls is None: return pytd.NamedType(name) else: return pytd.ClassType(name, cls)
def _create_call_arg(self, name, t, node): if t == pytd.ClassType("__builtin__.object"): # As an arg, "object" means: we can use anything for this argument, # because everything inherits from object. # TODO(kramm): Maybe we should use AnythingType for params without type. return self.convert.create_new_unsolvable(node, name) else: return self.convert.convert_constant( name, abstract.AsInstance(t), subst={}, node=self.root_cfg_node)
def testAnyReplacement(self): class_type_match = pytd.ClassType("match.foo") named_type_match = pytd.NamedType("match.bar") class_type_no_match = pytd.ClassType("match_no.foo") named_type_no_match = pytd.NamedType("match_no.bar") generic_type_match = pytd.GenericType(class_type_match, ()) generic_type_no_match = pytd.GenericType(class_type_no_match, ()) visitor = visitors.ReplaceModulesWithAny(["match."]) self.assertEqual(class_type_no_match, class_type_no_match.Visit(visitor)) self.assertEqual(named_type_no_match, named_type_no_match.Visit(visitor)) self.assertEqual(generic_type_no_match, generic_type_no_match.Visit(visitor)) self.assertEqual(pytd.AnythingType, class_type_match.Visit(visitor).__class__) self.assertEqual(pytd.AnythingType, named_type_match.Visit(visitor).__class__) self.assertEqual(pytd.AnythingType, generic_type_match.Visit(visitor).__class__)
def VisitNamedType(self, node): """Converts a named type to a class type, to be filled in later. Args: node: The NamedType. This type only has a name. Returns: A ClassType. This ClassType will (temporarily) only have a name. """ return pytd.ClassType(node.name)
def testVerifyHeterogeneousTuple(self): # Error: does not inherit from Generic base = pytd.ClassType("tuple") base.cls = pytd.Class("tuple", None, (), (), (), (), None, ()) t1 = pytd.TupleType(base, (pytd.NamedType("str"), pytd.NamedType("float"))) self.assertRaises(visitors.ContainerError, lambda: t1.Visit(visitors.VerifyContainers())) # Error: Generic[str, float] gen = pytd.ClassType("typing.Generic") gen.cls = pytd.Class("typing.Generic", None, (), (), (), (), None, ()) t2 = pytd.TupleType(gen, (pytd.NamedType("str"), pytd.NamedType("float"))) self.assertRaises(visitors.ContainerError, lambda: t2.Visit(visitors.VerifyContainers())) # Okay param = pytd.TypeParameter("T") parent = pytd.GenericType(gen, (param,)) base.cls = pytd.Class( "tuple", None, (parent,), (), (), (), None, (pytd.TemplateItem(param),)) t3 = pytd.TupleType(base, (pytd.NamedType("str"), pytd.NamedType("float"))) t3.Visit(visitors.VerifyContainers())
def testAddNamePrefixOnClassType(self): src = textwrap.dedent(""" x = ... # type: y class Y: ... """) tree = self.Parse(src) x = tree.Lookup("x") x = x.Replace(type=pytd.ClassType("Y")) tree = tree.Replace(constants=(x,), name="foo") tree = tree.Visit(visitors.AddNamePrefix()) self.assertEqual("foo.Y", tree.Lookup("foo.x").type.name)
def _make_pytd_function(self, params): pytd_params = [] for i, p in enumerate(params): p_type = pytd.ClassType(p.name) p_type.cls = p pytd_params.append( pytd.Parameter("_" + str(i), p_type, False, False, None)) pytd_sig = pytd.Signature(tuple(pytd_params), None, None, pytd.AnythingType(), (), ()) sig = abstract.PyTDSignature("f", pytd_sig, self._vm) return abstract.PyTDFunction("f", (sig, ), pytd.METHOD, self._vm)
def testInheritFromBuiltins(self): ty = self.Infer(""" class MyDict(dict): def __init__(self): dict.__setitem__(self, "abc", "foo") def f(): return NoCaseKeysDict() """, deep=False, solve_unknowns=False, show_library_calls=True) mydict = ty.Lookup("MyDict") self.assertOnlyHasReturnType(ty.Lookup("f"), pytd.ClassType("MyDict", mydict))
def _get_known_types_mapping(self): # The mapping includes only the types needed to define a namedtuple. builtin_classes = ("dict", "int", "NoneType", "object", "str", "tuple", "type") typing_classes = ("Callable", "Iterable", "Sized") d = {c: self._get_builtin_classtype(c) for c in builtin_classes} for c in typing_classes: d["typing." + c] = self._get_typing_classtype(c) d["collections.OrderedDict"] = pytd.ClassType( "collections.OrderedDict", self.collections_ast.Lookup("collections.OrderedDict")) return d
def test_basic(self): ast = self._import(a=""" from typing import List X = List[X] """) actual_x = ast.Lookup("a.X") expected_x = pytd.Alias(name="a.X", type=pytd.GenericType( base_type=pytd.ClassType("builtins.list"), parameters=(pytd.LateType( "a.X", recursive=True), ))) self.assertEqual(actual_x, expected_x)
def testInheritFromBuiltins(self): with self.Infer(""" class MyDict(dict): def __init__(self): dict.__setitem__(self, "abc", "foo") def f(): return NoCaseKeysDict() """, deep=False, solve_unknowns=False, extract_locals=False) as ty: mydict = ty.Lookup("MyDict") self.assertOnlyHasReturnType(ty.Lookup("f"), pytd.ClassType("MyDict", mydict))
def testLookup(self): with self.Infer(""" class Cloneable(object): def __init__(self): pass def clone(self): return Cloneable() Cloneable().clone() """, deep=False, solve_unknowns=False, extract_locals=False) as ty: cls = ty.Lookup("Cloneable") method = cls.Lookup("clone") self.assertOnlyHasReturnType(method, pytd.ClassType("Cloneable", cls))
def testLookup(self): ty = self.Infer(""" class Cloneable(object): def __init__(self): pass def clone(self): return Cloneable() Cloneable().clone() """, deep=False, show_library_calls=True) cls = ty.Lookup("Cloneable") method = cls.Lookup("clone") self.assertOnlyHasReturnType(method, pytd.ClassType("Cloneable", cls))
def _make_pytd_function(self, params, name="f"): pytd_params = [] for i, p in enumerate(params): p_type = pytd.ClassType(p.name) p_type.cls = p pytd_params.append( pytd.Parameter(function.argname(i), p_type, pytd.ParameterKind.REGULAR, False, None)) pytd_sig = pytd.Signature( tuple(pytd_params), None, None, pytd.AnythingType(), (), ()) sig = abstract.PyTDSignature(name, pytd_sig, self._ctx) return abstract.PyTDFunction(name, (sig,), pytd.MethodKind.METHOD, self._ctx)