| [case testNarrowingParentWithStrsBasic] |
| from dataclasses import dataclass |
| from typing import Literal, NamedTuple, Tuple, TypedDict, Union |
| |
| class Object1: |
| key: Literal["A"] |
| foo: int |
| class Object2: |
| key: Literal["B"] |
| bar: str |
| |
| @dataclass |
| class Dataclass1: |
| key: Literal["A"] |
| foo: int |
| @dataclass |
| class Dataclass2: |
| key: Literal["B"] |
| foo: str |
| |
| class NamedTuple1(NamedTuple): |
| key: Literal["A"] |
| foo: int |
| class NamedTuple2(NamedTuple): |
| key: Literal["B"] |
| foo: str |
| |
| Tuple1 = Tuple[Literal["A"], int] |
| Tuple2 = Tuple[Literal["B"], str] |
| |
| class TypedDict1(TypedDict): |
| key: Literal["A"] |
| foo: int |
| class TypedDict2(TypedDict): |
| key: Literal["B"] |
| foo: str |
| |
| x1: Union[Object1, Object2] |
| if x1.key == "A": |
| reveal_type(x1) # N: Revealed type is "__main__.Object1" |
| reveal_type(x1.key) # N: Revealed type is "Literal['A']" |
| else: |
| reveal_type(x1) # N: Revealed type is "__main__.Object2" |
| reveal_type(x1.key) # N: Revealed type is "Literal['B']" |
| |
| x2: Union[Dataclass1, Dataclass2] |
| if x2.key == "A": |
| reveal_type(x2) # N: Revealed type is "__main__.Dataclass1" |
| reveal_type(x2.key) # N: Revealed type is "Literal['A']" |
| else: |
| reveal_type(x2) # N: Revealed type is "__main__.Dataclass2" |
| reveal_type(x2.key) # N: Revealed type is "Literal['B']" |
| |
| x3: Union[NamedTuple1, NamedTuple2] |
| if x3.key == "A": |
| reveal_type(x3) # N: Revealed type is "tuple[Literal['A'], builtins.int, fallback=__main__.NamedTuple1]" |
| reveal_type(x3.key) # N: Revealed type is "Literal['A']" |
| else: |
| reveal_type(x3) # N: Revealed type is "tuple[Literal['B'], builtins.str, fallback=__main__.NamedTuple2]" |
| reveal_type(x3.key) # N: Revealed type is "Literal['B']" |
| if x3[0] == "A": |
| reveal_type(x3) # N: Revealed type is "tuple[Literal['A'], builtins.int, fallback=__main__.NamedTuple1]" |
| reveal_type(x3[0]) # N: Revealed type is "Literal['A']" |
| else: |
| reveal_type(x3) # N: Revealed type is "tuple[Literal['B'], builtins.str, fallback=__main__.NamedTuple2]" |
| reveal_type(x3[0]) # N: Revealed type is "Literal['B']" |
| |
| x4: Union[Tuple1, Tuple2] |
| if x4[0] == "A": |
| reveal_type(x4) # N: Revealed type is "tuple[Literal['A'], builtins.int]" |
| reveal_type(x4[0]) # N: Revealed type is "Literal['A']" |
| else: |
| reveal_type(x4) # N: Revealed type is "tuple[Literal['B'], builtins.str]" |
| reveal_type(x4[0]) # N: Revealed type is "Literal['B']" |
| |
| x5: Union[TypedDict1, TypedDict2] |
| if x5["key"] == "A": |
| reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': Literal['A'], 'foo': builtins.int})" |
| else: |
| reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': Literal['B'], 'foo': builtins.str})" |
| [builtins fixtures/primitives.pyi] |
| [typing fixtures/typing-typeddict.pyi] |
| |
| [case testNarrowingParentWithEnumsBasic] |
| from enum import Enum |
| from dataclasses import dataclass |
| from typing import Literal, NamedTuple, Tuple, TypedDict, Union |
| |
| class Key(Enum): |
| A = 1 |
| B = 2 |
| C = 3 |
| |
| class Object1: |
| key: Literal[Key.A] |
| foo: int |
| class Object2: |
| key: Literal[Key.B] |
| bar: str |
| |
| @dataclass |
| class Dataclass1: |
| key: Literal[Key.A] |
| foo: int |
| @dataclass |
| class Dataclass2: |
| key: Literal[Key.B] |
| foo: str |
| |
| class NamedTuple1(NamedTuple): |
| key: Literal[Key.A] |
| foo: int |
| class NamedTuple2(NamedTuple): |
| key: Literal[Key.B] |
| foo: str |
| |
| Tuple1 = Tuple[Literal[Key.A], int] |
| Tuple2 = Tuple[Literal[Key.B], str] |
| |
| class TypedDict1(TypedDict): |
| key: Literal[Key.A] |
| foo: int |
| class TypedDict2(TypedDict): |
| key: Literal[Key.B] |
| foo: str |
| |
| x1: Union[Object1, Object2] |
| if x1.key is Key.A: |
| reveal_type(x1) # N: Revealed type is "__main__.Object1" |
| reveal_type(x1.key) # N: Revealed type is "Literal[__main__.Key.A]" |
| else: |
| reveal_type(x1) # N: Revealed type is "__main__.Object2" |
| reveal_type(x1.key) # N: Revealed type is "Literal[__main__.Key.B]" |
| |
| x2: Union[Dataclass1, Dataclass2] |
| if x2.key is Key.A: |
| reveal_type(x2) # N: Revealed type is "__main__.Dataclass1" |
| reveal_type(x2.key) # N: Revealed type is "Literal[__main__.Key.A]" |
| else: |
| reveal_type(x2) # N: Revealed type is "__main__.Dataclass2" |
| reveal_type(x2.key) # N: Revealed type is "Literal[__main__.Key.B]" |
| |
| x3: Union[NamedTuple1, NamedTuple2] |
| if x3.key is Key.A: |
| reveal_type(x3) # N: Revealed type is "tuple[Literal[__main__.Key.A], builtins.int, fallback=__main__.NamedTuple1]" |
| reveal_type(x3.key) # N: Revealed type is "Literal[__main__.Key.A]" |
| else: |
| reveal_type(x3) # N: Revealed type is "tuple[Literal[__main__.Key.B], builtins.str, fallback=__main__.NamedTuple2]" |
| reveal_type(x3.key) # N: Revealed type is "Literal[__main__.Key.B]" |
| if x3[0] is Key.A: |
| reveal_type(x3) # N: Revealed type is "tuple[Literal[__main__.Key.A], builtins.int, fallback=__main__.NamedTuple1]" |
| reveal_type(x3[0]) # N: Revealed type is "Literal[__main__.Key.A]" |
| else: |
| reveal_type(x3) # N: Revealed type is "tuple[Literal[__main__.Key.B], builtins.str, fallback=__main__.NamedTuple2]" |
| reveal_type(x3[0]) # N: Revealed type is "Literal[__main__.Key.B]" |
| |
| x4: Union[Tuple1, Tuple2] |
| if x4[0] is Key.A: |
| reveal_type(x4) # N: Revealed type is "tuple[Literal[__main__.Key.A], builtins.int]" |
| reveal_type(x4[0]) # N: Revealed type is "Literal[__main__.Key.A]" |
| else: |
| reveal_type(x4) # N: Revealed type is "tuple[Literal[__main__.Key.B], builtins.str]" |
| reveal_type(x4[0]) # N: Revealed type is "Literal[__main__.Key.B]" |
| |
| x5: Union[TypedDict1, TypedDict2] |
| if x5["key"] is Key.A: |
| reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': Literal[__main__.Key.A], 'foo': builtins.int})" |
| else: |
| reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': Literal[__main__.Key.B], 'foo': builtins.str})" |
| [builtins fixtures/dict.pyi] |
| [typing fixtures/typing-typeddict.pyi] |
| |
| [case testNarrowingParentWithIsInstanceBasic] |
| from dataclasses import dataclass |
| from typing import NamedTuple, Tuple, TypedDict, Union |
| |
| class Object1: |
| key: int |
| class Object2: |
| key: str |
| |
| @dataclass |
| class Dataclass1: |
| key: int |
| @dataclass |
| class Dataclass2: |
| key: str |
| |
| class NamedTuple1(NamedTuple): |
| key: int |
| class NamedTuple2(NamedTuple): |
| key: str |
| |
| Tuple1 = Tuple[int] |
| Tuple2 = Tuple[str] |
| |
| class TypedDict1(TypedDict): |
| key: int |
| class TypedDict2(TypedDict): |
| key: str |
| |
| x1: Union[Object1, Object2] |
| if isinstance(x1.key, int): |
| reveal_type(x1) # N: Revealed type is "__main__.Object1" |
| else: |
| reveal_type(x1) # N: Revealed type is "__main__.Object2" |
| |
| x2: Union[Dataclass1, Dataclass2] |
| if isinstance(x2.key, int): |
| reveal_type(x2) # N: Revealed type is "__main__.Dataclass1" |
| else: |
| reveal_type(x2) # N: Revealed type is "__main__.Dataclass2" |
| |
| x3: Union[NamedTuple1, NamedTuple2] |
| if isinstance(x3.key, int): |
| reveal_type(x3) # N: Revealed type is "tuple[builtins.int, fallback=__main__.NamedTuple1]" |
| else: |
| reveal_type(x3) # N: Revealed type is "tuple[builtins.str, fallback=__main__.NamedTuple2]" |
| if isinstance(x3[0], int): |
| reveal_type(x3) # N: Revealed type is "tuple[builtins.int, fallback=__main__.NamedTuple1]" |
| else: |
| reveal_type(x3) # N: Revealed type is "tuple[builtins.str, fallback=__main__.NamedTuple2]" |
| |
| x4: Union[Tuple1, Tuple2] |
| if isinstance(x4[0], int): |
| reveal_type(x4) # N: Revealed type is "tuple[builtins.int]" |
| else: |
| reveal_type(x4) # N: Revealed type is "tuple[builtins.str]" |
| |
| x5: Union[TypedDict1, TypedDict2] |
| if isinstance(x5["key"], int): |
| reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': builtins.int})" |
| else: |
| reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': builtins.str})" |
| [builtins fixtures/dict.pyi] |
| [typing fixtures/typing-typeddict.pyi] |
| |
| [case testNarrowingParentMultipleKeys] |
| # flags: --warn-unreachable |
| from enum import Enum |
| from typing import Literal, Union |
| |
| class Key(Enum): |
| A = 1 |
| B = 2 |
| C = 3 |
| D = 4 |
| |
| class Object1: |
| key: Literal[Key.A, Key.C] |
| class Object2: |
| key: Literal[Key.B, Key.C] |
| |
| x: Union[Object1, Object2] |
| if x.key is Key.A: |
| reveal_type(x) # N: Revealed type is "__main__.Object1" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.Object1 | __main__.Object2" |
| |
| if x.key is Key.C: |
| reveal_type(x) # N: Revealed type is "__main__.Object1 | __main__.Object2" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.Object1 | __main__.Object2" |
| |
| if x.key is Key.D: |
| reveal_type(x) # E: Statement is unreachable |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.Object1 | __main__.Object2" |
| [builtins fixtures/tuple.pyi] |
| |
| [case testNarrowingTypedDictParentMultipleKeys] |
| # flags: --warn-unreachable |
| from typing import Literal, TypedDict, Union |
| |
| class TypedDict1(TypedDict): |
| key: Literal['A', 'C'] |
| class TypedDict2(TypedDict): |
| key: Literal['B', 'C'] |
| |
| x: Union[TypedDict1, TypedDict2] |
| if x['key'] == 'A': |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': Literal['A'] | Literal['C']})" |
| else: |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': Literal['A'] | Literal['C']}) | TypedDict('__main__.TypedDict2', {'key': Literal['B'] | Literal['C']})" |
| |
| if x['key'] == 'C': |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': Literal['A'] | Literal['C']}) | TypedDict('__main__.TypedDict2', {'key': Literal['B'] | Literal['C']})" |
| else: |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': Literal['A'] | Literal['C']}) | TypedDict('__main__.TypedDict2', {'key': Literal['B'] | Literal['C']})" |
| |
| if x['key'] == 'D': |
| reveal_type(x) # E: Statement is unreachable |
| else: |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': Literal['A'] | Literal['C']}) | TypedDict('__main__.TypedDict2', {'key': Literal['B'] | Literal['C']})" |
| [builtins fixtures/primitives.pyi] |
| [typing fixtures/typing-typeddict.pyi] |
| |
| [case testNarrowingPartialTypedDictParentMultipleKeys] |
| # flags: --warn-unreachable |
| from typing import Literal, TypedDict, Union |
| |
| class TypedDict1(TypedDict, total=False): |
| key: Literal['A', 'C'] |
| class TypedDict2(TypedDict, total=False): |
| key: Literal['B', 'C'] |
| |
| x: Union[TypedDict1, TypedDict2] |
| if x['key'] == 'A': |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key'?: Literal['A'] | Literal['C']})" |
| else: |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key'?: Literal['A'] | Literal['C']}) | TypedDict('__main__.TypedDict2', {'key'?: Literal['B'] | Literal['C']})" |
| |
| if x['key'] == 'C': |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key'?: Literal['A'] | Literal['C']}) | TypedDict('__main__.TypedDict2', {'key'?: Literal['B'] | Literal['C']})" |
| else: |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key'?: Literal['A'] | Literal['C']}) | TypedDict('__main__.TypedDict2', {'key'?: Literal['B'] | Literal['C']})" |
| |
| if x['key'] == 'D': |
| reveal_type(x) # E: Statement is unreachable |
| else: |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key'?: Literal['A'] | Literal['C']}) | TypedDict('__main__.TypedDict2', {'key'?: Literal['B'] | Literal['C']})" |
| [builtins fixtures/primitives.pyi] |
| [typing fixtures/typing-typeddict.pyi] |
| |
| [case testNarrowingNestedTypedDicts] |
| from typing import Literal, TypedDict, Union |
| |
| class A(TypedDict): |
| key: Literal['A'] |
| class B(TypedDict): |
| key: Literal['B'] |
| class C(TypedDict): |
| key: Literal['C'] |
| |
| class X(TypedDict): |
| inner: Union[A, B] |
| class Y(TypedDict): |
| inner: Union[B, C] |
| |
| unknown: Union[X, Y] |
| if unknown['inner']['key'] == 'A': |
| reveal_type(unknown) # N: Revealed type is "TypedDict('__main__.X', {'inner': TypedDict('__main__.A', {'key': Literal['A']}) | TypedDict('__main__.B', {'key': Literal['B']})})" |
| reveal_type(unknown['inner']) # N: Revealed type is "TypedDict('__main__.A', {'key': Literal['A']})" |
| if unknown['inner']['key'] == 'B': |
| reveal_type(unknown) # N: Revealed type is "TypedDict('__main__.X', {'inner': TypedDict('__main__.A', {'key': Literal['A']}) | TypedDict('__main__.B', {'key': Literal['B']})}) | TypedDict('__main__.Y', {'inner': TypedDict('__main__.B', {'key': Literal['B']}) | TypedDict('__main__.C', {'key': Literal['C']})})" |
| reveal_type(unknown['inner']) # N: Revealed type is "TypedDict('__main__.B', {'key': Literal['B']})" |
| if unknown['inner']['key'] == 'C': |
| reveal_type(unknown) # N: Revealed type is "TypedDict('__main__.Y', {'inner': TypedDict('__main__.B', {'key': Literal['B']}) | TypedDict('__main__.C', {'key': Literal['C']})})" |
| reveal_type(unknown['inner']) # N: Revealed type is "TypedDict('__main__.C', {'key': Literal['C']})" |
| [builtins fixtures/primitives.pyi] |
| [typing fixtures/typing-typeddict.pyi] |
| |
| [case testNarrowingParentWithMultipleParents] |
| from enum import Enum |
| from typing import Literal, Union |
| |
| class Key(Enum): |
| A = 1 |
| B = 2 |
| C = 3 |
| |
| class Object1: |
| key: Literal[Key.A] |
| class Object2: |
| key: Literal[Key.B] |
| class Object3: |
| key: Literal[Key.C] |
| class Object4: |
| key: str |
| |
| x: Union[Object1, Object2, Object3, Object4] |
| if x.key is Key.A: |
| reveal_type(x) # N: Revealed type is "__main__.Object1" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.Object2 | __main__.Object3 | __main__.Object4" |
| |
| if isinstance(x.key, str): |
| reveal_type(x) # N: Revealed type is "__main__.Object4" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.Object1 | __main__.Object2 | __main__.Object3" |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testNarrowingParentsWithGenerics] |
| from typing import Union, TypeVar, Generic |
| |
| T = TypeVar('T') |
| class Wrapper(Generic[T]): |
| key: T |
| |
| x: Union[Wrapper[int], Wrapper[str]] |
| if isinstance(x.key, int): |
| reveal_type(x) # N: Revealed type is "__main__.Wrapper[builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.Wrapper[builtins.str]" |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testNarrowingParentWithParentMixtures] |
| from enum import Enum |
| from typing import Literal, Union, NamedTuple, TypedDict |
| |
| class Key(Enum): |
| A = 1 |
| B = 2 |
| C = 3 |
| |
| class KeyedObject: |
| key: Literal[Key.A] |
| class KeyedTypedDict(TypedDict): |
| key: Literal[Key.B] |
| class KeyedNamedTuple(NamedTuple): |
| key: Literal[Key.C] |
| |
| ok_mixture: Union[KeyedObject, KeyedNamedTuple] |
| if ok_mixture.key is Key.A: |
| reveal_type(ok_mixture) # N: Revealed type is "__main__.KeyedObject" |
| else: |
| reveal_type(ok_mixture) # N: Revealed type is "tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]" |
| |
| impossible_mixture: Union[KeyedObject, KeyedTypedDict] |
| if impossible_mixture.key is Key.A: # E: Item "KeyedTypedDict" of "KeyedObject | KeyedTypedDict" has no attribute "key" |
| reveal_type(impossible_mixture) # N: Revealed type is "__main__.KeyedObject | TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]})" |
| else: |
| reveal_type(impossible_mixture) # N: Revealed type is "__main__.KeyedObject | TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]})" |
| |
| if impossible_mixture["key"] is Key.A: # E: Value of type "KeyedObject | KeyedTypedDict" is not indexable |
| reveal_type(impossible_mixture) # N: Revealed type is "__main__.KeyedObject | TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]})" |
| else: |
| reveal_type(impossible_mixture) # N: Revealed type is "__main__.KeyedObject | TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]})" |
| |
| weird_mixture: Union[KeyedTypedDict, KeyedNamedTuple] |
| if weird_mixture["key"] is Key.B: # E: No overload variant of "__getitem__" of "tuple" matches argument type "str" \ |
| # N: Possible overload variants: \ |
| # N: def __getitem__(self, int, /) -> Literal[Key.C] \ |
| # N: def __getitem__(self, slice, /) -> tuple[Literal[Key.C], ...] |
| reveal_type(weird_mixture) # N: Revealed type is "TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}) | tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]" |
| else: |
| reveal_type(weird_mixture) # N: Revealed type is "TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}) | tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]" |
| |
| if weird_mixture[0] is Key.B: # E: TypedDict key must be a string literal; expected one of ("key") |
| reveal_type(weird_mixture) # N: Revealed type is "TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}) | tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]" |
| else: |
| reveal_type(weird_mixture) # N: Revealed type is "TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}) | tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]" |
| [builtins fixtures/tuple.pyi] |
| [typing fixtures/typing-full.pyi] |
| |
| [case testNarrowingParentWithProperties] |
| from enum import Enum |
| from typing import Literal, Union |
| |
| class Key(Enum): |
| A = 1 |
| B = 2 |
| C = 3 |
| |
| class Object1: |
| key: Literal[Key.A] |
| |
| class Object2: |
| @property |
| def key(self) -> Literal[Key.A]: ... |
| |
| class Object3: |
| @property |
| def key(self) -> Literal[Key.B]: ... |
| |
| x: Union[Object1, Object2, Object3] |
| if x.key is Key.A: |
| reveal_type(x) # N: Revealed type is "__main__.Object1 | __main__.Object2" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.Object3" |
| [builtins fixtures/property.pyi] |
| |
| [case testNarrowingParentWithAny] |
| from enum import Enum |
| from typing import Literal, Union, Any |
| |
| class Key(Enum): |
| A = 1 |
| B = 2 |
| C = 3 |
| |
| class Object1: |
| key: Literal[Key.A] |
| |
| class Object2: |
| key: Literal[Key.B] |
| |
| x: Union[Object1, Object2, Any] |
| if x.key is Key.A: |
| reveal_type(x.key) # N: Revealed type is "Literal[__main__.Key.A]" |
| reveal_type(x) # N: Revealed type is "__main__.Object1 | Any" |
| else: |
| # TODO: Is this a bug? Should we skip inferring Any for singleton types? |
| reveal_type(x.key) # N: Revealed type is "Any | Literal[__main__.Key.B]" |
| reveal_type(x) # N: Revealed type is "__main__.Object1 | __main__.Object2 | Any" |
| [builtins fixtures/tuple.pyi] |
| |
| [case testNarrowingParentsHierarchy] |
| from typing import Literal, Union |
| from enum import Enum |
| |
| class Key(Enum): |
| A = 1 |
| B = 2 |
| C = 3 |
| |
| class Parent1: |
| child: Union[Child1, Child2] |
| class Parent2: |
| child: Union[Child2, Child3] |
| class Parent3: |
| child: Union[Child3, Child1] |
| |
| class Child1: |
| main: Literal[Key.A] |
| same_for_1_and_2: Literal[Key.A] |
| class Child2: |
| main: Literal[Key.B] |
| same_for_1_and_2: Literal[Key.A] |
| class Child3: |
| main: Literal[Key.C] |
| same_for_1_and_2: Literal[Key.B] |
| |
| x: Union[Parent1, Parent2, Parent3] |
| if x.child.main is Key.A: |
| reveal_type(x) # N: Revealed type is "__main__.Parent1 | __main__.Parent3" |
| reveal_type(x.child) # N: Revealed type is "__main__.Child1" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.Parent1 | __main__.Parent2 | __main__.Parent3" |
| reveal_type(x.child) # N: Revealed type is "__main__.Child2 | __main__.Child3" |
| |
| if x.child.same_for_1_and_2 is Key.A: |
| reveal_type(x) # N: Revealed type is "__main__.Parent1 | __main__.Parent2 | __main__.Parent3" |
| reveal_type(x.child) # N: Revealed type is "__main__.Child1 | __main__.Child2" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.Parent2 | __main__.Parent3" |
| reveal_type(x.child) # N: Revealed type is "__main__.Child3" |
| |
| y: Union[Parent1, Parent2] |
| if y.child.main is Key.A: |
| reveal_type(y) # N: Revealed type is "__main__.Parent1" |
| reveal_type(y.child) # N: Revealed type is "__main__.Child1" |
| else: |
| reveal_type(y) # N: Revealed type is "__main__.Parent1 | __main__.Parent2" |
| reveal_type(y.child) # N: Revealed type is "__main__.Child2 | __main__.Child3" |
| |
| if y.child.same_for_1_and_2 is Key.A: |
| reveal_type(y) # N: Revealed type is "__main__.Parent1 | __main__.Parent2" |
| reveal_type(y.child) # N: Revealed type is "__main__.Child1 | __main__.Child2" |
| else: |
| reveal_type(y) # N: Revealed type is "__main__.Parent2" |
| reveal_type(y.child) # N: Revealed type is "__main__.Child3" |
| [builtins fixtures/tuple.pyi] |
| |
| [case testNarrowingParentsHierarchyGenerics] |
| from typing import Generic, TypeVar, Union |
| |
| T = TypeVar('T') |
| class Model(Generic[T]): |
| attr: T |
| class A: |
| model: Model[int] |
| class B: |
| model: Model[str] |
| |
| x: Union[A, B] |
| if isinstance(x.model.attr, int): |
| reveal_type(x) # N: Revealed type is "__main__.A" |
| reveal_type(x.model) # N: Revealed type is "__main__.Model[builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.B" |
| reveal_type(x.model) # N: Revealed type is "__main__.Model[builtins.str]" |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testNarrowingParentsHierarchyTypedDict] |
| # flags: --warn-unreachable |
| from typing import Literal, TypedDict, Union |
| from enum import Enum |
| |
| class Key(Enum): |
| A = 1 |
| B = 2 |
| C = 3 |
| |
| class Parent1(TypedDict): |
| model: Model1 |
| foo: int |
| |
| class Parent2(TypedDict): |
| model: Model2 |
| bar: str |
| |
| class Model1(TypedDict): |
| key: Literal[Key.A] |
| |
| class Model2(TypedDict): |
| key: Literal[Key.B] |
| |
| x: Union[Parent1, Parent2] |
| if x["model"]["key"] is Key.A: |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.Parent1', {'model': TypedDict('__main__.Model1', {'key': Literal[__main__.Key.A]}), 'foo': builtins.int})" |
| reveal_type(x["model"]) # N: Revealed type is "TypedDict('__main__.Model1', {'key': Literal[__main__.Key.A]})" |
| else: |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.Parent2', {'model': TypedDict('__main__.Model2', {'key': Literal[__main__.Key.B]}), 'bar': builtins.str})" |
| reveal_type(x["model"]) # N: Revealed type is "TypedDict('__main__.Model2', {'key': Literal[__main__.Key.B]})" |
| |
| y: Union[Parent1, Parent2] |
| if y["model"]["key"] is Key.C: |
| reveal_type(y) # E: Statement is unreachable |
| reveal_type(y["model"]) |
| else: |
| reveal_type(y) # N: Revealed type is "TypedDict('__main__.Parent1', {'model': TypedDict('__main__.Model1', {'key': Literal[__main__.Key.A]}), 'foo': builtins.int}) | TypedDict('__main__.Parent2', {'model': TypedDict('__main__.Model2', {'key': Literal[__main__.Key.B]}), 'bar': builtins.str})" |
| reveal_type(y["model"]) # N: Revealed type is "TypedDict('__main__.Model1', {'key': Literal[__main__.Key.A]}) | TypedDict('__main__.Model2', {'key': Literal[__main__.Key.B]})" |
| [builtins fixtures/dict.pyi] |
| [typing fixtures/typing-typeddict.pyi] |
| |
| [case testNarrowingParentsHierarchyTypedDictWithStr] |
| # flags: --warn-unreachable |
| from typing import Literal, TypedDict, Union |
| |
| class Parent1(TypedDict): |
| model: Model1 |
| foo: int |
| |
| class Parent2(TypedDict): |
| model: Model2 |
| bar: str |
| |
| class Model1(TypedDict): |
| key: Literal['A'] |
| |
| class Model2(TypedDict): |
| key: Literal['B'] |
| |
| x: Union[Parent1, Parent2] |
| if x["model"]["key"] == 'A': |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.Parent1', {'model': TypedDict('__main__.Model1', {'key': Literal['A']}), 'foo': builtins.int})" |
| reveal_type(x["model"]) # N: Revealed type is "TypedDict('__main__.Model1', {'key': Literal['A']})" |
| else: |
| reveal_type(x) # N: Revealed type is "TypedDict('__main__.Parent2', {'model': TypedDict('__main__.Model2', {'key': Literal['B']}), 'bar': builtins.str})" |
| reveal_type(x["model"]) # N: Revealed type is "TypedDict('__main__.Model2', {'key': Literal['B']})" |
| |
| y: Union[Parent1, Parent2] |
| if y["model"]["key"] == 'C': |
| reveal_type(y) # E: Statement is unreachable |
| reveal_type(y["model"]) |
| else: |
| reveal_type(y) # N: Revealed type is "TypedDict('__main__.Parent1', {'model': TypedDict('__main__.Model1', {'key': Literal['A']}), 'foo': builtins.int}) | TypedDict('__main__.Parent2', {'model': TypedDict('__main__.Model2', {'key': Literal['B']}), 'bar': builtins.str})" |
| reveal_type(y["model"]) # N: Revealed type is "TypedDict('__main__.Model1', {'key': Literal['A']}) | TypedDict('__main__.Model2', {'key': Literal['B']})" |
| [builtins fixtures/primitives.pyi] |
| [typing fixtures/typing-typeddict.pyi] |
| |
| [case testNarrowingExprPropagation] |
| from typing import Literal, Union |
| |
| class A: |
| tag: Literal['A'] |
| |
| class B: |
| tag: Literal['B'] |
| |
| abo: Union[A, B, None] |
| |
| if abo is not None and abo.tag == "A": |
| reveal_type(abo.tag) # N: Revealed type is "Literal['A']" |
| reveal_type(abo) # N: Revealed type is "__main__.A" |
| |
| if not (abo is None or abo.tag != "B"): |
| reveal_type(abo.tag) # N: Revealed type is "Literal['B']" |
| reveal_type(abo) # N: Revealed type is "__main__.B" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingEqualityFlipFlop] |
| # flags: --warn-unreachable --strict-equality |
| from typing import Final, Literal |
| from enum import Enum |
| |
| class State(Enum): |
| A = 1 |
| B = 2 |
| |
| class FlipFlopEnum: |
| def __init__(self) -> None: |
| self.state = State.A |
| |
| def mutate(self) -> None: |
| self.state = State.B if self.state == State.A else State.A |
| |
| class FlipFlopStr: |
| def __init__(self) -> None: |
| self.state = "state-1" |
| |
| def mutate(self) -> None: |
| self.state = "state-2" if self.state == "state-1" else "state-1" |
| |
| |
| def test1(switch: FlipFlopStr) -> None: |
| # Naively, we might assume the 'assert' here would narrow the type to |
| # Literal["state-1"]. However, doing this ends up breaking a fair number of real-world |
| # code (usually test cases) that looks similar to this function: e.g. checks |
| # to make sure a field was mutated to some particular value. |
| # |
| # And since mypy can't really reason about state mutation, we take a conservative |
| # approach and avoid narrowing anything here. |
| |
| assert switch.state == "state-1" |
| reveal_type(switch.state) # N: Revealed type is "builtins.str" |
| |
| switch.mutate() |
| |
| assert switch.state == "state-2" |
| reveal_type(switch.state) # N: Revealed type is "builtins.str" |
| |
| def test2(switch: FlipFlopEnum) -> None: |
| # This is the same thing as 'test1', except we use enums, which we allow to be narrowed |
| # to literals. |
| |
| assert switch.state == State.A |
| reveal_type(switch.state) # N: Revealed type is "Literal[__main__.State.A]" |
| |
| switch.mutate() |
| |
| assert switch.state == State.B # E: Non-overlapping equality check (left operand type: "Literal[State.A]", right operand type: "Literal[State.B]") |
| reveal_type(switch.state) # E: Statement is unreachable |
| |
| def test3(switch: FlipFlopEnum) -> None: |
| # Same thing, but using 'is' comparisons. Previously mypy's behaviour differed |
| # here, narrowing when using 'is', but not when using '=='. |
| |
| assert switch.state is State.A |
| reveal_type(switch.state) # N: Revealed type is "Literal[__main__.State.A]" |
| |
| switch.mutate() |
| |
| assert switch.state is State.B # E: Non-overlapping identity check (left operand type: "Literal[State.A]", right operand type: "Literal[State.B]") |
| reveal_type(switch.state) # E: Statement is unreachable |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingEqualityRequiresExplicitStrLiteral] |
| from typing import Final, Literal |
| |
| A_final: Final = "A" |
| A_literal: Literal["A"] |
| |
| # Neither the LHS nor the RHS are explicit literals, so regrettably nothing |
| # is narrowed here -- see 'testNarrowingEqualityFlipFlop' for an example of |
| # why more precise inference here is problematic. |
| x_str: str |
| if x_str == "A": |
| reveal_type(x_str) # N: Revealed type is "builtins.str" |
| else: |
| reveal_type(x_str) # N: Revealed type is "builtins.str" |
| reveal_type(x_str) # N: Revealed type is "builtins.str" |
| |
| if x_str == A_final: |
| reveal_type(x_str) # N: Revealed type is "builtins.str" |
| else: |
| reveal_type(x_str) # N: Revealed type is "builtins.str" |
| reveal_type(x_str) # N: Revealed type is "builtins.str" |
| |
| # But the RHS is a literal, so we can at least narrow the 'if' case now. |
| if x_str == A_literal: |
| reveal_type(x_str) # N: Revealed type is "Literal['A']" |
| else: |
| reveal_type(x_str) # N: Revealed type is "builtins.str" |
| reveal_type(x_str) # N: Revealed type is "builtins.str" |
| |
| # But in these two cases, the LHS is a literal/literal-like type. So we |
| # assume the user *does* want literal-based narrowing and narrow accordingly |
| # regardless of whether the RHS is an explicit literal or not. |
| x_union: Literal["A", "B", None] |
| if x_union == A_final: |
| reveal_type(x_union) # N: Revealed type is "Literal['A']" |
| else: |
| reveal_type(x_union) # N: Revealed type is "Literal['B'] | None" |
| reveal_type(x_union) # N: Revealed type is "Literal['A'] | Literal['B'] | None" |
| |
| if x_union == A_literal: |
| reveal_type(x_union) # N: Revealed type is "Literal['A']" |
| else: |
| reveal_type(x_union) # N: Revealed type is "Literal['B'] | None" |
| reveal_type(x_union) # N: Revealed type is "Literal['A'] | Literal['B'] | None" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingEqualityRequiresExplicitEnumLiteral] |
| from typing import Final, Literal, Union |
| from enum import Enum |
| |
| class Foo(Enum): |
| A = 1 |
| B = 2 |
| |
| A_final: Final = Foo.A |
| A_literal: Literal[Foo.A] |
| |
| # Note this is unlike testNarrowingEqualityRequiresExplicitStrLiteral |
| # See also testNarrowingEqualityFlipFlop |
| x1: Foo |
| if x1 == Foo.A: |
| reveal_type(x1) # N: Revealed type is "Literal[__main__.Foo.A]" |
| else: |
| reveal_type(x1) # N: Revealed type is "Literal[__main__.Foo.B]" |
| |
| x2: Foo |
| if x2 == A_final: |
| reveal_type(x2) # N: Revealed type is "Literal[__main__.Foo.A]" |
| else: |
| reveal_type(x2) # N: Revealed type is "Literal[__main__.Foo.B]" |
| |
| # But we let this narrow since there's an explicit literal in the RHS. |
| x3: Foo |
| if x3 == A_literal: |
| reveal_type(x3) # N: Revealed type is "Literal[__main__.Foo.A]" |
| else: |
| reveal_type(x3) # N: Revealed type is "Literal[__main__.Foo.B]" |
| |
| |
| class SingletonFoo(Enum): |
| A = "A" |
| |
| def bar(x: Union[SingletonFoo, Foo], y: SingletonFoo) -> None: |
| if x == y: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.SingletonFoo.A]" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingEqualityDisabledForCustomEquality] |
| from typing import Literal, Union |
| from enum import Enum |
| |
| class Custom: |
| def __eq__(self, other: object) -> bool: return True |
| |
| class Default: pass |
| |
| x1: Union[Custom, Literal[1], Literal[2]] |
| if x1 == 1: |
| reveal_type(x1) # N: Revealed type is "__main__.Custom | Literal[1] | Literal[2]" |
| else: |
| reveal_type(x1) # N: Revealed type is "__main__.Custom | Literal[1] | Literal[2]" |
| |
| x2: Union[Default, Literal[1], Literal[2]] |
| if x2 == 1: |
| reveal_type(x2) # N: Revealed type is "Literal[1]" |
| else: |
| reveal_type(x2) # N: Revealed type is "__main__.Default | Literal[2]" |
| |
| class CustomEnum(Enum): |
| A = 1 |
| B = 2 |
| |
| def __eq__(self, other: object) -> bool: return True |
| |
| x3: CustomEnum |
| key: Literal[CustomEnum.A] |
| if x3 == key: |
| reveal_type(x3) # N: Revealed type is "__main__.CustomEnum" |
| else: |
| reveal_type(x3) # N: Revealed type is "__main__.CustomEnum" |
| |
| # For comparison, this narrows since we bypass __eq__ |
| if x3 is key: |
| reveal_type(x3) # N: Revealed type is "Literal[__main__.CustomEnum.A]" |
| else: |
| reveal_type(x3) # N: Revealed type is "Literal[__main__.CustomEnum.B]" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingEqualityDisabledForCustomEqualityChain] |
| # flags: --strict-equality --warn-unreachable |
| from typing import Literal, Union |
| |
| class Custom: |
| def __eq__(self, other: object) -> bool: return True |
| |
| class Default: pass |
| |
| x: Literal[1, 2, None] |
| y: Custom |
| z: Default |
| |
| # We could maybe try doing something clever, but for simplicity we |
| # treat the whole chain as contaminated and mostly disable narrowing. |
| # |
| # The only exception is that we do at least strip away the 'None'. We |
| # (perhaps optimistically) assume no custom class would be pathological |
| # enough to declare itself to be equal to None and so permit this narrowing, |
| # since it's often convenient in practice. |
| if 1 == x == y: |
| reveal_type(x) # N: Revealed type is "Literal[1] | Literal[2]" |
| reveal_type(y) # N: Revealed type is "__main__.Custom" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[1] | Literal[2] | None" |
| reveal_type(y) # N: Revealed type is "__main__.Custom" |
| |
| # No contamination here |
| if 1 == x == z: # E: Non-overlapping equality check (left operand type: "Literal[1, 2] | None", right operand type: "Default") |
| reveal_type(x) # E: Statement is unreachable |
| reveal_type(z) |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[1] | Literal[2] | None" |
| reveal_type(z) # N: Revealed type is "__main__.Default" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingUnreachableCases] |
| # flags: --strict-equality --warn-unreachable |
| from typing import Literal, Union |
| |
| a: Literal[1] |
| b: Literal[1, 2] |
| c: Literal[2, 3] |
| |
| if a == b == c: |
| reveal_type(a) # E: Statement is unreachable |
| reveal_type(b) |
| reveal_type(c) |
| else: |
| reveal_type(a) # N: Revealed type is "Literal[1]" |
| reveal_type(b) # N: Revealed type is "Literal[1] | Literal[2]" |
| reveal_type(c) # N: Revealed type is "Literal[2] | Literal[3]" |
| |
| if a == a == a: |
| reveal_type(a) # N: Revealed type is "Literal[1]" |
| else: |
| reveal_type(a) # E: Statement is unreachable |
| |
| if a == a == b: |
| reveal_type(a) # N: Revealed type is "Literal[1]" |
| reveal_type(b) # N: Revealed type is "Literal[1]" |
| else: |
| reveal_type(a) # N: Revealed type is "Literal[1]" |
| reveal_type(b) # N: Revealed type is "Literal[2]" |
| |
| # In this case, it's ok for 'b' to narrow down to Literal[1] in the else case |
| # since that's the only way 'b == 2' can be false |
| if b == 2: |
| reveal_type(b) # N: Revealed type is "Literal[2]" |
| else: |
| reveal_type(b) # N: Revealed type is "Literal[1]" |
| |
| # But in this case, we can't conclude anything about the else case. This expression |
| # could end up being either '2 == 2 == 3' or '1 == 2 == 2', which means we can't |
| # conclude anything. |
| if b == 2 == c: |
| reveal_type(b) # N: Revealed type is "Literal[2]" |
| reveal_type(c) # N: Revealed type is "Literal[2]" |
| else: |
| reveal_type(b) # N: Revealed type is "Literal[1] | Literal[2]" |
| reveal_type(c) # N: Revealed type is "Literal[2] | Literal[3]" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingUnreachableCases2] |
| # flags: --strict-equality --warn-unreachable |
| from typing import Literal, Union |
| |
| a: Literal[1, 2, 3, 4] |
| b: Literal[1, 2, 3, 4] |
| |
| if a == b == 1: |
| reveal_type(a) # N: Revealed type is "Literal[1]" |
| reveal_type(b) # N: Revealed type is "Literal[1]" |
| elif a == b == 2: |
| reveal_type(a) # N: Revealed type is "Literal[2]" |
| reveal_type(b) # N: Revealed type is "Literal[2]" |
| elif a == b == 3: |
| reveal_type(a) # N: Revealed type is "Literal[3]" |
| reveal_type(b) # N: Revealed type is "Literal[3]" |
| elif a == b == 4: |
| reveal_type(a) # N: Revealed type is "Literal[4]" |
| reveal_type(b) # N: Revealed type is "Literal[4]" |
| else: |
| # This branch is reachable if a == 1 and b == 2, for example. |
| reveal_type(a) # N: Revealed type is "Literal[1] | Literal[2] | Literal[3] | Literal[4]" |
| reveal_type(b) # N: Revealed type is "Literal[1] | Literal[2] | Literal[3] | Literal[4]" |
| |
| if a == a == 1: |
| reveal_type(a) # N: Revealed type is "Literal[1]" |
| elif a == a == 2: |
| reveal_type(a) # N: Revealed type is "Literal[2]" |
| elif a == a == 3: |
| reveal_type(a) # N: Revealed type is "Literal[3]" |
| elif a == a == 4: |
| reveal_type(a) # N: Revealed type is "Literal[4]" |
| else: |
| # In contrast, this branch must be unreachable: we assume (maybe naively) |
| # that 'a' won't be mutated in the middle of the expression. |
| reveal_type(a) # E: Statement is unreachable |
| reveal_type(b) |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingLiteralTruthiness] |
| from typing import Literal, Union |
| |
| str_or_false: Union[Literal[False], str] |
| |
| if str_or_false: |
| reveal_type(str_or_false) # N: Revealed type is "builtins.str" |
| else: |
| reveal_type(str_or_false) # N: Revealed type is "Literal[False] | Literal['']" |
| |
| true_or_false: Literal[True, False] |
| |
| if true_or_false: |
| reveal_type(true_or_false) # N: Revealed type is "Literal[True]" |
| else: |
| reveal_type(true_or_false) # N: Revealed type is "Literal[False]" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingFalseyToLiteral] |
| from typing import Union |
| |
| a: str |
| b: bytes |
| c: int |
| d: Union[str, bytes, int] |
| |
| if not a: |
| reveal_type(a) # N: Revealed type is "Literal['']" |
| if not b: |
| reveal_type(b) # N: Revealed type is "Literal[b'']" |
| if not c: |
| reveal_type(c) # N: Revealed type is "Literal[0]" |
| if not d: |
| reveal_type(d) # N: Revealed type is "Literal[''] | Literal[b''] | Literal[0]" |
| |
| [case testNarrowingIsInstanceFinalSubclass] |
| # flags: --warn-unreachable |
| |
| from typing import final |
| |
| class N: ... |
| @final |
| class F1: ... |
| @final |
| class F2: ... |
| |
| n: N |
| f1: F1 |
| |
| if isinstance(f1, F1): |
| reveal_type(f1) # N: Revealed type is "__main__.F1" |
| else: |
| reveal_type(f1) # E: Statement is unreachable |
| |
| if isinstance(n, F1): # E: Subclass of "N" and "F1" cannot exist: "F1" is final |
| reveal_type(n) # E: Statement is unreachable |
| else: |
| reveal_type(n) # N: Revealed type is "__main__.N" |
| |
| if isinstance(f1, N): # E: Subclass of "F1" and "N" cannot exist: "F1" is final |
| reveal_type(f1) # E: Statement is unreachable |
| else: |
| reveal_type(f1) # N: Revealed type is "__main__.F1" |
| |
| if isinstance(f1, F2): # E: Subclass of "F1" and "F2" cannot exist: "F1" is final \ |
| # E: Subclass of "F1" and "F2" cannot exist: "F2" is final |
| reveal_type(f1) # E: Statement is unreachable |
| else: |
| reveal_type(f1) # N: Revealed type is "__main__.F1" |
| [builtins fixtures/isinstance.pyi] |
| |
| |
| [case testNarrowingIsInstanceFinalSubclassWithUnions] |
| # flags: --warn-unreachable |
| |
| from typing import final, Union |
| |
| class N: ... |
| @final |
| class F1: ... |
| @final |
| class F2: ... |
| |
| n_f1: Union[N, F1] |
| n_f2: Union[N, F2] |
| f1_f2: Union[F1, F2] |
| |
| if isinstance(n_f1, F1): |
| reveal_type(n_f1) # N: Revealed type is "__main__.F1" |
| else: |
| reveal_type(n_f1) # N: Revealed type is "__main__.N" |
| |
| if isinstance(n_f2, F1): # E: Subclass of "N" and "F1" cannot exist: "F1" is final \ |
| # E: Subclass of "F2" and "F1" cannot exist: "F2" is final \ |
| # E: Subclass of "F2" and "F1" cannot exist: "F1" is final |
| reveal_type(n_f2) # E: Statement is unreachable |
| else: |
| reveal_type(n_f2) # N: Revealed type is "__main__.N | __main__.F2" |
| |
| if isinstance(f1_f2, F1): |
| reveal_type(f1_f2) # N: Revealed type is "__main__.F1" |
| else: |
| reveal_type(f1_f2) # N: Revealed type is "__main__.F2" |
| [builtins fixtures/isinstance.pyi] |
| |
| |
| [case testNarrowingIsSubclassFinalSubclassWithTypeVar] |
| # flags: --warn-unreachable |
| |
| from typing import final, Type, TypeVar |
| |
| @final |
| class A: ... |
| @final |
| class B: ... |
| |
| T = TypeVar("T", A, B) |
| |
| def f(cls: Type[T]) -> T: |
| if issubclass(cls, A): |
| reveal_type(cls) # N: Revealed type is "type[__main__.A]" |
| x: bool |
| if x: |
| return A() |
| else: |
| return B() # E: Incompatible return value type (got "B", expected "A") |
| assert False |
| |
| reveal_type(f(A)) # N: Revealed type is "__main__.A" |
| reveal_type(f(B)) # N: Revealed type is "__main__.B" |
| [builtins fixtures/isinstance.pyi] |
| |
| |
| [case testNarrowingLiteralIdentityCheck] |
| from typing import Literal, Union |
| |
| str_or_false: Union[Literal[False], str] |
| |
| if str_or_false is not False: |
| reveal_type(str_or_false) # N: Revealed type is "builtins.str" |
| else: |
| reveal_type(str_or_false) # N: Revealed type is "Literal[False]" |
| |
| if str_or_false is False: |
| reveal_type(str_or_false) # N: Revealed type is "Literal[False]" |
| else: |
| reveal_type(str_or_false) # N: Revealed type is "builtins.str" |
| |
| str_or_true: Union[Literal[True], str] |
| |
| if str_or_true is True: |
| reveal_type(str_or_true) # N: Revealed type is "Literal[True]" |
| else: |
| reveal_type(str_or_true) # N: Revealed type is "builtins.str" |
| |
| if str_or_true is not True: |
| reveal_type(str_or_true) # N: Revealed type is "builtins.str" |
| else: |
| reveal_type(str_or_true) # N: Revealed type is "Literal[True]" |
| |
| str_or_bool_literal: Union[Literal[False], Literal[True], str] |
| |
| if str_or_bool_literal is not True: |
| reveal_type(str_or_bool_literal) # N: Revealed type is "Literal[False] | builtins.str" |
| else: |
| reveal_type(str_or_bool_literal) # N: Revealed type is "Literal[True]" |
| |
| if str_or_bool_literal is not True and str_or_bool_literal is not False: |
| reveal_type(str_or_bool_literal) # N: Revealed type is "builtins.str" |
| else: |
| reveal_type(str_or_bool_literal) # N: Revealed type is "builtins.bool" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingBooleanIdentityCheck] |
| from typing import Literal, Optional |
| |
| bool_val: bool |
| |
| if bool_val is not False: |
| reveal_type(bool_val) # N: Revealed type is "Literal[True]" |
| else: |
| reveal_type(bool_val) # N: Revealed type is "Literal[False]" |
| |
| opt_bool_val: Optional[bool] |
| |
| if opt_bool_val is not None: |
| reveal_type(opt_bool_val) # N: Revealed type is "builtins.bool" |
| |
| if opt_bool_val is not False: |
| reveal_type(opt_bool_val) # N: Revealed type is "Literal[True] | None" |
| else: |
| reveal_type(opt_bool_val) # N: Revealed type is "Literal[False]" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingBooleanTruthiness] |
| from typing import Literal, Optional |
| |
| bool_val: bool |
| |
| if bool_val: |
| reveal_type(bool_val) # N: Revealed type is "Literal[True]" |
| else: |
| reveal_type(bool_val) # N: Revealed type is "Literal[False]" |
| reveal_type(bool_val) # N: Revealed type is "builtins.bool" |
| |
| opt_bool_val: Optional[bool] |
| |
| if opt_bool_val: |
| reveal_type(opt_bool_val) # N: Revealed type is "Literal[True]" |
| else: |
| reveal_type(opt_bool_val) # N: Revealed type is "Literal[False] | None" |
| reveal_type(opt_bool_val) # N: Revealed type is "builtins.bool | None" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingBooleanBoolOp] |
| from typing import Literal, Optional |
| |
| bool_a: bool |
| bool_b: bool |
| |
| if bool_a and bool_b: |
| reveal_type(bool_a) # N: Revealed type is "Literal[True]" |
| reveal_type(bool_b) # N: Revealed type is "Literal[True]" |
| else: |
| reveal_type(bool_a) # N: Revealed type is "builtins.bool" |
| reveal_type(bool_b) # N: Revealed type is "builtins.bool" |
| |
| if not bool_a or bool_b: |
| reveal_type(bool_a) # N: Revealed type is "builtins.bool" |
| reveal_type(bool_b) # N: Revealed type is "builtins.bool" |
| else: |
| reveal_type(bool_a) # N: Revealed type is "Literal[True]" |
| reveal_type(bool_b) # N: Revealed type is "Literal[False]" |
| |
| if True and bool_b: |
| reveal_type(bool_b) # N: Revealed type is "Literal[True]" |
| |
| x = True and bool_b |
| reveal_type(x) # N: Revealed type is "builtins.bool" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingTypedDictUsingEnumLiteral] |
| from typing import Literal, TypedDict, Union |
| from enum import Enum |
| |
| class E(Enum): |
| FOO = "a" |
| BAR = "b" |
| |
| class Foo(TypedDict): |
| tag: Literal[E.FOO] |
| x: int |
| |
| class Bar(TypedDict): |
| tag: Literal[E.BAR] |
| y: int |
| |
| def f(d: Union[Foo, Bar]) -> None: |
| assert d['tag'] == E.FOO |
| d['x'] |
| reveal_type(d) # N: Revealed type is "TypedDict('__main__.Foo', {'tag': Literal[__main__.E.FOO], 'x': builtins.int})" |
| [builtins fixtures/dict.pyi] |
| [typing fixtures/typing-typeddict.pyi] |
| |
| [case testNarrowingUsingMetaclass] |
| from typing import Type |
| |
| class M(type): |
| pass |
| |
| class C: pass |
| |
| def f(t: Type[C]) -> None: |
| if type(t) is M: |
| reveal_type(t) # N: Revealed type is "type[__main__.C]" |
| else: |
| reveal_type(t) # N: Revealed type is "type[__main__.C]" |
| if type(t) is not M: |
| reveal_type(t) # N: Revealed type is "type[__main__.C]" |
| else: |
| reveal_type(t) # N: Revealed type is "type[__main__.C]" |
| reveal_type(t) # N: Revealed type is "type[__main__.C]" |
| |
| [case testNarrowingUsingTypeVar] |
| from typing import Type, TypeVar |
| |
| class A: pass |
| class B(A): pass |
| |
| T = TypeVar("T", bound=A) |
| |
| def f(t: Type[T], a: A, b: B) -> None: |
| if type(a) is t: |
| reveal_type(a) # N: Revealed type is "T`-1" |
| else: |
| reveal_type(a) # N: Revealed type is "__main__.A" |
| |
| if type(b) is t: |
| reveal_type(b) # N: Revealed type is "T`-1" |
| else: |
| reveal_type(b) # N: Revealed type is "__main__.B" |
| |
| [case testNarrowingNestedUnionOfTypedDicts] |
| from typing import Literal, TypedDict, Union |
| |
| class A(TypedDict): |
| tag: Literal["A"] |
| a: int |
| |
| class B(TypedDict): |
| tag: Literal["B"] |
| b: int |
| |
| class C(TypedDict): |
| tag: Literal["C"] |
| c: int |
| |
| AB = Union[A, B] |
| ABC = Union[AB, C] |
| abc: ABC |
| |
| if abc["tag"] == "A": |
| reveal_type(abc) # N: Revealed type is "TypedDict('__main__.A', {'tag': Literal['A'], 'a': builtins.int})" |
| elif abc["tag"] == "C": |
| reveal_type(abc) # N: Revealed type is "TypedDict('__main__.C', {'tag': Literal['C'], 'c': builtins.int})" |
| else: |
| reveal_type(abc) # N: Revealed type is "TypedDict('__main__.B', {'tag': Literal['B'], 'b': builtins.int})" |
| [builtins fixtures/primitives.pyi] |
| [typing fixtures/typing-typeddict.pyi] |
| |
| [case testNarrowingRuntimeCover] |
| from typing import Dict, List, Union |
| |
| def unreachable(x: Union[str, List[str]]) -> None: |
| if isinstance(x, str): |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| elif isinstance(x, list): |
| reveal_type(x) # N: Revealed type is "builtins.list[builtins.str]" |
| else: |
| reveal_type(x) # No output: this branch is unreachable |
| |
| def all_parts_covered(x: Union[str, List[str], List[int], int]) -> None: |
| if isinstance(x, str): |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| elif isinstance(x, list): |
| reveal_type(x) # N: Revealed type is "builtins.list[builtins.str] | builtins.list[builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| |
| def two_type_vars(x: Union[str, Dict[str, int], Dict[bool, object], int]) -> None: |
| if isinstance(x, str): |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| elif isinstance(x, dict): |
| reveal_type(x) # N: Revealed type is "builtins.dict[builtins.str, builtins.int] | builtins.dict[builtins.bool, builtins.object]" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| [builtins fixtures/dict.pyi] |
| |
| |
| [case testNarrowingWithDef] |
| from typing import Callable, Optional |
| |
| def g() -> None: |
| foo: Optional[Callable[[], None]] = None |
| if foo is None: |
| def foo(): ... |
| foo() |
| [builtins fixtures/dict.pyi] |
| |
| |
| [case testNarrowingOptionalEqualsNone] |
| from typing import Optional |
| |
| class A: ... |
| |
| val: Optional[A] |
| |
| if val == None: |
| reveal_type(val) # N: Revealed type is "__main__.A | None" |
| else: |
| reveal_type(val) # N: Revealed type is "__main__.A" |
| if val != None: |
| reveal_type(val) # N: Revealed type is "__main__.A" |
| else: |
| reveal_type(val) # N: Revealed type is "__main__.A | None" |
| |
| if val in (None,): |
| reveal_type(val) # N: Revealed type is "__main__.A | None" |
| else: |
| reveal_type(val) # N: Revealed type is "__main__.A | None" |
| if val not in (None,): |
| reveal_type(val) # N: Revealed type is "__main__.A | None" |
| else: |
| reveal_type(val) # N: Revealed type is "__main__.A | None" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingWithTupleOfTypes] |
| from typing import Tuple, Type |
| |
| class Base: ... |
| |
| class Impl1(Base): ... |
| class Impl2(Base): ... |
| |
| impls: Tuple[Type[Base], ...] = (Impl1, Impl2) |
| some: object |
| |
| if isinstance(some, impls): |
| reveal_type(some) # N: Revealed type is "__main__.Base" |
| else: |
| reveal_type(some) # N: Revealed type is "builtins.object" |
| |
| raw: Tuple[type, ...] |
| if isinstance(some, raw): |
| reveal_type(some) # N: Revealed type is "builtins.object" |
| else: |
| reveal_type(some) # N: Revealed type is "builtins.object" |
| [builtins fixtures/dict.pyi] |
| |
| |
| [case testNarrowingWithTupleOfTypesPy310Plus] |
| # flags: --python-version 3.10 |
| class Base: ... |
| |
| class Impl1(Base): ... |
| class Impl2(Base): ... |
| |
| some: int | Base |
| |
| impls: tuple[type[Base], ...] = (Impl1, Impl2) |
| if isinstance(some, impls): |
| reveal_type(some) # N: Revealed type is "__main__.Base" |
| else: |
| reveal_type(some) # N: Revealed type is "builtins.int | __main__.Base" |
| |
| raw: tuple[type, ...] |
| if isinstance(some, raw): |
| reveal_type(some) # N: Revealed type is "builtins.int | __main__.Base" |
| else: |
| reveal_type(some) # N: Revealed type is "builtins.int | __main__.Base" |
| [builtins fixtures/dict.pyi] |
| |
| [case testNarrowingWithAnyOps] |
| from typing import Any |
| |
| class C: ... |
| class D(C): ... |
| tp: Any |
| |
| c: C |
| if isinstance(c, tp) or isinstance(c, D): |
| reveal_type(c) # N: Revealed type is "Any | __main__.D" |
| else: |
| reveal_type(c) # N: Revealed type is "__main__.C" |
| reveal_type(c) # N: Revealed type is "__main__.C" |
| |
| c1: C |
| if isinstance(c1, tp) and isinstance(c1, D): |
| reveal_type(c1) # N: Revealed type is "Any" |
| else: |
| reveal_type(c1) # N: Revealed type is "__main__.C" |
| reveal_type(c1) # N: Revealed type is "__main__.C" |
| |
| c2: C |
| if isinstance(c2, D) or isinstance(c2, tp): |
| reveal_type(c2) # N: Revealed type is "__main__.D | Any" |
| else: |
| reveal_type(c2) # N: Revealed type is "__main__.C" |
| reveal_type(c2) # N: Revealed type is "__main__.C" |
| |
| c3: C |
| if isinstance(c3, D) and isinstance(c3, tp): |
| reveal_type(c3) # N: Revealed type is "Any" |
| else: |
| reveal_type(c3) # N: Revealed type is "__main__.C" |
| reveal_type(c3) # N: Revealed type is "__main__.C" |
| |
| t: Any |
| if isinstance(t, (list, tuple)) and isinstance(t, tuple): |
| reveal_type(t) # N: Revealed type is "builtins.tuple[Any, ...]" |
| else: |
| reveal_type(t) # N: Revealed type is "Any" |
| reveal_type(t) # N: Revealed type is "Any" |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testNarrowingLenItemAndLenCompare] |
| from typing import Any |
| |
| x: Any |
| if len(x) == x: |
| reveal_type(x) # N: Revealed type is "Any" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenTuple] |
| from typing import Tuple, Union |
| |
| VarTuple = Union[Tuple[int, int], Tuple[int, int, int]] |
| |
| x: VarTuple |
| a = b = c = 0 |
| if len(x) == 3: |
| a, b, c = x |
| else: |
| a, b = x |
| |
| if len(x) != 3: |
| a, b = x |
| else: |
| a, b, c = x |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenHomogeneousTuple] |
| from typing import Tuple |
| |
| x: Tuple[int, ...] |
| if len(x) == 3: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" |
| |
| if len(x) != 3: |
| reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenTypeUnaffected] |
| from typing import Union, List |
| |
| x: Union[str, List[int]] |
| if len(x) == 3: |
| reveal_type(x) # N: Revealed type is "builtins.str | builtins.list[builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.str | builtins.list[builtins.int]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenAnyListElseNotAffected] |
| from typing import Any |
| |
| def f(self, value: Any) -> Any: |
| if isinstance(value, list) and len(value) == 0: |
| reveal_type(value) # N: Revealed type is "builtins.list[Any]" |
| return value |
| reveal_type(value) # N: Revealed type is "Any" |
| return None |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenMultiple] |
| from typing import Tuple, Union |
| |
| VarTuple = Union[Tuple[int, int], Tuple[int, int, int]] |
| |
| x: VarTuple |
| y: VarTuple |
| if len(x) == len(y) == 3: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]" |
| reveal_type(y) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenFinal] |
| from typing import Final, Tuple, Union |
| |
| VarTuple = Union[Tuple[int, int], Tuple[int, int, int]] |
| |
| x: VarTuple |
| fin: Final = 3 |
| if len(x) == fin: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenGreaterThan] |
| from typing import Tuple, Union |
| |
| VarTuple = Union[Tuple[int], Tuple[int, int], Tuple[int, int, int]] |
| |
| x: VarTuple |
| if len(x) > 1: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int]" |
| |
| if len(x) < 2: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| |
| if len(x) >= 2: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int]" |
| |
| if len(x) <= 2: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int] | tuple[builtins.int, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenBothSidesUnionTuples] |
| from typing import Tuple, Union |
| |
| VarTuple = Union[ |
| Tuple[int], |
| Tuple[int, int], |
| Tuple[int, int, int], |
| Tuple[int, int, int, int], |
| ] |
| |
| x: VarTuple |
| if 2 <= len(x) <= 3: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int] | tuple[builtins.int, builtins.int, builtins.int, builtins.int]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenGreaterThanHomogeneousTupleShort] |
| # flags: --enable-incomplete-feature=PreciseTupleTypes |
| from typing import Tuple |
| |
| VarTuple = Tuple[int, ...] |
| |
| x: VarTuple |
| if len(x) < 3: |
| reveal_type(x) # N: Revealed type is "tuple[()] | tuple[builtins.int] | tuple[builtins.int, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" |
| reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenBiggerThanHomogeneousTupleLong] |
| # flags: --enable-incomplete-feature=PreciseTupleTypes |
| from typing import Tuple |
| |
| VarTuple = Tuple[int, ...] |
| |
| x: VarTuple |
| if len(x) < 30: |
| reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenBothSidesHomogeneousTuple] |
| # flags: --enable-incomplete-feature=PreciseTupleTypes |
| from typing import Tuple |
| |
| x: Tuple[int, ...] |
| if 1 < len(x) < 4: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[()] | tuple[builtins.int] | tuple[builtins.int, builtins.int, builtins.int, builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" |
| reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenUnionTupleUnreachable] |
| # flags: --warn-unreachable |
| from typing import Tuple, Union |
| |
| x: Union[Tuple[int, int], Tuple[int, int, int]] |
| if len(x) >= 4: |
| reveal_type(x) # E: Statement is unreachable |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| |
| if len(x) < 2: |
| reveal_type(x) # E: Statement is unreachable |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenMixedTypes] |
| from typing import Tuple, List, Union |
| |
| x: Union[Tuple[int, int], Tuple[int, int, int], List[int]] |
| a = b = c = 0 |
| if len(x) == 3: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int] | builtins.list[builtins.int]" |
| a, b, c = x |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | builtins.list[builtins.int]" |
| a, b = x |
| |
| if len(x) != 3: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | builtins.list[builtins.int]" |
| a, b = x |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int] | builtins.list[builtins.int]" |
| a, b, c = x |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenTypeVarTupleEquals] |
| from typing import Tuple |
| from typing_extensions import TypeVarTuple, Unpack |
| |
| Ts = TypeVarTuple("Ts") |
| def foo(x: Tuple[int, Unpack[Ts], str]) -> None: |
| if len(x) == 5: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" |
| |
| if len(x) != 5: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenTypeVarTupleGreaterThan] |
| from typing import Tuple |
| from typing_extensions import TypeVarTuple, Unpack |
| |
| Ts = TypeVarTuple("Ts") |
| def foo(x: Tuple[int, Unpack[Ts], str]) -> None: |
| if len(x) > 5: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" |
| reveal_type(x[5]) # N: Revealed type is "builtins.object" |
| reveal_type(x[-6]) # N: Revealed type is "builtins.object" |
| reveal_type(x[-1]) # N: Revealed type is "builtins.str" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" |
| |
| if len(x) < 5: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" |
| x[5] # E: Tuple index out of range \ |
| # N: Variadic tuple can have length 5 |
| x[-6] # E: Tuple index out of range \ |
| # N: Variadic tuple can have length 5 |
| x[2] # E: Tuple index out of range \ |
| # N: Variadic tuple can have length 2 |
| x[-3] # E: Tuple index out of range \ |
| # N: Variadic tuple can have length 2 |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenTypeVarTupleUnreachable] |
| # flags: --warn-unreachable |
| from typing import Tuple |
| from typing_extensions import TypeVarTuple, Unpack |
| |
| Ts = TypeVarTuple("Ts") |
| def foo(x: Tuple[int, Unpack[Ts], str]) -> None: |
| if len(x) == 1: |
| reveal_type(x) # E: Statement is unreachable |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" |
| |
| if len(x) != 1: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" |
| else: |
| reveal_type(x) # E: Statement is unreachable |
| |
| def bar(x: Tuple[int, Unpack[Ts], str]) -> None: |
| if len(x) >= 2: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" |
| else: |
| reveal_type(x) # E: Statement is unreachable |
| |
| if len(x) < 2: |
| reveal_type(x) # E: Statement is unreachable |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[Ts`-1], builtins.str]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenVariadicTupleEquals] |
| from typing import Tuple |
| from typing_extensions import Unpack |
| |
| def foo(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: |
| if len(x) == 4: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.float, builtins.float, builtins.str]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" |
| |
| if len(x) != 4: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.float, builtins.float, builtins.str]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenVariadicTupleGreaterThan] |
| from typing import Tuple |
| from typing_extensions import Unpack |
| |
| def foo(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: |
| if len(x) > 3: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.float, builtins.float, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.str] | tuple[builtins.int, builtins.float, builtins.str]" |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" |
| |
| if len(x) < 3: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.str]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.float, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenVariadicTupleUnreachable] |
| # flags: --warn-unreachable |
| from typing import Tuple |
| from typing_extensions import Unpack |
| |
| def foo(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: |
| if len(x) == 1: |
| reveal_type(x) # E: Statement is unreachable |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" |
| |
| if len(x) != 1: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" |
| else: |
| reveal_type(x) # E: Statement is unreachable |
| |
| def bar(x: Tuple[int, Unpack[Tuple[float, ...]], str]) -> None: |
| if len(x) >= 2: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" |
| else: |
| reveal_type(x) # E: Statement is unreachable |
| |
| if len(x) < 2: |
| reveal_type(x) # E: Statement is unreachable |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.float, ...]], builtins.str]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenBareExpressionPrecise] |
| # flags: --enable-incomplete-feature=PreciseTupleTypes |
| from typing import Tuple |
| |
| x: Tuple[int, ...] |
| assert x |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenBareExpressionTypeVarTuple] |
| from typing import Tuple |
| from typing_extensions import TypeVarTuple, Unpack |
| |
| Ts = TypeVarTuple("Ts") |
| def test(*xs: Unpack[Ts]) -> None: |
| assert xs |
| xs[0] # OK |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenBareExpressionWithNonePrecise] |
| # flags: --enable-incomplete-feature=PreciseTupleTypes |
| from typing import Tuple, Optional |
| |
| x: Optional[Tuple[int, ...]] |
| if x: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, Unpack[builtins.tuple[builtins.int, ...]]]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[()] | None" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenBareExpressionWithNoneImprecise] |
| from typing import Tuple, Optional |
| |
| x: Optional[Tuple[int, ...]] |
| if x: |
| reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...]" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...] | None" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenMixWithAnyPrecise] |
| # flags: --enable-incomplete-feature=PreciseTupleTypes |
| from typing import Any |
| |
| x: Any |
| if isinstance(x, (list, tuple)) and len(x) == 0: |
| reveal_type(x) # N: Revealed type is "tuple[()] | builtins.list[Any]" |
| else: |
| reveal_type(x) # N: Revealed type is "Any" |
| reveal_type(x) # N: Revealed type is "Any" |
| |
| x1: Any |
| if isinstance(x1, (list, tuple)) and len(x1) > 1: |
| reveal_type(x1) # N: Revealed type is "tuple[Any, Any, Unpack[builtins.tuple[Any, ...]]] | builtins.list[Any]" |
| else: |
| reveal_type(x1) # N: Revealed type is "Any" |
| reveal_type(x1) # N: Revealed type is "Any" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenMixWithAnyImprecise] |
| from typing import Any |
| |
| x: Any |
| if isinstance(x, (list, tuple)) and len(x) == 0: |
| reveal_type(x) # N: Revealed type is "tuple[()] | builtins.list[Any]" |
| else: |
| reveal_type(x) # N: Revealed type is "Any" |
| reveal_type(x) # N: Revealed type is "Any" |
| |
| x1: Any |
| if isinstance(x1, (list, tuple)) and len(x1) > 1: |
| reveal_type(x1) # N: Revealed type is "builtins.tuple[Any, ...] | builtins.list[Any]" |
| else: |
| reveal_type(x1) # N: Revealed type is "Any" |
| reveal_type(x1) # N: Revealed type is "Any" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenExplicitLiteralTypes] |
| from typing import Literal, Tuple, Union |
| |
| VarTuple = Union[ |
| Tuple[int], |
| Tuple[int, int], |
| Tuple[int, int, int], |
| ] |
| x: VarTuple |
| |
| supported: Literal[2] |
| if len(x) == supported: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| |
| not_supported_yet: Literal[2, 3] |
| if len(x) == not_supported_yet: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int] | tuple[builtins.int, builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int] | tuple[builtins.int, builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenUnionOfVariadicTuples] |
| from typing import Tuple, Union |
| |
| x: Union[Tuple[int, ...], Tuple[str, ...]] |
| if len(x) == 2: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | tuple[builtins.str, builtins.str]" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.tuple[builtins.int, ...] | builtins.tuple[builtins.str, ...]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenUnionOfNamedTuples] |
| from typing import NamedTuple, Union |
| |
| class Point2D(NamedTuple): |
| x: int |
| y: int |
| class Point3D(NamedTuple): |
| x: int |
| y: int |
| z: int |
| |
| x: Union[Point2D, Point3D] |
| if len(x) == 2: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.Point2D]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, builtins.int, fallback=__main__.Point3D]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenTupleSubclass] |
| from typing import Tuple |
| |
| class Ints(Tuple[int, ...]): |
| size: int |
| |
| x: Ints |
| if len(x) == 2: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int, fallback=__main__.Ints]" |
| reveal_type(x.size) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.Ints" |
| reveal_type(x.size) # N: Revealed type is "builtins.int" |
| |
| reveal_type(x) # N: Revealed type is "__main__.Ints" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenTupleSubclassCustomNotAllowed] |
| from typing import Tuple |
| |
| class Ints(Tuple[int, ...]): |
| def __len__(self) -> int: |
| return 0 |
| |
| x: Ints |
| if len(x) > 2: |
| reveal_type(x) # N: Revealed type is "__main__.Ints" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.Ints" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenTupleSubclassPreciseNotAllowed] |
| # flags: --enable-incomplete-feature=PreciseTupleTypes |
| from typing import Tuple |
| |
| class Ints(Tuple[int, ...]): |
| size: int |
| |
| x: Ints |
| if len(x) > 2: |
| reveal_type(x) # N: Revealed type is "__main__.Ints" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.Ints" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenUnknownLen] |
| from typing import Any, Tuple, Union |
| |
| x: Union[Tuple[int, int], Tuple[int, int, int]] |
| |
| n: int |
| if len(x) == n: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| |
| a: Any |
| if len(x) == a: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int] | tuple[builtins.int, builtins.int, builtins.int]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingLenUnionWithUnreachable] |
| from typing import Union, Sequence |
| |
| def f(x: Union[int, Sequence[int]]) -> None: |
| if ( |
| isinstance(x, tuple) |
| and len(x) == 2 |
| and isinstance(x[0], int) |
| and isinstance(x[1], int) |
| ): |
| reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int]" |
| [builtins fixtures/len.pyi] |
| |
| [case testNarrowingIsSubclassNoneType1] |
| from typing import Type, Union |
| |
| def f(cls: Type[Union[None, int]]) -> None: |
| if issubclass(cls, int): |
| reveal_type(cls) # N: Revealed type is "type[builtins.int]" |
| else: |
| reveal_type(cls) # N: Revealed type is "type[None]" |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testNarrowingIsSubclassNoneType2] |
| from typing import Type, Union |
| |
| def f(cls: Type[Union[None, int]]) -> None: |
| if issubclass(cls, type(None)): |
| reveal_type(cls) # N: Revealed type is "type[None]" |
| else: |
| reveal_type(cls) # N: Revealed type is "type[builtins.int]" |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testNarrowingIsSubclassNoneType3] |
| from typing import Type, Union |
| |
| NoneType_ = type(None) |
| |
| def f(cls: Type[Union[None, int]]) -> None: |
| if issubclass(cls, NoneType_): |
| reveal_type(cls) # N: Revealed type is "type[None]" |
| else: |
| reveal_type(cls) # N: Revealed type is "type[builtins.int]" |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testNarrowingIsSubclassNoneType4] |
| # flags: --python-version 3.10 |
| |
| from types import NoneType |
| from typing import Type, Union |
| |
| def f(cls: Type[Union[None, int]]) -> None: |
| if issubclass(cls, NoneType): |
| reveal_type(cls) # N: Revealed type is "type[None]" |
| else: |
| reveal_type(cls) # N: Revealed type is "type[builtins.int]" |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testNarrowingIsInstanceNoIntersectionWithFinalTypeAndNoneType] |
| # flags: --warn-unreachable --python-version 3.10 |
| |
| from types import NoneType |
| from typing import final |
| |
| class X: ... |
| class Y: ... |
| @final |
| class Z: ... |
| |
| x: X |
| |
| if isinstance(x, (Y, Z)): |
| reveal_type(x) # N: Revealed type is "__main__.<subclass of "__main__.X" and "__main__.Y">" |
| if isinstance(x, (Y, NoneType)): |
| reveal_type(x) # N: Revealed type is "__main__.<subclass of "__main__.X" and "__main__.Y">" |
| if isinstance(x, (Y, Z, NoneType)): |
| reveal_type(x) # N: Revealed type is "__main__.<subclass of "__main__.X" and "__main__.Y">" |
| if isinstance(x, (Z, NoneType)): # E: Subclass of "X" and "Z" cannot exist: "Z" is final \ |
| # E: Subclass of "X" and "NoneType" cannot exist: "NoneType" is final |
| reveal_type(x) # E: Statement is unreachable |
| |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testTypeNarrowingReachableNegative] |
| # flags: --warn-unreachable |
| from typing import Literal |
| |
| x: Literal[-1] |
| |
| if x == -1: |
| assert True |
| |
| [typing fixtures/typing-medium.pyi] |
| [builtins fixtures/ops.pyi] |
| |
| [case testTypeNarrowingReachableNegativeUnion] |
| from typing import Literal |
| |
| x: Literal[-1, 1] |
| |
| if x == -1: |
| reveal_type(x) # N: Revealed type is "Literal[-1]" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[1]" |
| |
| [typing fixtures/typing-medium.pyi] |
| [builtins fixtures/ops.pyi] |
| |
| [case testNarrowingWithIntEnum] |
| # mypy: strict-equality |
| from __future__ import annotations |
| from typing import Any |
| from enum import IntEnum |
| |
| class IE(IntEnum): |
| X = 1 |
| Y = 2 |
| |
| def f1(x: int) -> None: |
| if x == IE.X: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| if x != IE.X: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| |
| def f2(x: IE) -> None: |
| if x == 1: |
| reveal_type(x) # N: Revealed type is "__main__.IE" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.IE" |
| |
| def f3(x: object) -> None: |
| if x == IE.X: |
| reveal_type(x) # N: Revealed type is "builtins.object" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.object" |
| |
| def f4(x: int | Any) -> None: |
| if x == IE.X: |
| reveal_type(x) # N: Revealed type is "builtins.int | Any" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int | Any" |
| |
| def f5(x: int) -> None: |
| if x is IE.X: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| if x is not IE.X: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" |
| |
| def f6(x: IE) -> None: |
| if x == IE.X: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.Y]" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingWithIntEnum2] |
| # mypy: strict-equality |
| from __future__ import annotations |
| from typing import Any |
| from enum import IntEnum, Enum |
| |
| class MyDecimal: ... |
| |
| class IE(IntEnum): |
| X = 1 |
| Y = 2 |
| |
| class IE2(IntEnum): |
| X = 1 |
| Y = 2 |
| |
| class E(Enum): |
| X = 1 |
| Y = 2 |
| |
| def f1(x: IE | MyDecimal) -> None: |
| if x == IE.X: |
| reveal_type(x) # N: Revealed type is "__main__.IE | __main__.MyDecimal" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.IE | __main__.MyDecimal" |
| |
| def f2(x: E | bytes) -> None: |
| if x == E.X: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.E.X]" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.E.Y] | builtins.bytes" |
| |
| def f3(x: IE | IE2) -> None: |
| if x == IE.X: |
| reveal_type(x) # N: Revealed type is "__main__.IE | __main__.IE2" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.IE | __main__.IE2" |
| |
| def f4(x: IE | E) -> None: |
| if x == IE.X: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" |
| elif x == E.X: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.E.X]" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.Y] | Literal[__main__.E.Y]" |
| |
| def f5(x: E | str | int) -> None: |
| if x == E.X: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.E.X]" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.E.Y] | builtins.str | builtins.int" |
| |
| def f6(x: IE | Any) -> None: |
| if x == IE.X: |
| reveal_type(x) # N: Revealed type is "__main__.IE | Any" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.IE | Any" |
| |
| def f7(x: IE | None) -> None: |
| if x == IE.X: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.Y] | None" |
| |
| def f8(x: IE | None) -> None: |
| if x is None: |
| reveal_type(x) # N: Revealed type is "None" |
| elif x == IE.X: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.Y]" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingWithStrEnum] |
| # mypy: strict-equality |
| from enum import StrEnum |
| |
| class SE(StrEnum): |
| A = 'a' |
| B = 'b' |
| |
| def f1(x: str) -> None: |
| if x == SE.A: |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| |
| def f2(x: SE) -> None: |
| if x == 'a': |
| reveal_type(x) # N: Revealed type is "__main__.SE" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.SE" |
| |
| def f3(x: object) -> None: |
| if x == SE.A: |
| reveal_type(x) # N: Revealed type is "builtins.object" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.object" |
| |
| def f4(x: SE) -> None: |
| if x == SE.A: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.SE.A]" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.SE.B]" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testConsistentNarrowingEqAndIn] |
| # flags: --python-version 3.10 |
| |
| # https://github.com/python/mypy/issues/17864 |
| def f(x: str | int) -> None: |
| if x == "x": |
| reveal_type(x) # N: Revealed type is "builtins.str | builtins.int" |
| y = x |
| |
| if x in ["x"]: |
| # TODO: we should fix this reveal https://github.com/python/mypy/issues/3229 |
| reveal_type(x) # N: Revealed type is "builtins.str | builtins.int" |
| y = x |
| z = x |
| z = y |
| [builtins fixtures/primitives.pyi] |
| |
| [case testConsistentNarrowingInWithCustomEq] |
| # flags: --python-version 3.10 |
| |
| # https://github.com/python/mypy/issues/17864 |
| class C: |
| def __init__(self, x: int) -> None: |
| self.x = x |
| |
| def __eq__(self, other: object) -> bool: |
| raise |
| # Example implementation: |
| # if isinstance(other, C) and other.x == self.x: |
| # return True |
| # return NotImplemented |
| |
| class D(C): |
| pass |
| |
| def f(x: C) -> None: |
| if x in [D(5)]: |
| reveal_type(x) # D # N: Revealed type is "__main__.C" |
| |
| f(C(5)) |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingTypeVarNone] |
| # flags: --warn-unreachable |
| |
| # https://github.com/python/mypy/issues/18126 |
| from typing import TypeVar |
| |
| T = TypeVar("T") |
| |
| def fn_if(arg: T) -> None: |
| if arg is None: |
| return None |
| return None |
| |
| def fn_while(arg: T) -> None: |
| while arg is None: |
| return None |
| return None |
| [builtins fixtures/primitives.pyi] |
| |
| [case testRefinePartialTypeWithinLoop] |
| # flags: --no-local-partial-types |
| |
| x = None |
| for _ in range(2): |
| if x is not None: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| x = 1 |
| reveal_type(x) # N: Revealed type is "builtins.int | None" |
| |
| def f() -> bool: ... |
| |
| y = None |
| while f(): |
| reveal_type(y) # N: Revealed type is "None | builtins.int" |
| y = 1 |
| reveal_type(y) # N: Revealed type is "builtins.int | None" |
| |
| z = [] # E: Need type annotation for "z" (hint: "z: list[<type>] = ...") |
| def g() -> None: |
| for i in range(2): |
| while f(): |
| if z: |
| z[0] + "v" # E: Unsupported operand types for + ("int" and "str") |
| z.append(1) |
| |
| class A: |
| def g(self) -> None: |
| z = [] # E: Need type annotation for "z" (hint: "z: list[<type>] = ...") |
| for i in range(2): |
| while f(): |
| if z: |
| z[0] + "v" # E: Unsupported operand types for + ("int" and "str") |
| z.append(1) |
| [builtins fixtures/primitives.pyi] |
| |
| [case testPersistentUnreachableLinesNestedInInpersistentUnreachableLines] |
| # flags: --warn-unreachable --python-version 3.11 |
| |
| x = None |
| y = None |
| while True: |
| if x is not None: |
| if y is not None: |
| reveal_type(y) # E: Statement is unreachable |
| x = 1 |
| [builtins fixtures/bool.pyi] |
| |
| [case testAvoidFalseRedundantCastInLoops] |
| # flags: --warn-redundant-casts |
| |
| from typing import Callable, cast, Union |
| |
| ProcessorReturnValue = Union[str, int] |
| Processor = Callable[[str], ProcessorReturnValue] |
| |
| def main_cast(p: Processor) -> None: |
| ed: ProcessorReturnValue |
| ed = cast(str, ...) |
| while True: |
| ed = p(cast(str, ed)) |
| |
| def main_no_cast(p: Processor) -> None: |
| ed: ProcessorReturnValue |
| ed = cast(str, ...) |
| while True: |
| ed = p(ed) # E: Argument 1 has incompatible type "str | int"; expected "str" |
| [builtins fixtures/bool.pyi] |
| |
| [case testAvoidFalseUnreachableInLoop1] |
| # flags: --warn-unreachable --python-version 3.11 |
| |
| def f() -> int | None: ... |
| def b() -> bool: ... |
| |
| x: int | None |
| x = 1 |
| while x is not None or b(): |
| x = f() |
| [builtins fixtures/bool.pyi] |
| |
| [case testAvoidFalseUnreachableInLoop2] |
| # flags: --warn-unreachable --python-version 3.11 |
| |
| y = None |
| while y is None: |
| if y is None: |
| y = [] |
| y.append(1) |
| [builtins fixtures/list.pyi] |
| |
| [case testAvoidFalseUnreachableInLoop3] |
| # flags: --warn-unreachable --python-version 3.11 |
| |
| xs: list[int | None] |
| y = None |
| for x in xs: |
| if x is not None: |
| if y is None: |
| y = {} # E: Need type annotation for "y" (hint: "y: dict[<type>, <type>] = ...") |
| [builtins fixtures/list.pyi] |
| |
| [case testAvoidFalseRedundantExprInLoop] |
| # flags: --enable-error-code redundant-expr --python-version 3.11 |
| |
| def f() -> int | None: ... |
| def b() -> bool: ... |
| |
| x: int | None |
| x = 1 |
| while x is not None and b(): |
| x = f() |
| [builtins fixtures/primitives.pyi] |
| |
| [case testAvoidFalseNonOverlappingEqualityCheckInLoop1] |
| # flags: --allow-redefinition-new --local-partial-types --strict-equality |
| |
| x = 1 |
| while True: |
| if x == str(): |
| break |
| x = str() |
| if x == int(): # E: Non-overlapping equality check (left operand type: "str", right operand type: "int") |
| break |
| [builtins fixtures/primitives.pyi] |
| |
| [case testAvoidFalseNonOverlappingEqualityCheckInLoop2] |
| # flags: --allow-redefinition-new --local-partial-types --strict-equality |
| |
| class A: ... |
| class B: ... |
| class C: ... |
| |
| x = A() |
| while True: |
| if x == C(): # E: Non-overlapping equality check (left operand type: "A | B", right operand type: "C") |
| break |
| x = B() |
| [builtins fixtures/primitives.pyi] |
| |
| [case testAvoidFalseNonOverlappingEqualityCheckInLoop3] |
| # flags: --strict-equality |
| |
| for y in [1.0]: |
| if y is not None or y != "None": |
| ... |
| |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowPromotionsInsideUnions1] |
| |
| from typing import Union |
| |
| x: Union[str, float, None] |
| y: Union[int, str] |
| x = y |
| reveal_type(x) # N: Revealed type is "builtins.str | builtins.int" |
| z: Union[complex, str] |
| z = x |
| reveal_type(z) # N: Revealed type is "builtins.int | builtins.str" |
| |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowPromotionsInsideUnions2] |
| # flags: --warn-unreachable |
| |
| from typing import Optional |
| |
| def b() -> bool: ... |
| def i() -> int: ... |
| x: Optional[float] |
| |
| while b(): |
| x = None |
| while b(): |
| reveal_type(x) # N: Revealed type is "None | builtins.int" |
| if x is None or b(): |
| x = i() |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| |
| [builtins fixtures/bool.pyi] |
| |
| [case testAvoidFalseUnreachableInFinally] |
| # flags: --allow-redefinition-new --local-partial-types --warn-unreachable |
| def f() -> None: |
| try: |
| x = 1 |
| if int(): |
| x = "" |
| return |
| if int(): |
| x = None |
| return |
| finally: |
| reveal_type(x) # N: Revealed type is "builtins.int | builtins.str | None" |
| if isinstance(x, str): |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| reveal_type(x) # N: Revealed type is "builtins.int | builtins.str | None" |
| |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testNarrowingTypeVarMultiple] |
| from typing import TypeVar |
| |
| class A: ... |
| class B: ... |
| |
| T = TypeVar("T") |
| def foo(x: T) -> T: |
| if isinstance(x, A): |
| pass |
| elif isinstance(x, B): |
| pass |
| else: |
| raise |
| reveal_type(x) # N: Revealed type is "T`-1" |
| return x |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testDoNotNarrowToNever] |
| def any(): |
| return 1 |
| |
| def f() -> None: |
| x = "a" |
| x = any() |
| assert isinstance(x, int) |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testNarrowTypeVarBoundType] |
| from typing import Type, TypeVar |
| |
| class A: ... |
| class B(A): |
| other: int |
| |
| T = TypeVar("T", bound=A) |
| def test(cls: Type[T]) -> T: |
| if issubclass(cls, B): |
| reveal_type(cls) # N: Revealed type is "type[T`-1]" |
| reveal_type(cls().other) # N: Revealed type is "builtins.int" |
| return cls() |
| return cls() |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testNarrowTypeVarBoundUnion] |
| from typing import TypeVar |
| |
| class A: |
| x: int |
| class B: |
| x: str |
| |
| T = TypeVar("T") |
| def test(x: T) -> T: |
| if not isinstance(x, (A, B)): |
| return x |
| reveal_type(x) # N: Revealed type is "T`-1" |
| reveal_type(x.x) # N: Revealed type is "builtins.int | builtins.str" |
| if isinstance(x, A): |
| reveal_type(x) # N: Revealed type is "T`-1" |
| reveal_type(x.x) # N: Revealed type is "builtins.int" |
| return x |
| reveal_type(x) # N: Revealed type is "T`-1" |
| reveal_type(x.x) # N: Revealed type is "builtins.str" |
| return x |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testIsinstanceNarrowingWithSelfTypes] |
| from typing import Generic, TypeVar, overload |
| |
| T = TypeVar("T") |
| |
| class A(Generic[T]): |
| def __init__(self: A[int]) -> None: |
| pass |
| |
| def check_a(obj: "A[T] | str") -> None: |
| reveal_type(obj) # N: Revealed type is "__main__.A[T`-1] | builtins.str" |
| if isinstance(obj, A): |
| reveal_type(obj) # N: Revealed type is "__main__.A[T`-1]" |
| else: |
| reveal_type(obj) # N: Revealed type is "builtins.str" |
| |
| |
| class B(Generic[T]): |
| @overload |
| def __init__(self, x: T) -> None: ... |
| @overload |
| def __init__(self: B[int]) -> None: ... |
| def __init__(self, x: "T | None" = None) -> None: |
| pass |
| |
| def check_b(obj: "B[T] | str") -> None: |
| reveal_type(obj) # N: Revealed type is "__main__.B[T`-1] | builtins.str" |
| if isinstance(obj, B): |
| reveal_type(obj) # N: Revealed type is "__main__.B[T`-1]" |
| else: |
| reveal_type(obj) # N: Revealed type is "builtins.str" |
| |
| |
| class C(Generic[T]): |
| @overload |
| def __init__(self: C[int]) -> None: ... |
| @overload |
| def __init__(self, x: T) -> None: ... |
| def __init__(self, x: "T | None" = None) -> None: |
| pass |
| |
| def check_c(obj: "C[T] | str") -> None: |
| reveal_type(obj) # N: Revealed type is "__main__.C[T`-1] | builtins.str" |
| if isinstance(obj, C): |
| reveal_type(obj) # N: Revealed type is "__main__.C[T`-1]" |
| else: |
| reveal_type(obj) # N: Revealed type is "builtins.str" |
| |
| |
| class D(tuple[T], Generic[T]): ... |
| |
| def check_d(arg: D[T]) -> None: |
| if not isinstance(arg, D): |
| return |
| reveal_type(arg) # N: Revealed type is "tuple[T`-1, fallback=__main__.D[Any]]" |
| [builtins fixtures/tuple.pyi] |
| |
| |
| [case testNarrowingUnionMixins] |
| class Base: ... |
| |
| class FooMixin: |
| def foo(self) -> None: ... |
| |
| class BarMixin: |
| def bar(self) -> None: ... |
| |
| def baz(item: Base) -> None: |
| if not isinstance(item, (FooMixin, BarMixin)): |
| raise |
| |
| reveal_type(item) # N: Revealed type is "__main__.<subclass of "__main__.Base" and "__main__.FooMixin"> | __main__.<subclass of "__main__.Base" and "__main__.BarMixin">" |
| if isinstance(item, FooMixin): |
| reveal_type(item) # N: Revealed type is "__main__.<subclass of "__main__.Base" and "__main__.FooMixin">" |
| item.foo() |
| else: |
| reveal_type(item) # N: Revealed type is "__main__.<subclass of "__main__.Base" and "__main__.BarMixin">" |
| item.bar() |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testCustomSetterNarrowingReWidened] |
| class B: ... |
| class C(B): ... |
| class C1(B): ... |
| class D(C): ... |
| |
| class Test: |
| @property |
| def foo(self) -> C: ... |
| @foo.setter |
| def foo(self, val: B) -> None: ... |
| |
| t: Test |
| t.foo = D() |
| reveal_type(t.foo) # N: Revealed type is "__main__.D" |
| t.foo = C1() |
| reveal_type(t.foo) # N: Revealed type is "__main__.C" |
| [builtins fixtures/property.pyi] |