Skip to content

Commit

Permalink
Support new union type syntax with isinstance
Browse files Browse the repository at this point in the history
Support things like `isinstance(x, int | str)` in Python 3.10

Closes #9880.
  • Loading branch information
JukkaL committed Jul 6, 2021
1 parent 53836bd commit 0cbb3fd
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 0 deletions.
6 changes: 6 additions & 0 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5363,6 +5363,12 @@ def flatten_types(t: Type) -> List[Type]:

def get_isinstance_type(expr: Expression,
type_map: Dict[Expression, Type]) -> Optional[List[TypeRange]]:
if isinstance(expr, OpExpr) and expr.op == '|':
left = get_isinstance_type(expr.left, type_map)
right = get_isinstance_type(expr.right, type_map)
if left is None or right is None:
return None
return left + right
all_types = get_proper_types(flatten_types(type_map[expr]))
types: List[TypeRange] = []
for typ in all_types:
Expand Down
27 changes: 27 additions & 0 deletions test-data/unit/check-union-or-syntax.test
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,30 @@ y: B
class C(list[int | None]):
pass
[builtins fixtures/list.pyi]

[case testUnionOrSyntaxInIsinstance]
# flags: --python-version 3.10
class C: pass

def f(x: int | str | C) -> None:
if isinstance(x, int | str):
reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]"
else:
reveal_type(x) # N: Revealed type is "__main__.C"

def g(x: int | str | tuple[int, str] | C) -> None:
if isinstance(x, int | str | tuple):
reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, Tuple[builtins.int, builtins.str]]"
else:
reveal_type(x) # N: Revealed type is "__main__.C"
[builtins fixtures/isinstance_python3_10.pyi]

[case testUnionOrSyntaxInIsinstanceNotSupported]
# flags: --python-version 3.9
from typing import Union
def f(x: Union[int, str, None]) -> None:
if isinstance(x, int | str): # E: Unsupported left operand type for | ("Type[int]")
reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str]"
else:
reveal_type(x) # N: Revealed type is "None"
[builtins fixtures/isinstance.pyi]
29 changes: 29 additions & 0 deletions test-data/unit/fixtures/isinstance_python3_10.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# For Python 3.10+ only
from typing import Tuple, TypeVar, Generic, Union, cast, Any, Type
import types

T = TypeVar('T')

class object:
def __init__(self) -> None: pass

class type(Generic[T]):
def __init__(self, x) -> None: pass
def __or__(self, x) -> types.Union: pass

class tuple(Generic[T]): pass

class function: pass

def isinstance(x: object, t: Union[Type[object], Tuple[Type[object], ...], types.Union]) -> bool: pass
def issubclass(x: object, t: Union[Type[object], Tuple[Type[object], ...]]) -> bool: pass

class int:
def __add__(self, other: 'int') -> 'int': pass
class float: pass
class bool(int): pass
class str:
def __add__(self, other: 'str') -> 'str': pass
class ellipsis: pass

NotImplemented = cast(Any, None)
5 changes: 5 additions & 0 deletions test-data/unit/lib-stub/types.pyi
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import TypeVar
import sys

_T = TypeVar('_T')

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

class ModuleType:
__file__ = ... # type: str

if sys.version_info >= (3, 10):
class Union:
def __or__(self, x) -> Union: ...

0 comments on commit 0cbb3fd

Please sign in to comment.