def testInstanceAttributeSet(self): with file_utils.Tempdir() as d: d.create_file( "a.pyi", """ from typing import Generic, TypeVar T = TypeVar("T") class A(Generic[T]): def f(self) -> T """) ty = self.Infer(""" import a def f(): inst = a.A() inst.x = inst.f() return inst.x """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ from typing import Any a = ... # type: module def f() -> Any """)
def testTypeParameterConflict(self): with file_utils.Tempdir() as d: d.create_file( "a.pyi", """ from typing import Generic, TypeVar T = TypeVar("T") K = TypeVar("K") V = TypeVar("V") class MyIterable(Generic[T]): pass class MyList(MyIterable[T]): pass class MyDict(MyIterable[K], Generic[K, V]): pass class Custom(MyDict[K, V], MyList[V]): pass """) ty = self.Infer(""" import a x = a.Custom() """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ a = ... # type: module x = ... # type: a.Custom[nothing, nothing] """)
def testMultipleTemplates(self): with file_utils.Tempdir() as d: d.create_file( "a.pyi", """ from typing import Generic, List, TypeVar K = TypeVar("K") V = TypeVar("V") class MyDict(Generic[K, V]): pass class A(MyDict[K, V], List[V]): pass """) ty = self.Infer(""" import a def f(): x = a.A() x.extend([42]) return x """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ a = ... # type: module def f() -> a.A[nothing, int] """)
def test_dynamic_attributes(self): foo1 = self.Infer(""" HAS_DYNAMIC_ATTRIBUTES = True """) foo2 = self.Infer(""" has_dynamic_attributes = True """) with file_utils.Tempdir() as d: d.create_file("foo1.pyi", pytd_utils.Print(foo1)) d.create_file("foo2.pyi", pytd_utils.Print(foo2)) d.create_file("bar.pyi", """ from foo1 import xyz from foo2 import zyx """) self.Check(""" import foo1 import foo2 import bar foo1.abc foo2.abc bar.xyz bar.zyx """, pythonpath=[d.path])
def testTypeParameterDeep(self): with file_utils.Tempdir() as d: d.create_file( "foo.pyi", """ from typing import Generic, TypeVar U = TypeVar("U") V = TypeVar("V") class A(Generic[U]): def bar(self) -> U: ... class B(A[V], Generic[V]): ... def baz() -> B[int] """) ty = self.Infer(""" import foo def f(): return foo.baz().bar() """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ foo = ... # type: module def f() -> int """)
def testAliasingDictConflictError(self): with file_utils.Tempdir() as d: d.create_file( "a.pyi", """ from typing import Dict, Generic, List, TypeVar T = TypeVar("T") U = TypeVar("U") class A(Dict[int, U], List[T], Generic[T, U]): ... """) ty = self.Infer(""" import a v = a.A() """, deep=False, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ from typing import Any a = ... # type: module # Type parameter a.A.T can be an alias for both List._T and Dict._K. # Due to this ambiguity, T is set to Any. v = ... # type: a.A[Any, nothing] """)
def testUnsolvableMetaclass(self): with file_utils.Tempdir() as d: d.create_file( "a.pyi", """ from typing import Any def __getattr__(name) -> Any """) d.create_file( "b.pyi", """ from a import A class B(metaclass=A): ... """) ty = self.Infer(""" import b x = b.B.x """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ from typing import Any b = ... # type: module x = ... # type: Any """)
def test_property_type_param3(self): # Don't mix up the class parameter and the property parameter with file_utils.Tempdir() as d: d.create_file( "a.pyi", """ from typing import TypeVar, List, Generic T = TypeVar('T') U = TypeVar('U') class A(Generic[U]): @property def foo(self: T) -> List[U]: ... def make_A() -> A[int]: ... """) ty = self.Infer(""" import a x = a.make_A().foo """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ import a x = ... # type: List[int] """)
def test_circular_import_with_external_type(self): with file_utils.Tempdir() as d: d.create_file( "os2/__init__.pyi", """ from posix2 import stat_result as stat_result from . import path as path _PathType = path._PathType def utime(path: _PathType) -> None: ... """) d.create_file( "os2/path.pyi", """ import os2 _PathType = bytes def samestate(stat1: os2.stat_result) -> bool: ... """) d.create_file("posix2.pyi", "class stat_result: ...") loader = load_pytd.Loader(None, self.python_version, pythonpath=[d.path]) # Make sure all three modules were resolved properly. loader.import_name("os2") loader.import_name("os2.path") loader.import_name("posix2")
def testType(self): pickled = self.Infer(""" x = type """, deep=False, pickle=True, module_name="foo") with file_utils.Tempdir() as d: u = d.create_file("u.pickled", pickled) ty = self.Infer(""" import u r = u.x """, deep=False, pythonpath=[""], imports_map={"u": u}) self.assertTypesMatchPytd( ty, """ from typing import Type import collections u = ... # type: module r = ... # type: Type[type] """)
def test_generic_property(self): with file_utils.Tempdir() as d: d.create_file( "foo.pyi", """ from typing import Generic, Optional, TypeVar T = TypeVar("T") class Foo(Generic[T]): @property def x(self) -> Optional[T]: ... def f() -> Foo[str]: ... """) ty = self.Infer(""" import foo def f(): return foo.f().x """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ from typing import Optional foo: module def f() -> Optional[str]: ... """)
def test_circular_dependency(self): with file_utils.Tempdir() as d: d.create_file( "foo.pyi", """ def get_bar() -> bar.Bar class Foo: pass """) d.create_file( "bar.pyi", """ def get_foo() -> foo.Foo class Bar: pass """) loader = load_pytd.Loader("base", self.python_version, pythonpath=[d.path]) foo = loader.import_name("foo") bar = loader.import_name("bar") f1, = foo.Lookup("foo.get_bar").signatures f2, = bar.Lookup("bar.get_foo").signatures self.assertEqual("bar.Bar", f1.return_type.cls.name) self.assertEqual("foo.Foo", f2.return_type.cls.name)
def test_reingest_custom_protocol_error(self): ty = self.Infer(""" from typing_extensions import Protocol class Appendable(Protocol): def append(self) -> None: pass """) with file_utils.Tempdir() as d: d.create_file("foo.pyi", pytd.Print(ty)) errors = self.CheckWithErrors("""\ import foo class NotAppendable(object): pass def f(x: foo.Appendable): pass f(42) # error f(NotAppendable()) # error """, pythonpath=[d.path]) self.assertErrorLogIs( errors, [(6, "wrong-arg-types", r"Appendable.*int.*append"), (7, "wrong-arg-types", r"Appendable.*NotAppendable.*append")])
def testAttrAndModule(self): with file_utils.Tempdir() as d: d.create_file("foo/__init__.pyi", "class X: ...") d.create_file("foo/bar.pyi", "v: str") d.create_file( "other.pyi", """ from foo import X as X from foo import bar as bar """) ty = self.Infer(""" import other X = other.X v = other.bar.v """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ from typing import Type import foo other: module X: Type[foo.X] v: str """)
def testImportPackageAliasNameConflict3(self): with file_utils.Tempdir() as d: d.create_file("a.pyi", "A: str") d.create_file("b.pyi", "A: int") d.create_file( "c.pyi", """ import b as a import a as _a x = _a.A y = a.A """) ty = self.Infer(""" import c x = c.x y = c.y """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ c: module x: str y: int """)
def test_import_map_congruence(self): with file_utils.Tempdir() as d: foo_path = d.create_file("foo.pyi", "class X: ...") bar_path = d.create_file("bar.pyi", "X = ... # type: another.foo.X") # Map the same pyi file under two module paths. imports_map = { "foo": foo_path, "another/foo": foo_path, "bar": bar_path, "empty1": "/dev/null", "empty2": "/dev/null", } # We cannot use tweak(imports_info=...) because that doesn't trigger # post-processing and we need an imports_map for the loader. loader = load_pytd.Loader("base", self.python_version, imports_map=imports_map, pythonpath=[""]) normal = loader.import_name("foo") self.assertEqual("foo", normal.name) loader.import_name( "bar") # check that we can resolve against another.foo another = loader.import_name("another.foo") # We do *not* treat foo.X and another.foo.X the same, because Python # doesn't, either: self.assertIsNot(normal, another) self.assertTrue([c.name.startswith("foo") for c in normal.classes]) self.assertTrue( [c.name.startswith("another.foo") for c in another.classes]) # Make sure that multiple modules using /dev/null are not treated as # congruent. empty1 = loader.import_name("empty1") empty2 = loader.import_name("empty2") self.assertIsNot(empty1, empty2) self.assertEqual("empty1", empty1.name) self.assertEqual("empty2", empty2.name)
def testFuncMatchForPytdClassError(self): with file_utils.Tempdir() as d: d.create_file( "a.pyi", """ from typing import TypeVar, Generic T1 = TypeVar('T1') S1 = TypeVar('S1') T2 = TypeVar('T2') S2 = TypeVar('S2') T = TypeVar('T') S = TypeVar('S') class A(Generic[T1, S1]): def fun1(self, x: T1, y: S1): ... class B(Generic[T2, S2]): def fun2(self, x: T2, y: S2): ... class C(A[T, S], B[T, S], Generic[T, S]): def fun3(self, x: T, y: S): ... """) _, errors = self.InferWithErrors("""\ import a o = a.C[int, int]() o.fun1("5", "5") o.fun2("5", "5") o.fun3("5", "5") """, deep=True, pythonpath=[d.path]) self.assertErrorLogIs(errors, [(5, "wrong-arg-types", r"int.*str"), (6, "wrong-arg-types", r"int.*str"), (7, "wrong-arg-types", r"int.*str")])
def test_default_params(self): with file_utils.Tempdir() as d: d.create_file( "bar.pyi", """ from dataclasses import dataclass @dataclass class A: x: bool y: int = ... """) d.create_file( "foo.pyi", """ from dataclasses import dataclass import bar @dataclass class B(bar.A): z: str = ... """) ty = self.Infer(""" import dataclasses import foo @dataclasses.dataclass class Foo(foo.B): a: str = "hello" """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ import dataclasses import foo from typing import Dict, Union @dataclasses.dataclass class Foo(foo.B): a: str __dataclass_fields__: Dict[str, dataclasses.Field[Union[int, str]]] def __init__(self, x: bool, y: int = ..., z: str = ..., a: str = ...) -> None: ... """)
def test_mutablesequence_and_list(self): # TODO(b/63407497): Enabling --strict-parameter-checks leads to a # wrong-arg-types error on line 10. self.options.tweak(strict_parameter_checks=False) with file_utils.Tempdir() as d: d.create_file("foo.pyi", """ from typing import List, MutableSequence def seq() -> MutableSequence[str]: ... def lst() -> List[str]: ... """) ty = self.Infer(""" import foo for seq in [foo.seq(), foo.lst()]: seq[0] = 3 del seq[0] a = seq.append(3) c = seq.insert(3, "foo") d = seq.reverse() e = seq.pop() f = seq.pop(4) g = seq.remove("foo") seq[0:5] = [1,2,3] b = seq.extend([1,2,3]) """, deep=False, pythonpath=[d.path]) self.assertTypesMatchPytd(ty, """ import foo from typing import Iterator, List, Sequence, Union # TODO(b/159065400): Should be List[Union[int, str]] seq = ... # type: Union[list, typing.MutableSequence[Union[int, str]]] a = ... # type: None b = ... # type: None c = ... # type: None d = ... # type: None e = ... # type: Union[int, str] f = ... # type: Union[int, str] g = ... # type: None """)
def testSuper(self): with file_utils.Tempdir() as d: d.create_file( "foo.pyi", """ from typing import Type def f(x: type): ... def g(x: Type[super]): ... """) ty = self.Infer(""" from typing import Any, Type import foo def f(x): ... def g(x: object): ... def h(x: Any): ... def i(x: type): ... def j(x: Type[super]): ... f(super) g(super) h(super) i(super) j(super) foo.f(super) foo.g(super) v = super """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ from typing import Any, Type foo = ... # type: module def f(x) -> None: ... def g(x: object) -> None: ... def h(x: Any) -> None: ... def i(x: type) -> None: ... def j(x: Type[super]) -> None: ... v = ... # type: Type[super] """)
def test_mapping(self): with file_utils.Tempdir() as d: d.create_file( "foo.pyi", """ from typing import Mapping K = TypeVar("K") V = TypeVar("V") class MyDict(Mapping[K, V]): ... def f() -> MyDict[str, int] """) ty = self.Infer(""" import foo m = foo.f() a = m.copy() b = "foo" in m c = m["foo"] d = m.get("foo", 3) e = [x for x in m.items()] f = [x for x in m.keys()] g = [x for x in m.values()] """, deep=False, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ from typing import List, Tuple, Union import foo foo = ... # type: module m = ... # type: foo.MyDict[str, int] a = ... # type: typing.Mapping[str, int] b = ... # type: bool c = ... # type: int d = ... # type: int e = ... # type: List[Tuple[str, int]] f = ... # type: List[str] g = ... # type: List[int] """)
def test_instance_attribute(self): with file_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 def f(): return a.A().x def g(): return a.A([42]).x """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ from typing import Any, Union a = ... # type: module def f() -> Union[int, float]: ... def g() -> int: ... """)
def test_type_parameter_subclass(self): """Test subclassing A[T] with T undefined and a type that depends on T.""" with file_utils.Tempdir() as d: d.create_file( "a.pyi", """ from typing import Generic, List T = TypeVar("T") class A(Generic[T]): data = ... # type: List[T] """) ty = self.Infer(""" import a class B(a.A): def foo(self): return self.data """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ a = ... # type: module class B(a.A): data = ... # type: list def foo(self) -> list: ... """)
def test_basic_enum_from_pyi(self): with file_utils.Tempdir() as d: d.create_file( "e.pyi", """ enum: module class Colors(enum.Enum): RED: int BLUE: int GREEN: int """) ty = self.Infer(""" import e c = e.Colors.RED n = e.Colors.BLUE.name v = e.Colors.GREEN.value """, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ e: module c: e.Colors n: str v: int """)
def test_value_lookup_pytd(self): with file_utils.Tempdir() as d: d.create_file( "m.pyi", """ enum: module class M(enum.Enum): A: int class N(enum.Enum): A: int B: str """) self.CheckWithErrors(""" from typing import Union from m import M, N assert_type(M(1), "m.M") # assert_type(M(1).value, "int") assert_type(M(-500), "m.M") M("str") # wrong-arg-types assert_type(N(1), "m.N") assert_type(N("str"), "m.N") # assert_type(N(499).value, "Union[int, str]") N(M.A) # wrong-arg-types """, pythonpath=[d.path])
def test_namedtuple_item(self): with file_utils.Tempdir() as d: d.create_file( "foo.pyi", """ from typing import NamedTuple def f() -> NamedTuple("ret", [("x", int), ("y", str)]) """) 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: module w: str x: int y: str z: int or str """)
def test_metaclass_getattribute(self): with file_utils.Tempdir() as d: d.create_file( "enum.pyi", """ from typing import Any class EnumMeta(type): def __getattribute__(self, name) -> Any: ... class Enum(metaclass=EnumMeta): ... class IntEnum(int, Enum): ... """) ty = self.Infer(""" import enum class A(enum.Enum): x = 1 class B(enum.IntEnum): x = 1 enum1 = A.x name1 = A.x.name enum2 = B.x name2 = B.x.name """, deep=False, pythonpath=[d.path]) self.assertTypesMatchPytd( ty, """ from typing import Any enum = ... # type: module class A(enum.Enum): x = ... # type: int class B(enum.IntEnum): x = ... # type: int enum1 = ... # type: Any name1 = ... # type: Any enum2 = ... # type: Any name2 = ... # type: Any """)
def testPropertyTypeParam(self): # We should allow property signatures of the form f(self: T) -> X[T] # without complaining about the class not being parametrised over T with file_utils.Tempdir() as d: d.create_file("a.pyi", """ from typing import TypeVar, List T = TypeVar('T') class A(object): @property def foo(self: T) -> List[T]: ... class B(A): ... """) ty = self.Infer(""" import a x = a.A().foo y = a.B().foo """, pythonpath=[d.path]) self.assertTypesMatchPytd(ty, """ from typing import List import a a = ... # type: module x = ... # type: List[a.A] y = ... # type: List[a.B] """)
def testInstanceAttributeInherited(self): with file_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]) 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 testInterpreterSubclass(self): with file_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]) self.assertTypesMatchPytd(ty, """ a = ... # type: module class B(a.A): pass def foo() -> str def bar() -> str """)