| -- Test cases for generating fine-grained dependencies for classes. | |
| -- | |
| -- The dependencies are used for fined-grained incremental checking. | |
| -- | |
| -- See the comment at the top of deps.test for more documentation. | |
| -- TODO: Move class related test cases from deps.test to here | |
| [case testNamedTuple] | |
| from typing import NamedTuple, Any | |
| from a import A | |
| N = NamedTuple('N', [('a', 'A')]) | |
| def f(a: Any) -> None: | |
| n = N(a) | |
| n.a | |
| [file a.py] | |
| class A: pass | |
| [builtins fixtures/tuple.pyi] | |
| [out] | |
| <m.N.__init__> -> m.f | |
| <m.N.__new__> -> m.f | |
| <m.N.a> -> m.f | |
| <m.N> -> m.f | |
| <a.A> -> <m.N.a>, <m.N>, m | |
| <a> -> m | |
| [case testNamedTuple2] | |
| from typing import NamedTuple, Any, Tuple | |
| from a import A, B | |
| N = NamedTuple('N', [('a', 'Tuple[A, B]')]) | |
| def f(a: Any) -> None: | |
| n = N(a) | |
| n.a | |
| [file a.py] | |
| class A: pass | |
| class B: pass | |
| [builtins fixtures/tuple.pyi] | |
| [out] | |
| <m.N.__init__> -> m.f | |
| <m.N.__new__> -> m.f | |
| <m.N.a> -> m.f | |
| <m.N> -> m.f | |
| <a.A> -> <m.N.a>, <m.N>, m | |
| <a.B> -> <m.N.a>, <m.N>, m | |
| <a> -> m | |
| [case testNamedTuple3] | |
| from typing import NamedTuple | |
| N = NamedTuple('N', [('x', int)]) | |
| x = N(1) | |
| M = NamedTuple('M', [('z', 'N')]) | |
| y = M(x) | |
| [builtins fixtures/tuple.pyi] | |
| [out] | |
| <m.M.__init__> -> m | |
| <m.M.__new__> -> m | |
| <m.M> -> <m.y>, m | |
| <m.N.__init__> -> m | |
| <m.N.__new__> -> m | |
| <m.N> -> <m.M.z>, <m.M>, <m.x>, <m.y>, m | |
| <m.x> -> m | |
| <m.y> -> m | |
| [case testNamedTuple4] | |
| from typing import NamedTuple, Any | |
| from a import A | |
| class N(NamedTuple): | |
| a: A | |
| def f(a: Any) -> None: | |
| n = N(a) | |
| n.a | |
| [file a.py] | |
| class A: pass | |
| [builtins fixtures/tuple.pyi] | |
| [out] | |
| <m.N.__init__> -> m.f | |
| <m.N.__new__> -> m.f | |
| <m.N.a> -> m.f | |
| <m.N> -> m.N, m.f | |
| <a.A> -> <m.N.a>, <m.N>, m, m.N | |
| <a> -> m | |
| [case testIfFalseInClassBody] | |
| class A: | |
| if False: | |
| x = None # type: str | |
| x.foo() | |
| [builtins fixtures/bool.pyi] | |
| [out] | |
| <m.A> -> m.A | |
| [case testAlwaysFalseIsinstanceInClassBody] | |
| class A: | |
| x: int | |
| if isinstance(x, str): | |
| y: str = None | |
| y.foo() | |
| [builtins fixtures/isinstance.pyi] | |
| [out] | |
| <m.A> -> m.A | |
| [case testDoubleAttributeInitializationToNone] | |
| class C: | |
| def __init__(self) -> None: | |
| self.x = None | |
| self.x = None | |
| [out] | |
| <m.C.x> -> m.C.__init__ | |
| <m.C> -> m.C | |
| [case testClassNestedWithinFunction] | |
| class C: pass | |
| def f() -> None: | |
| class S1(C): pass | |
| class D: | |
| def g(self) -> None: | |
| class S2(C): pass | |
| [out] | |
| -- TODO: Is it okay to have targets like m.S1@4.__init__? | |
| <m.C.(abstract)> -> <m.S1@4.__init__>, <m.S2@8.__init__>, m.D.g, m.f | |
| <m.C.__init__> -> <m.S1@4.__init__>, <m.S2@8.__init__> | |
| <m.C.__new__> -> <m.S1@4.__new__>, <m.S2@8.__new__> | |
| <m.C> -> m.C, m.D.g, m.f | |
| <m.D.g> -> m.D.g | |
| <m.D> -> m.D | |
| <m.f> -> m.f | |
| [case testClassSuper] | |
| class C: | |
| def __init__(self, x: int) -> None: pass | |
| def foo(self) -> None: pass | |
| class D(C): | |
| def __init__(self, x: int) -> None: | |
| super().__init__(x) | |
| super().foo() | |
| [out] | |
| <m.C.(abstract)> -> <m.D.__init__>, m | |
| <m.C.__init__> -> <m.D.__init__>, m.D.__init__ | |
| <m.C.__new__> -> <m.D.__new__> | |
| <m.C.foo> -> <m.D.foo>, m.D.__init__ | |
| <m.C> -> m, m.C, m.D | |
| <m.D> -> m.D | |
| [case testClassMissingInit] | |
| class C: | |
| def __init__(self, x: int) -> None: pass | |
| class D(C): | |
| pass | |
| def foo() -> None: | |
| D(6) | |
| [out] | |
| <m.C.(abstract)> -> <m.D.__init__>, m | |
| <m.C.__init__> -> <m.D.__init__> | |
| <m.C.__new__> -> <m.D.__new__> | |
| <m.C> -> m, m.C, m.D | |
| <m.D.__init__> -> m.foo | |
| <m.D.__new__> -> m.foo | |
| <m.D> -> m.D, m.foo | |
| [case testClassBasedEnum] | |
| from enum import Enum | |
| from m import B | |
| class A(Enum): | |
| X = B() | |
| def f(a: A) -> None: | |
| pass | |
| def g() -> None: | |
| A.X | |
| [file m.py] | |
| class B: pass | |
| [builtins fixtures/enum.pyi] | |
| [out] | |
| <m.A.X> -> m.g | |
| <m.A> -> <m.f>, m.A, m.f, m.g | |
| <m.B.__init__> -> m | |
| <m.B.__new__> -> m | |
| -- The <m.A.X> dependency target is superfluous but benign | |
| <m.B> -> <m.A.X>, m | |
| <m> -> m | |
| [case testClassAttribute] | |
| class C: | |
| x = 0 | |
| def f() -> None: | |
| C.x | |
| def g() -> None: | |
| C.x = 1 | |
| [out] | |
| <m.C.x> -> m.f, m.g | |
| <m.C> -> m.C, m.f, m.g | |
| [case testStaticAndClassMethods] | |
| class C: | |
| @staticmethod | |
| def foo() -> None: | |
| h() | |
| @classmethod | |
| def bar(cls) -> None: | |
| h() | |
| def fstatic() -> None: | |
| C.foo() | |
| def fclass() -> None: | |
| C.bar() | |
| cc = C() | |
| def gstatic() -> None: | |
| cc.foo() | |
| def gclass() -> None: | |
| cc.bar() | |
| def h() -> None: pass | |
| [builtins fixtures/classmethod.pyi] | |
| [out] | |
| <m.C.__init__> -> m | |
| <m.C.__new__> -> m | |
| <m.C.bar> -> m, m.fclass, m.gclass | |
| <m.C.foo> -> m, m.fstatic, m.gstatic | |
| <m.C> -> <m.cc>, m, m.C, m.fclass, m.fstatic | |
| <m.cc> -> m, m.gclass, m.gstatic | |
| <m.h> -> m.C.bar, m.C.foo | |
| [case testClassAttributeWithMetaclass] | |
| class M(type): | |
| x = 1 | |
| class C(metaclass=M): | |
| pass | |
| def f() -> None: | |
| C.x | |
| [out] | |
| <m.C.x> -> m.f | |
| <m.C> -> m.C, m.f | |
| <m.M.x> -> m.f | |
| <m.M> -> <m.C>, m, m.M |