blob: 03586e4109f672b0b785b72f896e1180c2c13720 [file] [log] [blame] [edit]
[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]