blob: 982a86e38edd1e08d9eeb3d9122d251921f79972 [file]
[case testNarrowingParentWithStrsBasic]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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: --strict-equality --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: # E: Non-overlapping identity check (left operand type: "Literal[Key.A, Key.C, Key.B]", right operand type: "Literal[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: --strict-equality --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': # E: Non-overlapping equality check (left operand type: "Literal['A', 'C', 'B']", right operand type: "Literal['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: --strict-equality --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': # E: Non-overlapping equality check (left operand type: "Literal['A', 'C', 'B']", right operand type: "Literal['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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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: --strict-equality --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: # E: Non-overlapping identity check (left operand type: "Literal[Key.A, Key.B]", right operand type: "Literal[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: --strict-equality --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': # E: Non-overlapping equality check (left operand type: "Literal['A', 'B']", right operand type: "Literal['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]
# flags: --strict-equality --warn-unreachable
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: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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 testNarrowingEqualityCustomEqualityDisabled]
# flags: --strict-equality --warn-unreachable
from typing import Literal, Union
class Custom:
def __eq__(self, other: object) -> bool: return True
def f1(x: Union[Custom, Literal[1], Literal[2]]):
if x == 1:
reveal_type(x) # N: Revealed type is "__main__.Custom | Literal[1]"
else:
reveal_type(x) # N: Revealed type is "__main__.Custom | Literal[2]"
class Default: pass
def f2(x: Union[Default, Literal[1], Literal[2]]):
if x == 1:
reveal_type(x) # N: Revealed type is "Literal[1]"
else:
reveal_type(x) # N: Revealed type is "__main__.Default | Literal[2]"
[builtins fixtures/primitives.pyi]
[case testNarrowingEqualityCustomEqualityEnum]
# flags: --strict-equality --warn-unreachable
from typing import Literal, Union
from enum import Enum
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 "Literal[__main__.CustomEnum.B]"
# 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 testNarrowingEqualityCustomEqualityChainedComparison]
# flags: --strict-equality --warn-unreachable
from typing import Literal, Union
class Custom:
def __eq__(self, other: object) -> bool: return True
def f1(x: Literal[1, 2, None], y: Custom):
if 1 == x == y:
reveal_type(x) # N: Revealed type is "Literal[1]"
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"
class Default: pass
def f2(x: Literal[1, 2, None], z: Default):
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 testNarrowingCustomEqualityLiteralElseBranch]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from typing import Literal
class Custom:
def __eq__(self, other: object) -> bool:
raise
def f(v: Custom | Literal["text"]) -> Custom | None:
if v == "text":
reveal_type(v) # N: Revealed type is "__main__.Custom | Literal['text']"
return None
else:
reveal_type(v) # N: Revealed type is "__main__.Custom"
return v
def g(v: Custom | Literal["text"]) -> Custom | None:
if v != "text":
reveal_type(v) # N: Revealed type is "__main__.Custom"
return None
else:
reveal_type(v) # N: Revealed type is "__main__.Custom | Literal['text']"
return v # E: Incompatible return value type (got "Custom | Literal['text']", expected "Custom | None")
[builtins fixtures/primitives.pyi]
[case testNarrowingCustomEqualityUnion]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from typing import Any
def realistic(x: dict[str, Any]):
val = x.get("hey")
if val == 12:
reveal_type(val) # N: Revealed type is "Any | Literal[12]?"
def f1(x: Any | None):
if x == 12:
reveal_type(x) # N: Revealed type is "Any | Literal[12]?"
class Custom:
def __eq__(self, other: object) -> bool:
raise
def f2(x: Custom | None):
if x == 12:
reveal_type(x) # N: Revealed type is "__main__.Custom"
else:
reveal_type(x) # N: Revealed type is "__main__.Custom | None"
[builtins fixtures/dict.pyi]
[case testNarrowingCustomEqualityUnion2]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from typing import Any
class Custom:
def __eq__(self, other: object) -> bool:
raise
def f3(x: str | Custom, y: str | int):
if x == y:
reveal_type(x) # N: Revealed type is "builtins.str | __main__.Custom"
reveal_type(y) # N: Revealed type is "builtins.str | builtins.int"
else:
reveal_type(x) # N: Revealed type is "builtins.str | __main__.Custom"
reveal_type(y) # N: Revealed type is "builtins.str | builtins.int"
def f4(x: str | Any, y: str | int):
if x == y:
reveal_type(x) # N: Revealed type is "builtins.str | Any | builtins.int"
reveal_type(y) # N: Revealed type is "builtins.str | builtins.int"
else:
reveal_type(x) # N: Revealed type is "builtins.str | Any"
reveal_type(y) # N: Revealed type is "builtins.str | builtins.int"
[builtins fixtures/dict.pyi]
[case testNarrowingCustomEqualityUnion3]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from typing import Any
class Custom:
def __eq__(self, other: object) -> bool:
raise
def f(x: Custom | None, y: int | None):
if x == y:
reveal_type(x) # N: Revealed type is "__main__.Custom | None"
reveal_type(y) # N: Revealed type is "builtins.int | None"
else:
reveal_type(x) # N: Revealed type is "__main__.Custom | None"
reveal_type(y) # N: Revealed type is "builtins.int | None"
[builtins fixtures/primitives.pyi]
[case testNarrowingCustomEqualityUnion4]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
class Custom:
def __eq__(self, other: object) -> bool:
return True
def f(x: Custom | None, y: Custom):
if x == y:
# We unsoundly special case None and narrow x to Custom here
reveal_type(x) # N: Revealed type is "__main__.Custom"
else:
reveal_type(x) # N: Revealed type is "__main__.Custom | None"
[builtins fixtures/primitives.pyi]
[case testNarrowingCustomEqualityUnion5]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from typing import Any
class Custom1:
def __eq__(self, other: object) -> bool:
raise
class Custom2:
def __eq__(self, other: object) -> bool:
raise
def f(x: Custom1 | int, y: Custom2 | int):
if x == y:
reveal_type(x) # N: Revealed type is "__main__.Custom1 | builtins.int"
reveal_type(y) # N: Revealed type is "__main__.Custom2 | builtins.int"
else:
reveal_type(x) # N: Revealed type is "__main__.Custom1 | builtins.int"
reveal_type(y) # N: Revealed type is "__main__.Custom2 | builtins.int"
[builtins fixtures/primitives.pyi]
[case testNarrowingCustomEqualitySubclass]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from typing import Any
class Custom:
def __eq__(self, other: object) -> bool:
raise
class CustomSub(Custom):
def __eq__(self, other: object) -> bool:
raise
def f(x: Custom, y: CustomSub):
if x == y:
reveal_type(x) # N: Revealed type is "__main__.Custom"
reveal_type(y) # N: Revealed type is "__main__.CustomSub"
else:
reveal_type(x) # N: Revealed type is "__main__.Custom"
reveal_type(y) # N: Revealed type is "__main__.CustomSub"
[builtins fixtures/tuple.pyi]
[case testNarrowingCustomEqualityGeneric]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from typing import Union
class Custom:
def __eq__(self, other: object) -> bool:
raise
class Default: ...
def f1(x: list[Custom] | Default, y: list[int]):
if x == y: # E: Non-overlapping equality check (left operand type: "list[Custom] | Default", right operand type: "list[int]")
reveal_type(x) # N: Revealed type is "builtins.list[__main__.Custom]"
reveal_type(y) # N: Revealed type is "builtins.list[builtins.int]"
else:
reveal_type(x) # N: Revealed type is "builtins.list[__main__.Custom] | __main__.Default"
reveal_type(y) # N: Revealed type is "builtins.list[builtins.int]"
f1([], [])
def f2(x: list[Custom] | Default, y: list[int] | list[Default]):
if x == y: # E: Non-overlapping equality check (left operand type: "list[Custom] | Default", right operand type: "list[int] | list[Default]")
reveal_type(x) # N: Revealed type is "builtins.list[__main__.Custom]"
reveal_type(y) # N: Revealed type is "builtins.list[builtins.int] | builtins.list[__main__.Default]"
else:
reveal_type(x) # N: Revealed type is "builtins.list[__main__.Custom] | __main__.Default"
reveal_type(y) # N: Revealed type is "builtins.list[builtins.int] | builtins.list[__main__.Default]"
listcustom_or_default = Union[list[Custom], Default]
listint_or_default = Union[list[int], list[Default]]
def f2_with_alias(x: listcustom_or_default, y: listint_or_default):
if x == y: # E: Non-overlapping equality check (left operand type: "list[Custom] | Default", right operand type: "list[int] | list[Default]")
reveal_type(x) # N: Revealed type is "builtins.list[__main__.Custom]"
reveal_type(y) # N: Revealed type is "builtins.list[builtins.int] | builtins.list[__main__.Default]"
else:
reveal_type(x) # N: Revealed type is "builtins.list[__main__.Custom] | __main__.Default"
reveal_type(y) # N: Revealed type is "builtins.list[builtins.int] | builtins.list[__main__.Default]"
def f3(x: Custom | dict[str, str], y: dict[int, int]):
if x == y:
reveal_type(x) # N: Revealed type is "__main__.Custom | builtins.dict[builtins.str, builtins.str]"
reveal_type(y) # N: Revealed type is "builtins.dict[builtins.int, builtins.int]"
else:
reveal_type(x) # N: Revealed type is "__main__.Custom | builtins.dict[builtins.str, builtins.str]"
reveal_type(y) # N: Revealed type is "builtins.dict[builtins.int, builtins.int]"
[builtins fixtures/primitives.pyi]
[case testNarrowingRecursiveCallable]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from typing import Callable
class A: ...
class B: ...
T = Callable[[A], "S"]
S = Callable[[B], "T"]
def f(x: S, y: T):
if x == y: # E: Unsupported left operand type for == ("Callable[[B], T]")
reveal_type(x) # E: Statement is unreachable
reveal_type(y)
else:
reveal_type(x) # N: Revealed type is "def (__main__.B) -> def (__main__.A) -> ..."
reveal_type(y) # N: Revealed type is "def (__main__.A) -> def (__main__.B) -> ..."
[builtins fixtures/tuple.pyi]
[case testNarrowingRecursiveUnion]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from typing import Union
class A: ...
class B: ...
T = Union[A, "S"]
S = Union[B, "T"] # E: Invalid recursive alias: a union item of itself
def f(x: S, y: T):
if x == y:
reveal_type(x) # N: Revealed type is "Any"
reveal_type(y) # N: Revealed type is "__main__.A | Any"
[builtins fixtures/tuple.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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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: --strict-equality --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: --strict-equality --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: --strict-equality --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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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 # E: Statement 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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
from typing import Optional
class A: ...
val: Optional[A]
if val == None:
reveal_type(val) # N: Revealed type is "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 "None"
if val in (None,):
reveal_type(val) # N: Revealed type is "None"
else:
reveal_type(val) # N: Revealed type is "__main__.A"
if val not in (None,):
reveal_type(val) # N: Revealed type is "__main__.A"
else:
reveal_type(val) # N: Revealed type is "None"
[builtins fixtures/primitives.pyi]
[case testNarrowingCustomEqualityOptionalEqualsNone]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
class Custom:
def __eq__(self, other) -> bool: ...
def f(x: Custom | None):
if x == None:
reveal_type(x) # N: Revealed type is "__main__.Custom | None"
else:
reveal_type(x) # N: Revealed type is "__main__.Custom"
if x != None:
reveal_type(x) # N: Revealed type is "__main__.Custom"
else:
reveal_type(x) # N: Revealed type is "__main__.Custom | None"
[builtins fixtures/primitives.pyi]
[case testNarrowingWithTupleOfTypes]
# flags: --strict-equality --warn-unreachable
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 --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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 testNarrowingAnyUnion]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from unknown import A, AA # E: Cannot find implementation or library stub for module named "unknown" \
# N: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
class B: ...
def f(x: A | B) -> None:
if isinstance(x, (AA, B)):
reveal_type(x) # N: Revealed type is "Any | __main__.B"
else:
reveal_type(x) # N: Revealed type is "Any"
[builtins fixtures/isinstancelist.pyi]
[case testNarrowingLenItemAndLenCompare]
# flags: --strict-equality --warn-unreachable
from typing import Any
x: Any
if len(x) == x:
reveal_type(x) # N: Revealed type is "Any"
[builtins fixtures/len.pyi]
[case testNarrowingLenTuple]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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 --strict-equality --warn-unreachable
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 --strict-equality --warn-unreachable
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 --strict-equality --warn-unreachable
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: --strict-equality --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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --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) == 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]
# flags: --strict-equality --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) > 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: --strict-equality --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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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: --strict-equality --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 --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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 --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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 --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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 --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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 --strict-equality --warn-unreachable
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 --strict-equality
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: --strict-equality --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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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 "Literal[__main__.IE.X] | 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]
# flags: --strict-equality --warn-unreachable
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 "Literal[__main__.IE.X] | Any"
else:
reveal_type(x) # N: Revealed type is "Literal[__main__.IE.Y] | 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]
# flags: --strict-equality --warn-unreachable
# 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 testNarrowingWithEnumStrSubclass]
# flags: --strict-equality --warn-unreachable
from enum import Enum
class ParameterLocation(str, Enum):
QUERY = "query"
HEADER = "header"
PATH = "path"
def foo(location: ParameterLocation):
if location == "path":
reveal_type(location) # N: Revealed type is "__main__.ParameterLocation"
else:
reveal_type(location) # N: Revealed type is "__main__.ParameterLocation"
if location == ParameterLocation.PATH:
reveal_type(location) # N: Revealed type is "Literal[__main__.ParameterLocation.PATH]"
else:
reveal_type(location) # N: Revealed type is "Literal[__main__.ParameterLocation.QUERY] | Literal[__main__.ParameterLocation.HEADER]"
[builtins fixtures/primitives.pyi]
[case testConsistentNarrowingEqAndIn]
# flags: --python-version 3.10 --strict-equality --warn-unreachable
# 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"
y = x
if x in ["x"]:
reveal_type(x) # N: Revealed type is "builtins.str"
y = x
z = x
z = y
[builtins fixtures/primitives.pyi]
[case testConsistentNarrowingEqAndInWithCustomEq]
# flags: --python-version 3.10 --strict-equality --warn-unreachable
# 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 f1(x: C) -> None:
if x in [D(5)]:
reveal_type(x) # D # N: Revealed type is "__main__.C"
f1(C(5))
def f2(x: C) -> None:
if x == D(5):
reveal_type(x) # D # N: Revealed type is "__main__.C"
f2(C(5))
[builtins fixtures/primitives.pyi]
[case testNarrowingTypeVarNone]
# flags: --strict-equality --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 --strict-equality --warn-unreachable
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 --strict-equality
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 --strict-equality --warn-unreachable
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 --strict-equality
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 --strict-equality
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 --strict-equality
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 --strict-equality --warn-unreachable
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 --warn-unreachable
def f(x: int) -> None:
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 # E: Statement is unreachable
[builtins fixtures/primitives.pyi]
[case testAvoidFalseNonOverlappingEqualityCheckInLoop2]
# flags: --allow-redefinition-new --local-partial-types --strict-equality --warn-unreachable
class A: ...
class B: ...
class C: ...
def f(x: A) -> None:
while True:
if x == C(): # E: Non-overlapping equality check (left operand type: "A | B", right operand type: "C")
break # E: Statement is unreachable
x = B()
[builtins fixtures/primitives.pyi]
[case testAvoidFalseNonOverlappingEqualityCheckInLoop3]
# flags: --strict-equality --warn-unreachable
for y in [1.0]:
if y is not None or y != "None": # E: Right operand of "or" is never evaluated
...
[builtins fixtures/primitives.pyi]
[case testNarrowPromotionsInsideUnions1]
# flags: --strict-equality --warn-unreachable
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: --strict-equality --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 --strict-equality --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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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 # E: Statement is unreachable
reveal_type(arg) # N: Revealed type is "tuple[T`-1, fallback=__main__.D[Any]]"
[builtins fixtures/tuple.pyi]
[case testNarrowingUnionMixins]
# flags: --strict-equality --warn-unreachable
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]
# flags: --strict-equality --warn-unreachable
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]
[case testNarrowingNotImplemented]
# flags: --python-version 3.10 --warn-unreachable
from __future__ import annotations
import types
def foo(x: types.NotImplementedType | str):
if x is not NotImplemented:
reveal_type(x) # N: Revealed type is "builtins.str"
from typing_extensions import Self
class X:
def __divmod__(self, other: Self | int) -> tuple[Self, Self]: ...
def __floordiv__(self, other: Self | int) -> Self:
qr = self.__divmod__(other)
if qr is NotImplemented:
return NotImplemented
return qr[0]
[builtins fixtures/notimplemented.pyi]
[case testNarrowingBooleans]
# flags: --warn-return-any --strict-equality --warn-unreachable
from typing import Any
def foo(x: dict[str, Any]) -> bool:
if x.get("event") is False:
return False
if x.get("event") is True:
return True
raise
[builtins fixtures/dict.pyi]
[case testNarrowingTypeObjects]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from typing import Callable, Any, TypeVar, Generic, Protocol
_T_co = TypeVar('_T_co', covariant=True)
class Boxxy(Protocol[_T_co]):
def get_box(self) -> _T_co: ...
class TupleLike(Generic[_T_co]):
def __init__(self, iterable: Boxxy[_T_co], /) -> None:
raise
class Box1(Generic[_T_co]):
def __init__(self, content: _T_co, /) -> None: ...
def get_box(self) -> _T_co: raise
class Box2(Generic[_T_co]):
def __init__(self, content: _T_co, /) -> None: ...
def get_box(self) -> _T_co: raise
def get_type(setting_name: str) -> Callable[[Box1], Any] | type[Any]:
raise
def main(key: str):
existing_value_type = get_type(key)
if existing_value_type is TupleLike:
reveal_type(TupleLike) # N: Revealed type is "def [_T_co] (__main__.Boxxy[_T_co`1]) -> __main__.TupleLike[_T_co`1]"
reveal_type(TupleLike(Box2("str"))) # N: Revealed type is "__main__.TupleLike[builtins.str]"
reveal_type(existing_value_type) # N: Revealed type is "(def [_T_co] (__main__.Boxxy[_T_co`1]) -> __main__.TupleLike[_T_co`1]) | type[Any]"
reveal_type(existing_value_type(Box2("str"))) # N: Revealed type is "__main__.TupleLike[builtins.str] | Any"
[builtins fixtures/tuple.pyi]
[case testNarrowingCollections]
# flags: --strict-equality --warn-unreachable
from typing import cast
class X:
def __init__(self) -> None:
self.x: dict[str, str] = {}
self.y: list[str] = []
def xxx(self) -> None:
if self.x == {}:
reveal_type(self.x) # N: Revealed type is "builtins.dict[builtins.str, builtins.str]"
self.x["asdf"]
if self.x == dict():
reveal_type(self.x) # N: Revealed type is "builtins.dict[builtins.str, builtins.str]"
self.x["asdf"]
if self.x == cast(dict[int, int], {}): # E: Non-overlapping equality check (left operand type: "dict[str, str]", right operand type: "dict[int, int]")
reveal_type(self.x) # N: Revealed type is "builtins.dict[builtins.str, builtins.str]"
self.x["asdf"]
def yyy(self) -> None:
if self.y == []:
reveal_type(self.y) # N: Revealed type is "builtins.list[builtins.str]"
self.y[0].does_not_exist # E: "str" has no attribute "does_not_exist"
if self.y == list():
reveal_type(self.y) # N: Revealed type is "builtins.list[builtins.str]"
self.y[0].does_not_exist # E: "str" has no attribute "does_not_exist"
if self.y == cast(list[int], []): # E: Non-overlapping equality check (left operand type: "list[str]", right operand type: "list[int]")
reveal_type(self.y) # N: Revealed type is "builtins.list[builtins.str]"
self.y[0].does_not_exist # E: "str" has no attribute "does_not_exist"
[builtins fixtures/dict.pyi]
[case testTypeNarrowingStringInLiteralContainer]
# flags: --strict-equality --warn-unreachable
from typing import Literal
def narrow_tuple(x: str, t: tuple[Literal['a', 'b']]):
if x in t:
reveal_type(x) # N: Revealed type is "Literal['a'] | Literal['b']"
else:
reveal_type(x) # N: Revealed type is "builtins.str"
if x not in t:
reveal_type(x) # N: Revealed type is "builtins.str"
else:
reveal_type(x) # N: Revealed type is "Literal['a'] | Literal['b']"
def narrow_homo_tuple(x: str, t: tuple[Literal['a', 'b'], ...]):
if x in t:
reveal_type(x) # N: Revealed type is "Literal['a'] | Literal['b']"
else:
reveal_type(x) # N: Revealed type is "builtins.str"
def narrow_list(x: str, t: list[Literal['a', 'b']]):
if x in t:
reveal_type(x) # N: Revealed type is "Literal['a'] | Literal['b']"
else:
reveal_type(x) # N: Revealed type is "builtins.str"
def narrow_set(x: str, t: set[Literal['a', 'b']]):
if x in t:
reveal_type(x) # N: Revealed type is "Literal['a'] | Literal['b']"
else:
reveal_type(x) # N: Revealed type is "builtins.str"
[builtins fixtures/primitives.pyi]
[case testNarrowingLiteralInLiteralContainer]
# flags: --strict-equality --warn-unreachable
from typing import Literal
def narrow_tuple(x: Literal['c'], overlap: list[Literal['b', 'c']], no_overlap: list[Literal['a', 'b']]):
if x in overlap:
reveal_type(x) # N: Revealed type is "Literal['c']"
else:
reveal_type(x) # N: Revealed type is "Literal['c']"
if x in no_overlap:
reveal_type(x) # E: Statement is unreachable
else:
reveal_type(x) # N: Revealed type is "Literal['c']"
[builtins fixtures/tuple.pyi]
[case testTypeNarrowingUnionInContainer]
# flags: --strict-equality --warn-unreachable
from typing import Union, Literal
def f1(x: Union[str, float], t1: list[Literal['a', 'b']], t2: list[str]):
if x in t1:
reveal_type(x) # N: Revealed type is "Literal['a'] | Literal['b']"
else:
reveal_type(x) # N: Revealed type is "builtins.str | builtins.float"
if x in t2:
reveal_type(x) # N: Revealed type is "builtins.str"
else:
reveal_type(x) # N: Revealed type is "builtins.str | builtins.float"
[builtins fixtures/primitives.pyi]
[case testNarrowAnyWithEqualityOrContainment]
# flags: --strict-equality --warn-unreachable
# https://github.com/python/mypy/issues/17841
from typing import Any
def f1(x: Any) -> None:
if x is not None and x not in ["x"]:
return
reveal_type(x) # N: Revealed type is "Any"
def f2(x: Any) -> None:
if x is not None and x != "x":
return
reveal_type(x) # N: Revealed type is "Any"
[builtins fixtures/tuple.pyi]
[case testNarrowChainedContainment]
# flags: --strict-equality --warn-unreachable
def bad_compare_has_key(has_key: bool, key: str, s: tuple[str, ...]) -> None:
if has_key == key in s: # E: Non-overlapping equality check (left operand type: "bool", right operand type: "str")
reveal_type(has_key) # E: Statement is unreachable
reveal_type(key)
def bad_but_should_pass(has_key: bool, key: bool, s: tuple[bool, ...]) -> None:
if has_key == key in s:
reveal_type(has_key) # N: Revealed type is "builtins.bool"
reveal_type(key) # N: Revealed type is "builtins.bool"
[builtins fixtures/primitives.pyi]
[case testNarrowChainedComparisonMeet]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from typing import Any
def f1(a: str | None, b: str | None) -> None:
if None is not a == b:
reveal_type(a) # N: Revealed type is "builtins.str"
reveal_type(b) # N: Revealed type is "builtins.str | None"
if (None is not a) and (a == b):
reveal_type(a) # N: Revealed type is "builtins.str"
reveal_type(b) # N: Revealed type is "builtins.str"
def f2(a: Any | None, b: str | None) -> None:
if None is not a == b:
reveal_type(a) # N: Revealed type is "Any"
reveal_type(b) # N: Revealed type is "builtins.str | None"
if (None is not a) and (a == b):
reveal_type(a) # N: Revealed type is "Any"
reveal_type(b) # N: Revealed type is "builtins.str | None"
def f3(a: str | None, b: Any | None) -> None:
if None is not a == b:
reveal_type(a) # N: Revealed type is "builtins.str"
reveal_type(b) # N: Revealed type is "Any | builtins.str | None"
if (None is not a) and (a == b):
reveal_type(a) # N: Revealed type is "builtins.str"
reveal_type(b) # N: Revealed type is "Any | builtins.str"
[builtins fixtures/primitives.pyi]
[case testNarrowTypeObject]
# flags: --strict-equality --warn-unreachable
from typing import Any
# https://github.com/python/mypy/issues/13704
def f1(cls: type):
if cls is str:
reveal_type(cls) # N: Revealed type is "def (o: builtins.object =) -> builtins.str"
reveal_type(cls(5)) # N: Revealed type is "builtins.str"
if issubclass(cls, int):
reveal_type(cls) # N: Revealed type is "type[builtins.int]"
elif cls is str:
reveal_type(cls) # N: Revealed type is "def (o: builtins.object =) -> builtins.str"
reveal_type(cls(5)) # N: Revealed type is "builtins.str"
def f2(cls: type[object]):
if cls is str:
reveal_type(cls) # N: Revealed type is "def (o: builtins.object =) -> builtins.str"
reveal_type(cls(5)) # N: Revealed type is "builtins.str"
if issubclass(cls, int):
reveal_type(cls) # N: Revealed type is "type[builtins.int]"
elif cls is str:
reveal_type(cls) # N: Revealed type is "def (o: builtins.object =) -> builtins.str"
reveal_type(cls(5)) # N: Revealed type is "builtins.str"
def f3(cls: type[Any]):
if cls is str:
reveal_type(cls) # N: Revealed type is "type[Any]"
reveal_type(cls(5)) # N: Revealed type is "Any"
if issubclass(cls, int):
reveal_type(cls) # N: Revealed type is "type[builtins.int]"
elif cls is str:
reveal_type(cls) # N: Revealed type is "type[Any]"
reveal_type(cls(5)) # N: Revealed type is "Any"
[builtins fixtures/isinstance.pyi]
[case testNarrowTypeObjectUnion]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
def f4(cls: type[str | int]):
reveal_type(cls) # N: Revealed type is "type[builtins.str] | type[builtins.int]"
if cls is int:
reveal_type(cls) # N: Revealed type is "type[builtins.int]"
if cls == int:
reveal_type(cls) # N: Revealed type is "type[builtins.int]"
[builtins fixtures/primitives.pyi]
[case testTypeEqualsCheck]
# flags: --strict-equality --warn-unreachable
from typing import Any
y: Any
if type(y) == int:
reveal_type(y) # N: Revealed type is "builtins.int"
[case testMultipleTypeEqualsCheck]
# flags: --strict-equality --warn-unreachable
from typing import Any
x: Any
y: Any
if type(x) == type(y) == int:
reveal_type(y) # N: Revealed type is "builtins.int"
reveal_type(x) # N: Revealed type is "builtins.int"
z: Any
if int == type(z) == int:
reveal_type(z) # N: Revealed type is "builtins.int"
[case testTypeEqualsCheckWidening]
# flags: --strict-equality --warn-unreachable
from typing import Any
def f(x: str, y: Any, z: object):
if type(x) is type(y):
reveal_type(x) # N: Revealed type is "builtins.str"
reveal_type(y) # N: Revealed type is "builtins.str"
if type(x) == type(y):
reveal_type(x) # N: Revealed type is "builtins.str"
reveal_type(y) # N: Revealed type is "builtins.str"
if type(x) is type(z):
reveal_type(x) # N: Revealed type is "builtins.str"
reveal_type(z) # N: Revealed type is "builtins.str"
if type(x) == type(z):
reveal_type(x) # N: Revealed type is "builtins.str"
reveal_type(z) # N: Revealed type is "builtins.str"
[builtins fixtures/primitives.pyi]
[case testTypeEqualsCheckUsingIs]
# flags: --strict-equality --warn-unreachable
from typing import Any
y: Any
if type(y) is int:
reveal_type(y) # N: Revealed type is "builtins.int"
[case testTypeEqualsCheckUsingIsNonOverlapping]
# flags: --strict-equality --warn-unreachable
from typing import Union
y: str
if type(y) is int: # E: Non-overlapping identity check (left operand type: "type[str]", right operand type: "type[int]") \
# E: Subclass of "str" and "int" cannot exist: would have incompatible method signatures
y # E: Statement is unreachable
else:
reveal_type(y) # N: Revealed type is "builtins.str"
[builtins fixtures/isinstance.pyi]
[case testTypeEqualsCheckUsingIsNonOverlappingChild]
# flags: --strict-equality --warn-unreachable
from typing import Union
class A: ...
class B: ...
class C(A): ...
def main(x: Union[B, C]):
# C instance cannot be exactly its parent A, we need reversed subtyping relationship
# here (type(parent) is Child).
if type(x) is A:
reveal_type(x) # E: Statement is unreachable
else:
reveal_type(x) # N: Revealed type is "__main__.B | __main__.C"
[builtins fixtures/isinstance.pyi]
[case testTypeEqualsCheckTypeVar]
# flags: --strict-equality --warn-unreachable
from typing import TypeVar
class A: ...
class B: ...
T = TypeVar("T")
def forgets_about_subclasses(self, obj: T) -> T:
if isinstance(obj, A):
return A() # E: Incompatible return value type (got "A", expected "T")
elif isinstance(obj, B):
return B() # E: Incompatible return value type (got "B", expected "T")
raise
def correct1(self, obj: T) -> T:
# TODO: mypy should not error in this case
if type(obj) == A:
return A() # E: Incompatible return value type (got "A", expected "T")
elif type(obj) == B:
return B() # E: Incompatible return value type (got "B", expected "T")
raise
T_value = TypeVar("T_value", A, B)
def forgets_about_multiple_inheritance(self, obj: T_value) -> T_value:
# Note that it is a little confusing that mypy only errors in the first branch here, but this
# is a branch that would be taken by a subclass of both A and B.
# See also https://github.com/python/mypy/issues/10302#issuecomment-3832182574
if isinstance(obj, A):
return A() # E: Incompatible return value type (got "A", expected "B")
elif isinstance(obj, B):
return B()
raise
def correct2(self, obj: T_value) -> T_value:
if type(obj) == A:
return A()
elif type(obj) == B:
return B()
raise
[builtins fixtures/primitives.pyi]
[case testTypeEqualsCheckUsingDifferentSpecializedTypes]
# flags: --strict-equality --warn-unreachable
from collections import defaultdict
x: defaultdict
y: dict
z: object
if type(x) is type(y) is type(z):
reveal_type(x) # N: Revealed type is "collections.defaultdict[Any, Any]"
reveal_type(y) # N: Revealed type is "collections.defaultdict[Any, Any]"
reveal_type(z) # N: Revealed type is "collections.defaultdict[Any, Any]"
[case testTypeEqualsTypeObjectUnion]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
def f(x: object, y: type[int] | type[str]):
if type(x) == y:
reveal_type(x) # N: Revealed type is "builtins.int | builtins.str"
reveal_type(y) # N: Revealed type is "type[builtins.int] | type[builtins.str]"
[builtins fixtures/primitives.pyi]
[case testUnionTypeEquality]
# flags: --strict-equality --warn-unreachable
from typing import Any, reveal_type
def f(x: Any):
if type(x) == (int, str): # E: Non-overlapping equality check (left operand type: "type[Any]", right operand type: "tuple[type[int], type[str]]")
reveal_type(x) # E: Statement is unreachable
[builtins fixtures/tuple.pyi]
[case testTypeIntersectionWithConcreteTypes]
# flags: --strict-equality --warn-unreachable
class X: x = 1
class Y: y = 1
class Z(X, Y): ...
z = Z()
x: X = z
y: Y = z
if type(x) is type(y): # E: Non-overlapping identity check (left operand type: "type[X]", right operand type: "type[Y]")
reveal_type(x) # N: Revealed type is "__main__.<subclass of "__main__.X" and "__main__.Y">"
reveal_type(y) # N: Revealed type is "__main__.<subclass of "__main__.Y" and "__main__.X">"
x.y + y.x
if isinstance(x, type(y)) and isinstance(y, type(x)):
reveal_type(x) # N: Revealed type is "__main__.<subclass of "__main__.X" and "__main__.Y">"
reveal_type(y) # N: Revealed type is "__main__.<subclass of "__main__.X" and "__main__.Y">"
x.y + y.x
[builtins fixtures/isinstance.pyi]
[case testTypeEqualsNarrowingUnionWithElse]
# flags: --strict-equality --warn-unreachable
from typing import Union
x: Union[int, str]
if type(x) is int:
reveal_type(x) # N: Revealed type is "builtins.int"
else:
reveal_type(x) # N: Revealed type is "builtins.int | builtins.str"
[case testTypeEqualsMultipleTypesShouldntNarrow]
# flags: --strict-equality --warn-unreachable
from typing import Union
x: Union[int, str]
if type(x) == int == str: # E: Non-overlapping equality check (left operand type: "type[int]", right operand type: "type[str]")
reveal_type(x) # E: Statement is unreachable
else:
reveal_type(x) # N: Revealed type is "builtins.int | builtins.str"
[builtins fixtures/primitives.pyi]
[case testTypeNotEqualsCheck]
# flags: --strict-equality --warn-unreachable
from typing import Union
x: Union[int, str]
if type(x) != int:
reveal_type(x) # N: Revealed type is "builtins.int | builtins.str"
else:
reveal_type(x) # N: Revealed type is "builtins.int"
# mypy thinks int isn't defined unless we include this
[builtins fixtures/primitives.pyi]
[case testTypeNotEqualsCheckUsingIsNot]
# flags: --strict-equality --warn-unreachable
from typing import Union
x: Union[int, str]
if type(x) is not int:
reveal_type(x) # N: Revealed type is "builtins.int | builtins.str"
else:
reveal_type(x) # N: Revealed type is "builtins.int"
[case testTypeNarrowingAgainstType]
# flags: --strict-equality --warn-unreachable
class A:
def foo(self, x: object) -> None:
reveal_type(self) # N: Revealed type is "__main__.A"
reveal_type(x) # N: Revealed type is "builtins.object"
if type(self) is type(x):
reveal_type(self) # N: Revealed type is "__main__.A"
reveal_type(x) # N: Revealed type is "__main__.A"
else:
reveal_type(self) # N: Revealed type is "__main__.A"
reveal_type(x) # N: Revealed type is "builtins.object"
if type(self) == type(x):
reveal_type(self) # N: Revealed type is "__main__.A"
reveal_type(x) # N: Revealed type is "__main__.A"
else:
reveal_type(self) # N: Revealed type is "__main__.A"
reveal_type(x) # N: Revealed type is "builtins.object"
class B:
y: int
def __eq__(self, other: object) -> bool:
return type(other) is type(self) and other.y == self.y
[builtins fixtures/primitives.pyi]
[case testNarrowInElseCaseIfFinal]
# flags: --strict-equality --warn-unreachable
from typing import final, Union
@final
class C:
pass
class D:
pass
x: Union[C, D]
if type(x) is C:
reveal_type(x) # N: Revealed type is "__main__.C"
else:
reveal_type(x) # N: Revealed type is "__main__.D"
[case testNarrowInIfCaseIfFinalUsingIsNot]
# flags: --strict-equality --warn-unreachable
from typing import final, Union
@final
class C:
pass
class D:
pass
x: Union[C, D]
if type(x) is not C:
reveal_type(x) # N: Revealed type is "__main__.D"
else:
reveal_type(x) # N: Revealed type is "__main__.C"
[case testDunderClassNarrowing]
# flags: --strict-equality --warn-unreachable
from typing import Any
def foo(y: object):
if y.__class__ == int:
reveal_type(y) # N: Revealed type is "builtins.int"
else:
reveal_type(y) # N: Revealed type is "builtins.object"
if y.__class__ is int:
reveal_type(y) # N: Revealed type is "builtins.int"
else:
reveal_type(y) # N: Revealed type is "builtins.object"
def bar(y: Any):
if y.__class__ == int:
reveal_type(y) # N: Revealed type is "builtins.int"
else:
reveal_type(y) # N: Revealed type is "Any"
if y.__class__ is int:
reveal_type(y) # N: Revealed type is "builtins.int"
else:
reveal_type(y) # N: Revealed type is "Any"
[builtins fixtures/dict-full.pyi]
[case testNarrowTypeVarType]
# flags: --strict-equality --warn-unreachable
from typing import TypeVar
T = TypeVar("T")
class A: ...
def foo(X: type[T]) -> T:
if X == A:
return X()
raise
# It could be nice to make these two test cases consistent, but it would be tricky
# The above case is much more common in real world code
def bar(X: type[T]) -> T:
if X == A:
return A() # E: Incompatible return value type (got "A", expected "T")
raise
[builtins fixtures/type.pyi]
[case testNarrowingConstrainedTypeVarType]
# flags: --strict-equality --warn-unreachable
from typing import TypeVar, Any, Type
TargetType = TypeVar("TargetType", int, float, str)
# TODO: this behaviour is incorrect, it will be fixed by improving reachability
def convert_type(target_type: Type[TargetType]) -> TargetType:
if target_type == str:
return str()
if target_type == int:
return int()
if target_type == float:
return float() # E: Incompatible return value type (got "float", expected "int")
raise
[builtins fixtures/primitives.pyi]
[case testNarrowingEqualityWithPromotions]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from typing import Literal
def f1(number: float, i: int):
if number == i:
reveal_type(number) # N: Revealed type is "builtins.float"
reveal_type(i) # N: Revealed type is "builtins.int"
def f2(number: float, five: Literal[5]):
if number == five:
reveal_type(number) # N: Revealed type is "builtins.float"
reveal_type(five) # N: Revealed type is "Literal[5]"
def f3(number: float | int, five: Literal[5]):
if number == five:
reveal_type(number) # N: Revealed type is "builtins.float | Literal[5]"
reveal_type(five) # N: Revealed type is "Literal[5]"
def f8(number: float | Literal[5], five: Literal[5]):
if number == five:
reveal_type(number) # N: Revealed type is "builtins.float | Literal[5]"
reveal_type(five) # N: Revealed type is "Literal[5]"
else:
reveal_type(number) # N: Revealed type is "builtins.float"
reveal_type(five) # N: Revealed type is "Literal[5]"
def f4(number: float | None, i: int):
if number == i:
reveal_type(number) # N: Revealed type is "builtins.float"
reveal_type(i) # N: Revealed type is "builtins.int"
def f5(number: float | int, i: int):
if number == i:
reveal_type(number) # N: Revealed type is "builtins.float | builtins.int"
reveal_type(i) # N: Revealed type is "builtins.int"
def f6(number: float | complex, i: int):
if number == i:
reveal_type(number) # N: Revealed type is "builtins.float | builtins.complex"
reveal_type(i) # N: Revealed type is "builtins.int"
class Custom:
def __eq__(self, other: object) -> bool: return True
def f7(number: float, x: Custom | int):
if number == x:
reveal_type(number) # N: Revealed type is "builtins.float"
reveal_type(x) # N: Revealed type is "__main__.Custom | builtins.int"
[builtins fixtures/primitives.pyi]
[case testNarrowingAnyNegativeIntersection-xfail]
# flags: --strict-equality --warn-unreachable
# https://github.com/python/mypy/issues/20597
from __future__ import annotations
from typing import Any
class array: ...
def get_result() -> Any: ...
def foo(x: str | array) -> str:
result = get_result()
if isinstance(result, array):
return "asdf"
if result is x:
reveal_type(result) # N: Revealed type is "Any"
return result
raise
[builtins fixtures/tuple.pyi]
[case testNarrowingOptionalBytes]
from __future__ import annotations
def f(x: bytes | None):
if x == b"asdf":
reveal_type(x) # N: Revealed type is "builtins.bytes"
else:
reveal_type(x) # N: Revealed type is "builtins.bytes | None"
[builtins fixtures/primitives.pyi]
[case testNarrowingBytesLikeWithPromotion]
# flags: --strict-equality --warn-unreachable --strict-bytes
from __future__ import annotations
def check_test(x: bytes) -> None: ...
check_test(bytearray(b"asdf")) # E: Argument 1 to "check_test" has incompatible type "bytearray"; expected "bytes"
def main(
v_bytes: bytes,
v_bytearray: bytearray,
v_memoryview: memoryview,
v_all: bytes | bytearray | memoryview,
) -> None:
if v_bytes == v_bytearray:
reveal_type(v_bytes) # N: Revealed type is "builtins.bytes"
reveal_type(v_bytearray) # N: Revealed type is "builtins.bytearray"
if v_bytes == v_memoryview:
reveal_type(v_bytes) # N: Revealed type is "builtins.bytes"
reveal_type(v_memoryview) # N: Revealed type is "builtins.memoryview"
if v_bytearray == v_memoryview:
reveal_type(v_bytearray) # N: Revealed type is "builtins.bytearray"
reveal_type(v_memoryview) # N: Revealed type is "builtins.memoryview"
if v_all == v_bytes:
reveal_type(v_all) # N: Revealed type is "builtins.bytes"
reveal_type(v_bytes) # N: Revealed type is "builtins.bytes"
if v_all == v_bytearray:
reveal_type(v_all) # N: Revealed type is "builtins.bytes | builtins.bytearray | builtins.memoryview"
reveal_type(v_bytearray) # N: Revealed type is "builtins.bytearray"
if v_all == v_memoryview:
reveal_type(v_all) # N: Revealed type is "builtins.bytes | builtins.bytearray | builtins.memoryview"
reveal_type(v_memoryview) # N: Revealed type is "builtins.memoryview"
[builtins fixtures/primitives.pyi]
[case testNarrowingBytesLikeNoPromotion]
# flags: --strict-equality --warn-unreachable --no-strict-bytes
from __future__ import annotations
def check_test(x: bytes) -> None: ...
check_test(bytearray(b"asdf"))
def main(
v_bytes: bytes,
v_bytearray: bytearray,
v_memoryview: memoryview,
v_all: bytes | bytearray | memoryview,
) -> None:
if v_bytes == v_bytearray:
reveal_type(v_bytes) # N: Revealed type is "builtins.bytes"
reveal_type(v_bytearray) # N: Revealed type is "builtins.bytearray"
if v_bytes == v_memoryview:
reveal_type(v_bytes) # N: Revealed type is "builtins.bytes"
reveal_type(v_memoryview) # N: Revealed type is "builtins.memoryview"
if v_bytearray == v_memoryview:
reveal_type(v_bytearray) # N: Revealed type is "builtins.bytearray"
reveal_type(v_memoryview) # N: Revealed type is "builtins.memoryview"
if v_all == v_bytes:
reveal_type(v_all) # N: Revealed type is "builtins.bytes | builtins.bytearray | builtins.memoryview"
reveal_type(v_bytes) # N: Revealed type is "builtins.bytes"
if v_all == v_bytearray:
reveal_type(v_all) # N: Revealed type is "builtins.bytes | builtins.bytearray | builtins.memoryview"
reveal_type(v_bytearray) # N: Revealed type is "builtins.bytearray"
if v_all == v_memoryview:
reveal_type(v_all) # N: Revealed type is "builtins.bytes | builtins.bytearray | builtins.memoryview"
reveal_type(v_memoryview) # N: Revealed type is "builtins.memoryview"
[builtins fixtures/primitives.pyi]
[case testNarrowNewTypeVsSubclass]
# mypy: strict-equality, warn-unreachable
from typing import NewType
M1 = NewType("M1", int)
M2 = NewType("M2", int)
def check_m(base: int, m1: M1, m2: M2):
if m1 == m2: # E: Non-overlapping equality check (left operand type: "M1", right operand type: "M2")
reveal_type(m1) # N: Revealed type is "__main__.M1"
reveal_type(m2) # N: Revealed type is "__main__.M2"
if m1 == base:
# We do not narrow base
reveal_type(m1) # N: Revealed type is "__main__.M1"
reveal_type(base) # N: Revealed type is "builtins.int"
if m2 == base:
reveal_type(m2) # N: Revealed type is "__main__.M2"
reveal_type(base) # N: Revealed type is "builtins.int"
# We do narrow for subclasses! (assuming no custom equality)
class A: ...
class A1(A): ...
class A2(A): ...
def check_a(base: A, a1: A1, a2: A2):
if a1 == a2: # E: Non-overlapping equality check (left operand type: "A1", right operand type: "A2")
reveal_type(a1) # E: Statement is unreachable
reveal_type(a2)
if a1 == base:
# We do narrow base
reveal_type(a1) # N: Revealed type is "__main__.A1"
reveal_type(base) # N: Revealed type is "__main__.A1"
if a2 == base: # E: Non-overlapping equality check (left operand type: "A2", right operand type: "A1")
reveal_type(a2) # E: Statement is unreachable
reveal_type(base)
[builtins fixtures/primitives.pyi]
[case testNarrowNewTypeFromObject]
# mypy: strict-equality, warn-unreachable
from __future__ import annotations
from typing import NewType
UserId = NewType("UserId", int)
def f1(whatever: object, uid: UserId):
# The general principle is that we should not be able to produce a value of NewType
# without there being explicit wrapping somewhere
if whatever == uid:
reveal_type(whatever) # N: Revealed type is "builtins.int"
reveal_type(uid) # N: Revealed type is "__main__.UserId"
class Other: ...
def f2(whatever: object, uid: UserId | Other):
if whatever == uid:
reveal_type(whatever) # N: Revealed type is "builtins.int | __main__.Other"
reveal_type(uid) # N: Revealed type is "__main__.UserId | __main__.Other"
[builtins fixtures/primitives.pyi]
[case testNarrowNewTypeNested]
# mypy: strict-equality, warn-unreachable
from typing import NewType, Final
Path = NewType("Path", str)
NormPath = NewType("NormPath", Path)
def op(normpath: NormPath, path: Path):
if normpath == path:
# No narrowing
reveal_type(normpath) # N: Revealed type is "__main__.NormPath"
reveal_type(path) # N: Revealed type is "__main__.Path"
[builtins fixtures/primitives.pyi]
[case testNarrowNewTypeSharedValue]
# mypy: strict-equality, warn-unreachable
from typing import NewType, Final
UserId = NewType("UserId", int)
TeamId = NewType("TeamId", int)
INVALID = 123
def get_owner(uid: UserId, tid: TeamId):
# No narrowing for INVALID
if uid == INVALID:
reveal_type(uid) # N: Revealed type is "__main__.UserId"
reveal_type(tid) # N: Revealed type is "__main__.TeamId"
reveal_type(INVALID) # N: Revealed type is "builtins.int"
if tid == INVALID:
reveal_type(uid) # N: Revealed type is "__main__.UserId"
reveal_type(tid) # N: Revealed type is "__main__.TeamId"
reveal_type(INVALID) # N: Revealed type is "builtins.int"
return None
[builtins fixtures/primitives.pyi]
[case testNarrowCallableTypeVarByEquality]
from typing import Callable, TypeVar
T = TypeVar("T")
def remove(path: str) -> None: ...
def unlink(path: str) -> None: ...
def f1(func: Callable[..., T], arg: str) -> T:
if func == remove:
reveal_type(func) # N: Revealed type is "def (path: builtins.str) -> T`-1"
reveal_type(func(arg)) # N: Revealed type is "T`-1"
return func(arg)
return func(arg)
def f2(func: Callable[..., T], arg: str) -> T:
if func in [unlink, remove]:
reveal_type(func) # N: Revealed type is "def (path: builtins.str) -> T`-1"
reveal_type(func(arg)) # N: Revealed type is "T`-1"
return func(arg)
return func(arg)
[builtins fixtures/primitives.pyi]