2
2
3
3
import itertools
4
4
5
- from typing import Any , Dict , Set , List , cast , Tuple , Callable , TypeVar , Union
5
+ from typing import (
6
+ Any , Dict , Set , List , cast , Tuple , Callable , TypeVar , Union , Optional
7
+ )
6
8
7
9
from mypy .errors import Errors
8
10
from mypy .nodes import (
47
49
from mypy .meet import meet_simple , meet_simple_away , nearest_builtin_ancestor , is_overlapping_types
48
50
49
51
50
- # Kinds of isinstance checks.
51
- ISINSTANCE_OVERLAPPING = 0
52
- ISINSTANCE_ALWAYS_TRUE = 1
53
- ISINSTANCE_ALWAYS_FALSE = 2
54
-
55
52
T = TypeVar ('T' )
56
53
57
54
@@ -1448,16 +1445,21 @@ def visit_if_stmt(self, s: IfStmt) -> Type:
1448
1445
for e , b in zip (s .expr , s .body ):
1449
1446
t = self .accept (e )
1450
1447
self .check_not_void (t , e )
1451
- var , type , elsetype , kind = find_isinstance_check (e , self .type_map ,
1452
- self .typing_mode_weak ())
1453
- if kind == ISINSTANCE_ALWAYS_FALSE :
1448
+ if_map , else_map = find_isinstance_check (
1449
+ e , self .type_map ,
1450
+ self .typing_mode_weak ()
1451
+ )
1452
+ if if_map is None :
1453
+ # The condition is always false
1454
1454
# XXX should issue a warning?
1455
1455
pass
1456
1456
else :
1457
1457
# Only type check body if the if condition can be true.
1458
1458
self .binder .push_frame ()
1459
- if var :
1460
- self .binder .push (var , type )
1459
+ if if_map :
1460
+ for var , type in if_map .items ():
1461
+ self .binder .push (var , type )
1462
+
1461
1463
self .accept (b )
1462
1464
_ , frame = self .binder .pop_frame ()
1463
1465
if not self .breaking_out :
@@ -1466,9 +1468,10 @@ def visit_if_stmt(self, s: IfStmt) -> Type:
1466
1468
1467
1469
self .breaking_out = False
1468
1470
1469
- if var :
1470
- self .binder .push (var , elsetype )
1471
- if kind == ISINSTANCE_ALWAYS_TRUE :
1471
+ if else_map :
1472
+ for var , type in else_map .items ():
1473
+ self .binder .push (var , type )
1474
+ if else_map is None :
1472
1475
# The condition is always true => remaining elif/else blocks
1473
1476
# can never be reached.
1474
1477
@@ -2061,24 +2064,17 @@ def map_type_from_supertype(typ: Type, sub_info: TypeInfo,
2061
2064
2062
2065
def find_isinstance_check (node : Node ,
2063
2066
type_map : Dict [Node , Type ],
2064
- weak : bool = False ) -> Tuple [Node , Type , Type , int ]:
2065
- """Check if node is an isinstance(variable, type) check.
2066
-
2067
- If successful, return tuple (variable, target-type, else-type,
2068
- kind); otherwise, return (None, AnyType, AnyType, -1).
2067
+ weak : bool = False ) \
2068
+ -> Tuple [Optional [Dict [Node , Type ]], Optional [Dict [Node , Type ]]]:
2069
+ """Find any isinstance checks (within a chain of ands).
2069
2070
2070
- When successful, the kind takes one of these values:
2071
+ Return value is a map of variables to their types if the condition
2072
+ is true and a map of variables to their types if the condition is false.
2071
2073
2072
- ISINSTANCE_OVERLAPPING: The type of variable and the target type are
2073
- partially overlapping => the test result can be True or False.
2074
- ISINSTANCE_ALWAYS_TRUE: The target type at least as general as the
2075
- variable type => the test is always True.
2076
- ISINSTANCE_ALWAYS_FALSE: The target type and the variable type are not
2077
- overlapping => the test is always False.
2074
+ If either of the values in the tuple is None, then that particular
2075
+ branch can never occur.
2078
2076
2079
- If it is an isinstance check, but we don't understand the argument
2080
- type, then in weak mode it is treated as Any and in non-weak mode
2081
- it is not treated as an isinstance.
2077
+ Guaranteed to not return None, None. (But may return {}, {})
2082
2078
"""
2083
2079
if isinstance (node , CallExpr ):
2084
2080
if refers_to_fullname (node .callee , 'builtins.isinstance' ):
@@ -2087,46 +2083,48 @@ def find_isinstance_check(node: Node,
2087
2083
vartype = type_map [expr ]
2088
2084
type = get_isinstance_type (node .args [1 ], type_map )
2089
2085
if type :
2090
- kind = ISINSTANCE_OVERLAPPING
2091
2086
elsetype = vartype
2092
2087
if vartype :
2093
2088
if is_proper_subtype (vartype , type ):
2094
- kind = ISINSTANCE_ALWAYS_TRUE
2095
2089
elsetype = None
2090
+ return {expr : type }, None
2096
2091
elif not is_overlapping_types (vartype , type ):
2097
- kind = ISINSTANCE_ALWAYS_FALSE
2092
+ return None , { expr : elsetype }
2098
2093
else :
2099
2094
elsetype = restrict_subtype_away (vartype , type )
2100
- return expr , type , elsetype , kind
2095
+ return { expr : type }, { expr : elsetype }
2101
2096
else :
2102
2097
# An isinstance check, but we don't understand the type
2103
2098
if weak :
2104
- return expr , AnyType (), vartype , ISINSTANCE_OVERLAPPING
2099
+ return { expr : AnyType ()}, { expr : vartype }
2105
2100
elif isinstance (node , OpExpr ) and node .op == 'and' :
2106
- # XXX We should extend this to support two isinstance checks in the same
2107
- # expression
2108
- (var , type , elsetype , kind ) = find_isinstance_check (node .left , type_map , weak )
2109
- if var is None :
2110
- (var , type , elsetype , kind ) = find_isinstance_check (node .left , type_map , weak )
2111
- if var :
2112
- if kind == ISINSTANCE_ALWAYS_TRUE :
2113
- kind = ISINSTANCE_OVERLAPPING
2114
- return (var , type , AnyType (), kind )
2101
+ left_if_vars , right_else_vars = find_isinstance_check (
2102
+ node .left ,
2103
+ type_map ,
2104
+ weak ,
2105
+ )
2106
+
2107
+ right_if_vars , right_else_vars = find_isinstance_check (
2108
+ node .right ,
2109
+ type_map ,
2110
+ weak ,
2111
+ )
2112
+ if left_if_vars :
2113
+ if right_if_vars is not None :
2114
+ left_if_vars .update (right_if_vars )
2115
+ else :
2116
+ left_if_vars = None
2117
+ else :
2118
+ left_if_vars = right_if_vars
2119
+
2120
+ # Make no claim about the types in else
2121
+ return left_if_vars , {}
2115
2122
elif isinstance (node , UnaryExpr ) and node .op == 'not' :
2116
- ( var , type , elsetype , kind ) = find_isinstance_check (node .expr , type_map , weak )
2117
- return ( var , elsetype , type , invert_isinstance_kind ( kind ))
2123
+ left , right = find_isinstance_check (node .expr , type_map , weak )
2124
+ return right , left
2118
2125
2119
2126
# Not a supported isinstance check
2120
- return None , AnyType (), AnyType (), - 1
2121
-
2122
-
2123
- def invert_isinstance_kind (kind : int ) -> int :
2124
- if kind == ISINSTANCE_ALWAYS_TRUE :
2125
- return ISINSTANCE_ALWAYS_FALSE
2126
- elif kind == ISINSTANCE_ALWAYS_FALSE :
2127
- return ISINSTANCE_ALWAYS_TRUE
2128
- else :
2129
- return kind
2127
+ return {}, {}
2130
2128
2131
2129
2132
2130
def get_isinstance_type (node : Node , type_map : Dict [Node , Type ]) -> Type :
0 commit comments