Skip to content

Commit 0cbb3fd

Browse files
committed
Support new union type syntax with isinstance
Support things like `isinstance(x, int | str)` in Python 3.10 Closes #9880.
1 parent 53836bd commit 0cbb3fd

File tree

4 files changed

+67
-0
lines changed

4 files changed

+67
-0
lines changed

mypy/checker.py

+6
Original file line numberDiff line numberDiff line change
@@ -5363,6 +5363,12 @@ def flatten_types(t: Type) -> List[Type]:
53635363

53645364
def get_isinstance_type(expr: Expression,
53655365
type_map: Dict[Expression, Type]) -> Optional[List[TypeRange]]:
5366+
if isinstance(expr, OpExpr) and expr.op == '|':
5367+
left = get_isinstance_type(expr.left, type_map)
5368+
right = get_isinstance_type(expr.right, type_map)
5369+
if left is None or right is None:
5370+
return None
5371+
return left + right
53665372
all_types = get_proper_types(flatten_types(type_map[expr]))
53675373
types: List[TypeRange] = []
53685374
for typ in all_types:

test-data/unit/check-union-or-syntax.test

+27
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,30 @@ y: B
169169
class C(list[int | None]):
170170
pass
171171
[builtins fixtures/list.pyi]
172+
173+
[case testUnionOrSyntaxInIsinstance]
174+
# flags: --python-version 3.10
175+
class C: pass
176+
177+
def f(x: int | str | C) -> None:
178+
if isinstance(x, int | str):
179+
reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]"
180+
else:
181+
reveal_type(x) # N: Revealed type is "__main__.C"
182+
183+
def g(x: int | str | tuple[int, str] | C) -> None:
184+
if isinstance(x, int | str | tuple):
185+
reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, Tuple[builtins.int, builtins.str]]"
186+
else:
187+
reveal_type(x) # N: Revealed type is "__main__.C"
188+
[builtins fixtures/isinstance_python3_10.pyi]
189+
190+
[case testUnionOrSyntaxInIsinstanceNotSupported]
191+
# flags: --python-version 3.9
192+
from typing import Union
193+
def f(x: Union[int, str, None]) -> None:
194+
if isinstance(x, int | str): # E: Unsupported left operand type for | ("Type[int]")
195+
reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]"
196+
else:
197+
reveal_type(x) # N: Revealed type is "None"
198+
[builtins fixtures/isinstance.pyi]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# For Python 3.10+ only
2+
from typing import Tuple, TypeVar, Generic, Union, cast, Any, Type
3+
import types
4+
5+
T = TypeVar('T')
6+
7+
class object:
8+
def __init__(self) -> None: pass
9+
10+
class type(Generic[T]):
11+
def __init__(self, x) -> None: pass
12+
def __or__(self, x) -> types.Union: pass
13+
14+
class tuple(Generic[T]): pass
15+
16+
class function: pass
17+
18+
def isinstance(x: object, t: Union[Type[object], Tuple[Type[object], ...], types.Union]) -> bool: pass
19+
def issubclass(x: object, t: Union[Type[object], Tuple[Type[object], ...]]) -> bool: pass
20+
21+
class int:
22+
def __add__(self, other: 'int') -> 'int': pass
23+
class float: pass
24+
class bool(int): pass
25+
class str:
26+
def __add__(self, other: 'str') -> 'str': pass
27+
class ellipsis: pass
28+
29+
NotImplemented = cast(Any, None)

test-data/unit/lib-stub/types.pyi

+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from typing import TypeVar
2+
import sys
23

34
_T = TypeVar('_T')
45

@@ -8,3 +9,7 @@ class bool: ...
89

910
class ModuleType:
1011
__file__ = ... # type: str
12+
13+
if sys.version_info >= (3, 10):
14+
class Union:
15+
def __or__(self, x) -> Union: ...

0 commit comments

Comments
 (0)