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

Skip to content

Commit 172ac66

Browse files
committed
Support or in isinstance checks
Fixes #942.
1 parent 052b814 commit 172ac66

File tree

2 files changed

+73
-3
lines changed

2 files changed

+73
-3
lines changed

mypy/checker.py

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

24362436

2437+
def or_conditional_maps(m1: Optional[TypeMap], m2: Optional[TypeMap]) -> Optional[TypeMap]:
2438+
if m1 is None:
2439+
return m2
2440+
if m2 is None:
2441+
return m1
2442+
# Both branches are possible. Combine information about variables
2443+
# whose type is refined in both branches.
2444+
result = {}
2445+
for n1 in m1:
2446+
for n2 in m2:
2447+
if n1.literal_hash == n2.literal_hash:
2448+
result[n1] = UnionType.make_simplified_union([m1[n1], m2[n2]])
2449+
return result
2450+
2451+
24372452
def find_isinstance_check(node: Node,
24382453
type_map: Dict[Node, Type],
24392454
weak: bool=False
@@ -2481,7 +2496,22 @@ def find_isinstance_check(node: Node,
24812496
_, if_vars = conditional_type_map(node, vartype, NoneTyp(), weak=weak)
24822497
return if_vars, {}
24832498
elif isinstance(node, OpExpr) and node.op == 'and':
2484-
left_if_vars, right_else_vars = find_isinstance_check(
2499+
left_if_vars, left_else_vars = find_isinstance_check(
2500+
node.left,
2501+
type_map,
2502+
weak,
2503+
)
2504+
2505+
right_if_vars, right_else_vars = find_isinstance_check(
2506+
node.right,
2507+
type_map,
2508+
weak,
2509+
)
2510+
2511+
return (and_conditional_maps(left_if_vars, right_if_vars),
2512+
or_conditional_maps(left_else_vars, right_else_vars))
2513+
elif isinstance(node, OpExpr) and node.op == 'or':
2514+
left_if_vars, left_else_vars = find_isinstance_check(
24852515
node.left,
24862516
type_map,
24872517
weak,
@@ -2493,8 +2523,8 @@ def find_isinstance_check(node: Node,
24932523
weak,
24942524
)
24952525

2496-
# Make no claim about the types in else
2497-
return and_conditional_maps(left_if_vars, right_if_vars), {}
2526+
return (or_conditional_maps(left_if_vars, right_if_vars),
2527+
and_conditional_maps(left_else_vars, right_else_vars))
24982528
elif isinstance(node, UnaryExpr) and node.op == 'not':
24992529
left, right = find_isinstance_check(node.expr, type_map, weak)
25002530
return right, left

test-data/unit/check-isinstance.test

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,25 @@ 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
817+
[builtins fixtures/isinstance.py]
818+
[out]
819+
main: note: In function "h":
820+
main:13: error: Incompatible return value type (got "object", expected "int")
821+
803822
[case testIsinstanceWithOverlappingUnionType]
804823
from typing import Union
805824
def f(x: Union[float, int]) -> None:
@@ -854,3 +873,24 @@ def f(x: Union[List[int], str]) -> None:
854873
[out]
855874
main: note: In function "f":
856875
main:4: error: "int" not callable
876+
877+
[case testIsinstanceOrIsinstance]
878+
class A: pass
879+
class B(A):
880+
flag = 1
881+
class C(A):
882+
flag = 2
883+
def f(x: A) -> int:
884+
if isinstance(x, B) or isinstance(x, C):
885+
return x.flag
886+
else:
887+
return 0
888+
def g(x: A) -> int:
889+
if isinstance(x, A) or isinstance(x, C):
890+
return x.flag
891+
else:
892+
return 0
893+
[builtins fixtures/isinstance.py]
894+
[out]
895+
main: note: In function "g":
896+
main:13: error: "A" has no attribute "flag"

0 commit comments

Comments
 (0)
0