Skip to content

Commit dda38dc

Browse files
kirbyfan64gvanrossum
authored andcommitted
Support isinstance with a tuple of types
Fix #1328
1 parent 6bd7870 commit dda38dc

4 files changed

Lines changed: 55 additions & 11 deletions

File tree

‎lib-python/3.2/posixpath.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from genericpath import *
1818

1919
from typing import (
20-
Tuple, BinaryIO, TextIO, Pattern, AnyStr, List, Set, Any, Union
20+
Tuple, BinaryIO, TextIO, Pattern, AnyStr, List, Set, Any, Union, cast
2121
)
2222

2323
__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
@@ -56,7 +56,7 @@ def normcase(s: AnyStr) -> AnyStr:
5656
if not isinstance(s, (bytes, str)):
5757
raise TypeError("normcase() argument must be str or bytes, "
5858
"not '{}'".format(s.__class__.__name__))
59-
return s
59+
return cast(AnyStr, s)
6060

6161

6262
# Return whether a path is absolute.

‎mypy/checker.py‎

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2412,12 +2412,29 @@ def find_isinstance_check(node: Node,
24122412

24132413
def get_isinstance_type(node: Node, type_map: Dict[Node, Type]) -> Type:
24142414
type = type_map[node]
2415-
if isinstance(type, FunctionLike):
2416-
if type.is_type_obj():
2417-
# Type variables may be present -- erase them, which is the best
2418-
# we can do (outside disallowing them here).
2419-
return erase_typevars(type.items()[0].ret_type)
2420-
return None
2415+
2416+
if isinstance(type, TupleType):
2417+
all_types = type.items
2418+
else:
2419+
all_types = [type]
2420+
2421+
types = [] # type: List[Type]
2422+
2423+
for type in all_types:
2424+
if isinstance(type, FunctionLike):
2425+
if type.is_type_obj():
2426+
# Type variables may be present -- erase them, which is the best
2427+
# we can do (outside disallowing them here).
2428+
type = erase_typevars(type.items()[0].ret_type)
2429+
2430+
types.append(type)
2431+
2432+
if len(types) == 0:
2433+
return None
2434+
elif len(types) == 1:
2435+
return types[0]
2436+
else:
2437+
return UnionType(types)
24212438

24222439

24232440
def expand_node(defn: Node, map: Dict[int, Type]) -> Node:

‎mypy/test/data/check-isinstance.test‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,30 @@ while 1:
343343
z = h.pet.paws + 'a' # E: Unsupported operand types for + ("int" and "str")
344344
[builtins fixtures/isinstancelist.py]
345345

346+
[case testIsinstanceTuple]
347+
from typing import Union
348+
349+
class A:
350+
pass
351+
352+
class B:
353+
def method2(self, arg: int):
354+
return 123
355+
356+
class C:
357+
def method2(self, arg: int):
358+
return 456
359+
360+
def method3(self, arg: str):
361+
return 'abc'
362+
363+
v = A() # type: Union[A, B, C]
364+
365+
if isinstance(v, (B, C)):
366+
v.method2(123)
367+
v.method3('xyz') # E: Some element of union has no attribute "method3"
368+
[builtins fixtures/isinstance.py]
369+
346370
[case testMemberAssignmentChanges-skip]
347371
from typing import Union
348372

‎mypy/test/data/fixtures/isinstance.py‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1-
from typing import builtinclass
1+
from typing import builtinclass, Tuple, TypeVar, Generic, Union
2+
3+
T = TypeVar('T')
24

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

68
class type:
79
def __init__(self, x) -> None: pass
810

9-
class tuple: pass
11+
class tuple(Generic[T]): pass
12+
1013
class function: pass
1114

12-
def isinstance(x: object, t: type) -> bool: pass
15+
def isinstance(x: object, t: Union[type, Tuple[type, ...]]) -> bool: pass
1316

1417
class int: pass
1518
class float: pass

0 commit comments

Comments
 (0)