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 (
45
47
from mypy .meet import meet_simple , meet_simple_away , nearest_builtin_ancestor , is_overlapping_types
46
48
47
49
48
- # Kinds of isinstance checks.
49
- ISINSTANCE_OVERLAPPING = 0
50
- ISINSTANCE_ALWAYS_TRUE = 1
51
- ISINSTANCE_ALWAYS_FALSE = 2
52
-
53
50
T = TypeVar ('T' )
54
51
55
52
@@ -1433,16 +1430,21 @@ def visit_if_stmt(self, s: IfStmt) -> Type:
1433
1430
for e , b in zip (s .expr , s .body ):
1434
1431
t = self .accept (e )
1435
1432
self .check_not_void (t , e )
1436
- var , type , elsetype , kind = find_isinstance_check (e , self .type_map ,
1437
- self .typing_mode_weak ())
1438
- if kind == ISINSTANCE_ALWAYS_FALSE :
1433
+ if_map , else_map = find_isinstance_check (
1434
+ e , self .type_map ,
1435
+ self .typing_mode_weak ()
1436
+ )
1437
+ if if_map is None :
1438
+ # The condition is always false
1439
1439
# XXX should issue a warning?
1440
1440
pass
1441
1441
else :
1442
1442
# Only type check body if the if condition can be true.
1443
1443
self .binder .push_frame ()
1444
- if var :
1445
- self .binder .push (var , type )
1444
+ if if_map :
1445
+ for var , type in if_map .items ():
1446
+ self .binder .push (var , type )
1447
+
1446
1448
self .accept (b )
1447
1449
_ , frame = self .binder .pop_frame ()
1448
1450
if not self .breaking_out :
@@ -1451,9 +1453,10 @@ def visit_if_stmt(self, s: IfStmt) -> Type:
1451
1453
1452
1454
self .breaking_out = False
1453
1455
1454
- if var :
1455
- self .binder .push (var , elsetype )
1456
- if kind == ISINSTANCE_ALWAYS_TRUE :
1456
+ if else_map :
1457
+ for var , type in else_map .items ():
1458
+ self .binder .push (var , type )
1459
+ if else_map is None :
1457
1460
# The condition is always true => remaining elif/else blocks
1458
1461
# can never be reached.
1459
1462
@@ -2046,24 +2049,17 @@ def map_type_from_supertype(typ: Type, sub_info: TypeInfo,
2046
2049
2047
2050
def find_isinstance_check (node : Node ,
2048
2051
type_map : Dict [Node , Type ],
2049
- weak : bool = False ) -> Tuple [Node , Type , Type , int ]:
2050
- """Check if node is an isinstance(variable, type) check.
2051
-
2052
- If successful, return tuple (variable, target-type, else-type,
2053
- kind); otherwise, return (None, AnyType, AnyType, -1).
2052
+ weak : bool = False ) \
2053
+ -> Tuple [Optional [Dict [Node , Type ]], Optional [Dict [Node , Type ]]]:
2054
+ """Find any isinstance checks (within a chain of ands)".
2054
2055
2055
- When successful, the kind takes one of these values:
2056
+ Return value is a map of variables to their types if the condition
2057
+ is true and a map of variables to their types if the condition is false.
2056
2058
2057
- ISINSTANCE_OVERLAPPING: The type of variable and the target type are
2058
- partially overlapping => the test result can be True or False.
2059
- ISINSTANCE_ALWAYS_TRUE: The target type at least as general as the
2060
- variable type => the test is always True.
2061
- ISINSTANCE_ALWAYS_FALSE: The target type and the variable type are not
2062
- overlapping => the test is always False.
2059
+ If either of the values in the tuple is None, then that particular
2060
+ branch can never occur.
2063
2061
2064
- If it is an isinstance check, but we don't understand the argument
2065
- type, then in weak mode it is treated as Any and in non-weak mode
2066
- it is not treated as an isinstance.
2062
+ Guaranteed to not return None, None. (But may return {}, {})
2067
2063
"""
2068
2064
if isinstance (node , CallExpr ):
2069
2065
if refers_to_fullname (node .callee , 'builtins.isinstance' ):
@@ -2072,46 +2068,45 @@ def find_isinstance_check(node: Node,
2072
2068
vartype = type_map [expr ]
2073
2069
type = get_isinstance_type (node .args [1 ], type_map )
2074
2070
if type :
2075
- kind = ISINSTANCE_OVERLAPPING
2076
2071
elsetype = vartype
2077
2072
if vartype :
2078
2073
if is_proper_subtype (vartype , type ):
2079
- kind = ISINSTANCE_ALWAYS_TRUE
2080
2074
elsetype = None
2075
+ return {expr : type }, None
2081
2076
elif not is_overlapping_types (vartype , type ):
2082
- kind = ISINSTANCE_ALWAYS_FALSE
2077
+ return None , { expr : elsetype }
2083
2078
else :
2084
2079
elsetype = restrict_subtype_away (vartype , type )
2085
- return expr , type , elsetype , kind
2080
+ return { expr : type }, { expr : elsetype }
2086
2081
else :
2087
2082
# An isinstance check, but we don't understand the type
2088
2083
if weak :
2089
- return expr , AnyType (), vartype , ISINSTANCE_OVERLAPPING
2084
+ return { expr : AnyType ()}, { expr : vartype }
2090
2085
elif isinstance (node , OpExpr ) and node .op == 'and' :
2091
- # XXX We should extend this to support two isinstance checks in the same
2092
- # expression
2093
- (var , type , elsetype , kind ) = find_isinstance_check (node .left , type_map , weak )
2094
- if var is None :
2095
- (var , type , elsetype , kind ) = find_isinstance_check (node .left , type_map , weak )
2096
- if var :
2097
- if kind == ISINSTANCE_ALWAYS_TRUE :
2098
- kind = ISINSTANCE_OVERLAPPING
2099
- return (var , type , AnyType (), kind )
2086
+ left_if_vars , right_else_vars = find_isinstance_check (
2087
+ node .left ,
2088
+ type_map ,
2089
+ weak ,
2090
+ )
2091
+
2092
+ right_if_vars , right_else_vars = find_isinstance_check (
2093
+ node .right ,
2094
+ type_map ,
2095
+ weak ,
2096
+ )
2097
+ if left_if_vars :
2098
+ left_if_vars .update (right_if_vars )
2099
+ else :
2100
+ left_if_vars = right_if_vars
2101
+
2102
+ # Make no claim about the types in else
2103
+ return left_if_vars , {}
2100
2104
elif isinstance (node , UnaryExpr ) and node .op == 'not' :
2101
- ( var , type , elsetype , kind ) = find_isinstance_check (node .expr , type_map , weak )
2102
- return ( var , elsetype , type , invert_isinstance_kind ( kind ))
2105
+ left , right = find_isinstance_check (node .expr , type_map , weak )
2106
+ return right , left
2103
2107
2104
2108
# Not a supported isinstance check
2105
- return None , AnyType (), AnyType (), - 1
2106
-
2107
-
2108
- def invert_isinstance_kind (kind : int ) -> int :
2109
- if kind == ISINSTANCE_ALWAYS_TRUE :
2110
- return ISINSTANCE_ALWAYS_FALSE
2111
- elif kind == ISINSTANCE_ALWAYS_FALSE :
2112
- return ISINSTANCE_ALWAYS_TRUE
2113
- else :
2114
- return kind
2109
+ return {}, {}
2115
2110
2116
2111
2117
2112
def get_isinstance_type (node : Node , type_map : Dict [Node , Type ]) -> Type :
0 commit comments