8000 Support `or` in `isinstance` checks · python/mypy@d025588 · GitHub
[go: up one dir, main page]

Skip to content

Commit d025588

Browse files
committed
Support or in isinstance checks
Fixes #942.
1 parent 2428312 commit d025588

File tree

2 files changed

+71
-3
lines changed

2 files changed

+71
-3
lines changed

mypy/checker.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2437,6 +2437,21 @@ def and_conditional_maps(m1: Optional[TypeMap], m2: Optional[TypeMap]) -> Option
24372437
return result
24382438

24392439

2440+
def or_conditional_maps(m1: Optional[TypeMap], m2: Optional[TypeMap]) -> Optional[TypeMap]:
2441+
if m1 is None:
2442+
return m2
2443+
if m2 is None:
2444+
return m1
2445+
# Both branches are possible. Combine information about variables
2446+
# whose type is refined in both branches.
2447+
result = {}
2448+
for n1 in m1:
2449+
for n2 in m2:
2450+
if n1.literal_hash == n2.literal_hash:
2451+
result[n1] = UnionType.make_simplified_union([m1[n1], m2[n2]])
2452+
return result
2453+
2454+
24402455
def find_isinstance_check(node: Node,
24412456
type_map: Dict[Node, Type],
24422457
weak: bool=False
@@ -2484,7 +2499,22 @@ def find_isinstance_check(node: Node,
24842499
_, if_vars = conditional_type_map(node, vartype, NoneTyp(), weak=weak)
24852500
return if_vars, {}
24862501
elif isinstance(node, OpExpr) and node.op == 'and':
2487-
left_if_vars, right_else_vars = find_isinstance_check(
2502+
left_if_vars, left_else_vars = find_isinstance_check(
2503+
node.left,
2504+
type_map,
2505+
weak,
2506+
)
2507+
2508+
right_if_vars, right_else_vars = find_isinstance_check(
2509+
node.right,
2510+
type_map,
2511+
weak,
2512+
)
2513+
2514+
return (and_conditional_maps(left_if_vars, right_if_vars),
2515+
or_conditional_maps(left_else_vars, right_else_vars))
2516+
elif isinstance(node, OpExpr) and node.op == 'or':
2517+
left_if_vars, left_else_vars = find_isinstance_check(
24882518
node.left,
24892519
type_map,
24902520
weak,
@@ -2496,8 +2526,8 @@ def find_isinstance_check(node: Node,
24962526
weak,
24972527
)
24982528

2499-
# Make no claim about the types in else
2500-
return and_conditional_maps(left_if_vars, right_if_vars), {}
2529+
return (or_conditional_maps(left_if_vars, right_if_vars),
2530+
and_conditional_maps(left_else_vars, right_else_vars))
25012531
elif isinstance(node, UnaryExpr) and node.op == 'not':
25022532
left, right = find_isinstance_check(node.expr, type_map, weak)
25032533
return right, left

test-data/unit/check-isinstance.test

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,24 @@ def foo(): pass
800800
[out]
801801
main: note: In function "f":
802802

803+
[case testIsinstanceOr]
804+
from typing import Optional
805+
def f(a: bool, x: object) -> Optional[int]:
806+
if a or not isinstance(x, int):
807+
return None
808+
return x
809+
def g(a: bool, x: object) -> Optional[int]:
810+
if not isinstance(x, int) or a:
811+
return None
812+
return x
813+
def h(a: bool, x: object) -> Optional[int]:
814+
if a or isinstance(x, int):
815+
return None
816+
return x # E: Incompatible return value type (got "object", expected "int")
817+
[builtins fixtures/isinstance.py]
818+
[out]
819+
main: note: In function "h":
820+
803821
[case testIsinstanceWithOverlappingUnionType]
804822
from typing import Union
805823
def f(x: Union[float, int]) -> None:
@@ -854,3 +872,23 @@ def f(x: Union[List[int], str]) -> None:
854872
[out]
855873
main: note: In function "f":
856874
main:4: error: "int" not callable
875+
876+
[case testIsinstanceOrIsinstance]
877+
class A: pass
878+
class B(A):
879+
flag = 1
880+
class C(A):
881+
flag = 2
882+
def f(x: A) -> int:
883+
if isinstance(x, B) or isinstance(x, C):
884+
return x.flag
885+
else:
886+
return 0
887+
def g(x: A) -> int:
888+
if isinstance(x, A) or isinstance(x, C):
889+
return x.flag # E: "A" has no attribute "flag"
890+
else:
891+
return 0
892+
[builtins fixtures/isinstance.py]
893+
[out]
894+
main: note: In function "g":

0 commit comments

Comments
 (0)
0