def testMatchType(self): with utils.Tempdir() as d: d.create_file("a.pyi", """ from typing import Type class A(object): ... class B(A): ... class C(object): ... def f(x: Type[A]) -> bool """) ty, errors = self.InferAndCheck("""\ import a x = a.f(a.A) y = a.f(a.B) z = a.f(a.C) """, pythonpath=[d.path], deep=True) error = r"Expected.*Type\[a\.A\].*Actual.*Type\[a\.C\]" self.assertErrorLogIs(errors, [(4, "wrong-arg-types", error)]) self.assertTypesMatchPytd(ty, """ from typing import Any a = ... # type: module x = ... # type: bool y = ... # type: bool z = ... # type: Any """)
def testCallable(self): with utils.Tempdir() as d: d.create_file( "foo.pyi", """ from typing import Any from typing import Callable def process_function(func: Callable[..., Any]) -> None: ... """) ty = self.Infer("""\ import foo def bar(): pass x = foo.process_function(bar) """, deep=False, pythonpath=[d.path], solve_unknowns=True) self.assertTypesMatchPytd( ty, """ from typing import Any foo = ... # type: module def bar() -> Any: ... # 'Any' because deep=False x = ... # type: NoneType """)
def testMaybeIdentityDecorators(self): foo = self.Infer(""" def maybe_decorate(f): return f or (lambda *args: 42) """, deep=True) with utils.Tempdir() as d: d.create_file("foo.pyi", pytd.Print(foo)) ty = self.Infer(""" import foo @foo.maybe_decorate def f(): return 3 def g(): return f() """, deep=True, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ foo = ... # type: module def f() -> int def g() -> int """)
def testInterpreterSubclass(self): with utils.Tempdir() as d: d.create_file("a.pyi", """ from typing import List, TypeVar T = TypeVar("T") class A(List[T]): def __init__(self) -> None: self := A[str] def f(self) -> T """) ty = self.Infer(""" import a class B(a.A): pass def foo(): return B().f() def bar(): return B()[0] """, pythonpath=[d.path], deep=True) self.assertTypesMatchPytd(ty, """ a = ... # type: module class B(a.A): pass def foo() -> str def bar() -> str """)
def testInstanceAttributeInherited(self): with utils.Tempdir() as d: d.create_file("a.pyi", """ from typing import List, TypeVar T = TypeVar("T", int, float) class A(List[T]): x = ... # type: T """) ty = self.Infer(""" import a class B(a.A): pass def f(): return B().x def g(): return B([42]).x """, pythonpath=[d.path], deep=True) self.assertTypesMatchPytd(ty, """ from typing import Any a = ... # type: module class B(a.A): x = ... # type: int or float def f() -> int or float def g() -> int """)
def test_mutableset(self): with utils.Tempdir() as d: d.create_file( "foo.pyi", """ from typing import MutableSet def f() -> MutableSet[str] """) ty = self.Infer("""\ import foo x = foo.f() x.add(1) a = x.pop() x.discard(2) x.clear() x.add(3j) x.remove(3j) b = x & {1,2,3} c = x | {1,2,3} d = x ^ {1,2,3} e = 3 in x """, deep=False, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ from typing import MutableSet, Union foo = ... # type: module a = ... # type: Union[int, str] # TODO(kramm): We do a clear() after adding "int". # Why does "int" still appear for b? b = ... # type: MutableSet[Union[complex, int, str]] c = ... # type: MutableSet[Union[complex, int, str]] d = ... # type: MutableSet[Union[complex, int, str]] e = ... # type: bool x = ... # type: MutableSet[Union[complex, int, str]] """)
def testLoadWithDifferentModuleName(self): with utils.Tempdir() as d: original_module_name = "module1" pickled_ast_filename = os.path.join(d.path, "module1.pyi.pickled") module_map = self._StoreAst(d, original_module_name, pickled_ast_filename) original_ast = module_map[original_module_name] del module_map[original_module_name] new_module_name = "wurstbrot.module2" serializable_ast = pytd_utils.LoadPickle(pickled_ast_filename) serializable_ast = serialize_ast.EnsureAstName(serializable_ast, new_module_name, fix=True) loaded_ast = serialize_ast.ProcessAst(serializable_ast, module_map) self.assertTrue(loaded_ast) self.assertIsNot(loaded_ast, original_ast) self.assertEqual(loaded_ast.name, new_module_name) loaded_ast.Visit(visitors.VerifyLookup()) self.assertFalse(original_ast.ASTeq(loaded_ast)) ast_new_module, _ = self._GetAst(temp_dir=d, module_name=new_module_name) self.assertTrue(ast_new_module.ASTeq(loaded_ast))
def testRenameModuleWithTypeParameter(self): module_name = "foo.bar" src = """ import typing T = TypeVar('T') class SomeClass(typing.Generic[T]): def __init__(self, foo: T) -> None: pass """ with utils.Tempdir() as d: ast, _ = self._GetAst(temp_dir=d, module_name=module_name, src=src) new_ast = ast.Visit( serialize_ast.RenameModuleVisitor(module_name, "other.name")) some_class = new_ast.Lookup("other.name.SomeClass") self.assertTrue(some_class) init_function = some_class.Lookup("__init__") self.assertTrue(init_function) self.assertEqual(len(init_function.signatures), 1) signature, = init_function.signatures _, param2 = signature.params self.assertEqual(param2.type.scope, "other.name.SomeClass")
def testTempdir(self): with utils.Tempdir() as d: filename1 = d.create_file("foo.txt") filename2 = d.create_file("bar.txt", "\tdata2") filename3 = d.create_file("baz.txt", "data3") filename4 = d.create_file("d1/d2/qqsv.txt", " data4.1\n data4.2") filename5 = d.create_directory("directory") self.assertEquals(filename1, d["foo.txt"]) self.assertEquals(filename2, d["bar.txt"]) self.assertEquals(filename3, d["baz.txt"]) self.assertEquals(filename4, d["d1/d2/qqsv.txt"]) self.assertTrue(os.path.isdir(d.path)) self.assertTrue(os.path.isfile(filename1)) self.assertTrue(os.path.isfile(filename2)) self.assertTrue(os.path.isfile(filename3)) self.assertTrue(os.path.isfile(filename4)) self.assertTrue(os.path.isdir(os.path.join(d.path, "d1"))) self.assertTrue(os.path.isdir(os.path.join(d.path, "d1", "d2"))) self.assertTrue(os.path.isdir(filename5)) self.assertEqual(filename4, os.path.join(d.path, "d1", "d2", "qqsv.txt")) for filename, contents in [ (filename1, ""), (filename2, "data2"), # dedented (filename3, "data3"), (filename4, "data4.1\ndata4.2"), # dedented ]: with open(filename, "rb") as fi: self.assertEquals(fi.read(), contents) self.assertFalse(os.path.isdir(d.path)) self.assertFalse(os.path.isfile(filename1)) self.assertFalse(os.path.isfile(filename2)) self.assertFalse(os.path.isfile(filename3)) self.assertFalse(os.path.isdir(os.path.join(d.path, "d1"))) self.assertFalse(os.path.isdir(os.path.join(d.path, "d1", "d2"))) self.assertFalse(os.path.isdir(filename5))
def testDontPropagatePyval(self): # in functions like f(x: T) -> T, if T has constraints we should not copy # the value of constant types between instances of the typevar. with utils.Tempdir() as d: d.create_file( "a.pyi", """ from typing import TypeVar AnyInt = TypeVar('AnyInt', int) def f(x: AnyInt) -> AnyInt """) ty = self.Infer(""" import a if a.f(0): x = 3 if a.f(1): y = 3 """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ a = ... # type: module x = ... # type: int y = ... # type: int """)
def testInferCalledDecoratedMethod(self): with utils.Tempdir() as d: d.create_file( "foo.pyi", """ from typing import Any, Callable, List, TypeVar T = TypeVar("T") def decorator(x: Callable[Any, T]) -> Callable[Any, T]: ... """) ty = self.Infer(""" import foo class A(object): @foo.decorator def f(self, x=None): pass A().f(42) """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ from typing import Any, Callable foo = ... # type: module class A(object): f = ... # type: Callable """)
def test_function_class(self): with utils.Tempdir() as d: d.create_file("foo.pyi", """ def f() -> None: ... """) ty = self.Infer(""" import foo def f(): pass v1 = (foo.f,) v2 = type(foo.f) w1 = (f,) w2 = type(f) """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ from typing import Any, Callable, Tuple, Type foo = ... # type: module def f() -> None: ... v1 = ... # type: Tuple[Callable[[], None]] v2 = ... # type: Type[Callable] w1 = ... # type: Tuple[Callable[[], Any]] w2 = ... # type: Type[Callable] """)
def test_type_parameter_in_return(self): with utils.Tempdir() as d: d.create_file( "foo.pyi", """ from typing import Generic, TypeVar T = TypeVar("T") class MyPattern(Generic[T]): def match(self, string: T) -> MyMatch[T] class MyMatch(Generic[T]): pass def compile() -> MyPattern[T]: ... """) ty = self.Infer("""\ import foo x = foo.compile().match("") """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ import typing foo = ... # type: module x = ... # type: foo.MyMatch[str] """)
def test_namedtuple_item(self): with utils.Tempdir() as d: d.create_file( "foo.pyi", """ from typing import NamedTuple def f() -> NamedTuple("ret", [("x", int), ("y", unicode)]) """) ty = self.Infer(""" import foo w = foo.f()[-1] x = foo.f()[0] y = foo.f()[1] z = foo.f()[2] # out of bounds, fall back to the combined element type """, deep=False, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ foo = ... # type: module w = ... # type: unicode x = ... # type: int y = ... # type: unicode z = ... # type: int or unicode """)
def testCallableParameters(self): with utils.Tempdir() as d: d.create_file("foo.pyi", """ from typing import Any, Callable, List, TypeVar T = TypeVar("T") def f1(x: Callable[..., T]) -> List[T]: ... def f2(x: Callable[[T], Any]) -> List[T]: ... """) ty = self.Infer("""\ from __future__ import google_type_annotations from typing import Any, Callable import foo def g1(): pass def g2() -> int: pass v1 = foo.f1(g1) v2 = foo.f1(g2) def g3(x): pass def g4(x: int): pass w1 = foo.f2(g3) w2 = foo.f2(g4) """, deep=False, pythonpath=[d.path]) self.assertTypesMatchPytd(ty, """ from typing import Any, List foo = ... # type: module def g1() -> Any: ... def g2() -> int: ... def g3(x) -> Any: ... def g4(x: int) -> Any: ... v1 = ... # type: list v2 = ... # type: List[int] w1 = ... # type: list w2 = ... # type: List[int] """)
def testRaises(self): with utils.Tempdir() as d: d.create_file("foo.pyi", """ def f(raises): ... """) self.Check("import foo", pythonpath=[d.path])
def testNoInit(self): with utils.Tempdir() as d: d.create_directory("baz") self.options.tweak(pythonpath=[d.path]) loader = load_pytd.Loader("base", self.options) self.assertTrue(loader.import_name("baz"))
def testInit(self): with utils.Tempdir() as d1: d1.create_file("baz/__init__.pyi", "x = ... # type: int") self.options.tweak(pythonpath=[d1.path]) loader = load_pytd.Loader("base", self.options) self.assertTrue(loader.import_name("baz").Lookup("baz.x"))
def testUnrestorableChild(self): # Assume .cls in a ClassType X in module1 was referencing something for # which, Visitors.LookupExternalTypes returned AnythingType. # Now if something in module1 is referencing X external types need to be # resolved before local types, so that we can resolve local types to the # correct ClassType, as the ClassType instance changes, if .cls can not be # filled and instead AnythingType is used. class RenameVisitor(visitors.Visitor): def __init__(self, *args, **kwargs): super(RenameVisitor, self).__init__(*args, **kwargs) self._init = False def EnterFunction(self, func): if func.name == "__init__": self._init = True return None return False def LeaveFunction(self, func): self._init = False def VisitClassType(self, cls_type): if self._init: cls_type = cls_type.Replace( name="other_module.unknown_Reference") # Needs to be copied manually as it is not part of the NamedTuple. cls_type.cls = None return cls_type with utils.Tempdir() as d: src = (""" import other_module x = other_module.UnusedReferenceNeededToKeepTheImport class SomeClass(object): def __init__(will_be_replaced_with_visitor) -> None: pass def func(a:SomeClass) -> None: pass """) d.create_file( "other_module.pyi", """ from typing import Any def __getattr__(self, name) -> Any: ...""") ast, loader = self._GetAst(temp_dir=d, module_name="module1", src=src) ast = ast.Visit(RenameVisitor()) pickled_ast_filename = os.path.join(d.path, "module1.pyi.pickled") module_map = self._StoreAst(d, "module1", pickled_ast_filename, ast=ast, loader=loader) del module_map["module1"] serialized_ast = pytd_utils.LoadPickle(pickled_ast_filename) loaded_ast = serialize_ast.ProcessAst(serialized_ast, module_map) # Look up the "SomeClass" in "def func(a: SomeClass), then run # VerifyLookup on it. We can't run VerifyLookup on the root node, since # visitors don't descend into the "cls" attribute of ClassType. cls = loaded_ast.functions[0].signatures[0].params[0].type.cls cls.Visit(visitors.VerifyLookup())
def testNoInit(self): with utils.Tempdir() as d: d.create_directory("baz") loader = load_pytd.Loader( "base", self.PYTHON_VERSION, pythonpath=[d.path]) self.assertTrue(loader.import_name("baz"))
def _load_ast(self, name, src): with utils.Tempdir() as d: d.create_file(name + ".pyi", src) self._vm.options.tweak(pythonpath=[d.path]) return self._vm.loader.import_name(name)
def _load_ast(self, name, src): with utils.Tempdir() as d: d.create_file(name + ".pyi", src) self._vm.loader.pythonpath = [d.path] # monkeypatch return self._vm.loader.import_name(name)
def testInit(self): with utils.Tempdir() as d1: d1.create_file("baz/__init__.pytd", "x = ... # type: int") loader = load_pytd.Loader("base", python_version=self.PYTHON_VERSION, pythonpath=[d1.path]) self.assertTrue(loader.import_name("baz").Lookup("baz.x"))