| [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[int | None], /) -> 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" |
| |
| def f1(x: Any | None): |
| if x == 12: |
| reveal_type(x) # N: Revealed type is "Any" |
| |
| 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" |
| 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: --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 "__main__.D" |
| 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 "__main__.D" |
| 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: --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 --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 "builtins.int | Any" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int | Any" |
| |
| def f5(x: int) -> None: |
| if x is IE.X: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| if x is not IE.X: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" |
| |
| def f6(x: IE) -> None: |
| if x == IE.X: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.X]" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.Y]" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingWithIntEnum2] |
| # 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 "Literal[__main__.IE.X]" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.Y] | __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 "Literal[__main__.IE.X] | __main__.IE2" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.IE.Y] | __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 testNarrowingWithBytesEnum] |
| # flags: --strict-equality --warn-unreachable |
| from enum import Enum |
| |
| class BE(bytes, Enum): |
| A = b'a' |
| B = b'b' |
| |
| def f1(x: object) -> None: |
| if x == BE.A: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.BE.A]" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.object" |
| |
| def f2(x: BE) -> None: |
| if x == BE.A: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.BE.A]" |
| else: |
| reveal_type(x) # N: Revealed type is "Literal[__main__.BE.B]" |
| |
| def f3(x: bytearray | BE) -> None: |
| if x == BE.A: |
| reveal_type(x) # N: Revealed type is "builtins.bytearray | Literal[__main__.BE.A]" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.bytearray | Literal[__main__.BE.B]" |
| |
| def f4(x: memoryview | BE) -> None: |
| if x == BE.A: |
| reveal_type(x) # N: Revealed type is "builtins.memoryview | Literal[__main__.BE.A]" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.memoryview | Literal[__main__.BE.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: --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: --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] |
| |
| -- Parallel mode does not support --no-local-partial-types |
| [case testRefinePartialTypeWithinLoop_no_parallel] |
| # 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 --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 --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 --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: --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 testNegativeNarrowingInContainer] |
| # flags: --warn-unreachable |
| from enum import Enum |
| |
| class Color(Enum): |
| RED = 1 |
| |
| def count(colors: list[Color]) -> None: |
| counts: dict[Color, int] = {} |
| for color in colors: |
| if color not in counts: |
| counts[color] = 0 |
| counts[color] += 1 |
| [builtins fixtures/dict.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 testNarrowAnyUnion] |
| from typing import Any |
| |
| class A: ... |
| |
| def f1(x: A | int, y: Any): |
| if x == y: |
| reveal_type(x) # N: Revealed type is "__main__.A | builtins.int" |
| reveal_type(y) # N: Revealed type is "Any" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.A | builtins.int" |
| reveal_type(y) # N: Revealed type is "Any" |
| |
| def f2(x: A | int, y: Any | None): |
| if x == y: |
| reveal_type(x) # N: Revealed type is "__main__.A | builtins.int" |
| reveal_type(y) # N: Revealed type is "Any" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.A | builtins.int" |
| reveal_type(y) # N: Revealed type is "Any | None" |
| |
| class AA(A): ... |
| |
| def f3(x: A | int, y: AA | Any): |
| if x == y: |
| reveal_type(x) # N: Revealed type is "__main__.A | builtins.int" |
| reveal_type(y) # N: Revealed type is "__main__.AA | Any" |
| else: |
| reveal_type(x) # N: Revealed type is "__main__.A | builtins.int" |
| reveal_type(y) # N: Revealed type is "__main__.AA | Any" |
| [builtins fixtures/primitives.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 testNarrowChainedComparisonMeetAndForwardPropagation] |
| # 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" |
| |
| 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" |
| |
| 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" |
| |
| def f4(a: str | None, b: str | None, c: str | None) -> None: |
| if None is not a == b == c: |
| reveal_type(a) # N: Revealed type is "builtins.str" |
| reveal_type(b) # N: Revealed type is "builtins.str" |
| reveal_type(c) # N: Revealed type is "builtins.str" |
| |
| if (None is not a) and (a == b) and (b == c): |
| reveal_type(a) # N: Revealed type is "builtins.str" |
| reveal_type(b) # N: Revealed type is "builtins.str" |
| reveal_type(c) # N: Revealed type is "builtins.str" |
| |
| def f5(pair: tuple[None, int] | tuple[str, str], other: str | None) -> None: |
| if None is not pair[0] == other: |
| reveal_type(pair[0]) # N: Revealed type is "builtins.str" |
| reveal_type(pair) # N: Revealed type is "tuple[builtins.str, builtins.str]" |
| reveal_type(other) # N: Revealed type is "builtins.str" |
| |
| if (None is not pair[0]) and (pair[0] == other): |
| reveal_type(pair[0]) # N: Revealed type is "builtins.str" |
| reveal_type(pair) # N: Revealed type is "tuple[builtins.str, builtins.str]" |
| reveal_type(other) # N: Revealed type is "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 testTypeEqualsCheckWideningSelf] |
| # flags: --strict-equality --warn-unreachable |
| from typing import Any |
| from typing_extensions import Self |
| |
| class A: |
| def f(self: Self, y: Any, z: object): |
| if type(self) is type(y): |
| reveal_type(self) # N: Revealed type is "Self`0" |
| reveal_type(y) # N: Revealed type is "Self`0" |
| |
| if type(self) is type(z): |
| reveal_type(self) # N: Revealed type is "Self`0" |
| reveal_type(z) # N: Revealed type is "Self`0" |
| |
| if type(self) == type(y): |
| reveal_type(self) # N: Revealed type is "Self`0" |
| reveal_type(y) # N: Revealed type is "Self`0" |
| |
| if type(self) == type(z): |
| reveal_type(self) # N: Revealed type is "Self`0" |
| reveal_type(z) # N: Revealed type is "Self`0" |
| [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) |
| |
| 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() |
| 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 "Literal[5] | builtins.float" |
| 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 |
| from typing import Any |
| |
| 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, |
| v_object: object, |
| v_any: Any, |
| ) -> 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" |
| |
| if v_object == v_bytes: |
| reveal_type(v_object) # N: Revealed type is "builtins.bytes" |
| if v_object == b"asdf": |
| reveal_type(v_object) # N: Revealed type is "Literal[b'asdf']?" |
| if v_any == v_bytes: |
| reveal_type(v_any) # N: Revealed type is "Any" |
| if v_any == b"asdf": |
| reveal_type(v_any) # N: Revealed type is "Any" |
| [builtins fixtures/primitives.pyi] |
| |
| [case testNarrowingBytesLikeNoPromotion] |
| # flags: --strict-equality --warn-unreachable --no-strict-bytes |
| from __future__ import annotations |
| from typing import Any |
| |
| 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, |
| v_object: object, |
| v_any: Any, |
| ) -> 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" |
| |
| if v_object == v_bytes: |
| reveal_type(v_object) # N: Revealed type is "builtins.bytes" |
| if v_object == b"asdf": |
| reveal_type(v_object) # N: Revealed type is "Literal[b'asdf']?" |
| if v_any == v_bytes: |
| reveal_type(v_any) # N: Revealed type is "Any" |
| if v_any == b"asdf": |
| reveal_type(v_any) # N: Revealed type is "Any" |
| [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] |
| |
| |
| [case testNarrowGenericCallableEquality] |
| # flags: --strict-equality --warn-unreachable |
| from typing import Callable, TypeVar |
| |
| S = TypeVar("S") |
| T = TypeVar("T") |
| |
| def identity(x: T) -> T: |
| return x |
| |
| def msg(cmp_property: Callable[[T], S]) -> None: |
| if cmp_property == identity: |
| # TODO: the swapping of these reveal's is not ideal |
| reveal_type(cmp_property) # N: Revealed type is "def [T] (x: T`-1) -> T`-1" |
| reveal_type(identity) # N: Revealed type is "def (T`-1) -> S`-2" |
| return |
| [builtins fixtures/primitives.pyi] |
| |
| |
| [case testPropagatedParentNarrowingMeet] |
| # flags: --strict-equality --warn-unreachable |
| from __future__ import annotations |
| |
| class A: |
| tag: int |
| |
| class B: |
| tag: int |
| name = "b" |
| |
| class C: |
| tag: str |
| |
| def stringify(value: A | B | C) -> str: |
| if isinstance(value.tag, int) and isinstance(value, B): |
| reveal_type(value) # N: Revealed type is "__main__.B" |
| return value.name |
| return "" |
| [builtins fixtures/tuple.pyi] |
| |
| [case testTypeVariableBoundNarrowingSymmetric] |
| from typing import TypeVar |
| |
| class B: ... |
| class C(B): |
| x: int |
| |
| H = TypeVar('H', bound=B) |
| |
| def func(y: H) -> H: |
| x: C |
| assert x == y |
| reveal_type(x) # N: Revealed type is "H`-1" |
| reveal_type(y) # N: Revealed type is "H`-1" |
| reveal_type(x.x) # N: Revealed type is "builtins.int" |
| reveal_type(y.x) # N: Revealed type is "builtins.int" |
| if bool(): |
| return x |
| else: |
| return y |
| [builtins fixtures/primitives.pyi] |