@@ -2823,7 +2823,7 @@ def function_type(self, func: FuncBase) -> FunctionLike:
2823
2823
return function_type (func , self .named_type ('builtins.function' ))
2824
2824
2825
2825
def find_isinstance_check (self , n : Expression ) -> 'Tuple[TypeMap, TypeMap]' :
2826
- return find_isinstance_check (n , self .type_map )
2826
+ return find_isinstance_check (n , self .type_map , self )
2827
2827
2828
2828
def push_type_map (self , type_map : 'TypeMap' ) -> None :
2829
2829
if type_map is None :
@@ -2897,7 +2897,45 @@ def conditional_type_map(expr: Expression,
2897
2897
return {}, {}
2898
2898
2899
2899
2900
- def partition_by_callable (type : Type ) -> Tuple [List [Type ], List [Type ]]:
2900
+ def intersect_instance_callable (type : Instance , callable_type : CallableType ) -> Type :
2901
+ """Creates a fake type that represents the intersection of an
2902
+ Instance and a CallableType.
2903
+
2904
+ It operates by creating a bare-minimum dummy TypeInfo that
2905
+ subclasses type and adds a __call__ method matching callable_type."""
2906
+
2907
+ # Build the fake ClassDef and TypeInfo together.
2908
+ # The ClassDef is full of lies and doesn't actually contain a body.
2909
+ cdef = ClassDef ("<callable subtype of {}>" .format (type .type .name ()), Block ([]))
2910
+ info = TypeInfo (SymbolTable (), cdef , '<dummy>' )
2911
+ cdef .info = info
2912
+ info .bases = [type ]
2913
+ info .calculate_mro ()
2914
+
2915
+ # Build up a fake FuncDef so we can populate the symbol table.
2916
+ func_def = FuncDef ('__call__' , [], Block ([]), callable_type )
2917
+ func_def .info = info
2918
+ info .names ['__call__' ] = SymbolTableNode (MDEF , func_def , callable_type )
2919
+
2920
+ return Instance (info , [])
2921
+
2922
+
2923
+ def make_fake_callable (type : Instance , typechecker : TypeChecker ) -> Type :
2924
+ """Produce a new type that makes type Callable with a generic callable type."""
2925
+
2926
+ fallback = typechecker .named_type ('builtins.function' )
2927
+ callable_type = CallableType ([AnyType (TypeOfAny .explicit ),
2928
+ AnyType (TypeOfAny .explicit )],
2929
+ [nodes .ARG_STAR , nodes .ARG_STAR2 ],
2930
+ [None , None ],
2931
+ ret_type = AnyType (TypeOfAny .explicit ),
2932
+ fallback = fallback ,
2933
+ is_ellipsis_args = True )
2934
+
2935
+ return intersect_instance_callable (type , callable_type )
2936
+
2937
+
2938
+ def partition_by_callable (type : Type , typechecker : TypeChecker ) -> Tuple [List [Type ], List [Type ]]:
2901
2939
"""Takes in a type and partitions that type into callable subtypes and
2902
2940
uncallable subtypes.
2903
2941
@@ -2918,29 +2956,43 @@ def partition_by_callable(type: Type) -> Tuple[List[Type], List[Type]]:
2918
2956
callables = []
2919
2957
uncallables = []
2920
2958
for subtype in type .relevant_items ():
2921
- subcallables , subuncallables = partition_by_callable (subtype )
2959
+ subcallables , subuncallables = partition_by_callable (subtype , typechecker )
2922
2960
callables .extend (subcallables )
2923
2961
uncallables .extend (subuncallables )
2924
2962
return callables , uncallables
2925
2963
2926
2964
if isinstance (type , TypeVarType ):
2927
- return partition_by_callable (type .erase_to_union_or_bound ())
2965
+ # We could do better probably?
2966
+ # Refine the the type variable's bound as our type in the case that
2967
+ # callable() is true. This unfortuantely loses the information that
2968
+ # the type is a type variable in that branch.
2969
+ # This matches what is done for isinstance, but it may be possible to
2970
+ # do better.
2971
+ # If it is possible for the false branch to execute, return the original
2972
+ # type to avoid losing type information.
2973
+ callables , uncallables = partition_by_callable (type .erase_to_union_or_bound (), typechecker )
2974
+ uncallables = [type ] if len (uncallables ) else []
2975
+ return callables , uncallables
2928
2976
2929
2977
if isinstance (type , Instance ):
2930
2978
method = type .type .get_method ('__call__' )
2931
2979
if method and method .type :
2932
- callables , uncallables = partition_by_callable (method .type )
2980
+ callables , uncallables = partition_by_callable (method .type , typechecker )
2933
2981
if len (callables ) and not len (uncallables ):
2934
2982
# Only consider the type callable if its __call__ method is
2935
2983
# definitely callable.
2936
2984
return [type ], []
2937
- return [], [type ]
2938
2985
2939
- return [], [type ]
2986
+ ret = make_fake_callable (type , typechecker )
2987
+ return [ret ], [type ]
2988
+
2989
+ # We don't know how properly make the type callable.
2990
+ return [type ], [type ]
2940
2991
2941
2992
2942
2993
def conditional_callable_type_map (expr : Expression ,
2943
2994
current_type : Optional [Type ],
2995
+ typechecker : TypeChecker ,
2944
2996
) -> Tuple [TypeMap , TypeMap ]:
2945
2997
"""Takes in an expression and the current type of the expression.
2946
2998
@@ -2954,7 +3006,7 @@ def conditional_callable_type_map(expr: Expression,
2954
3006
if isinstance (current_type , AnyType ):
2955
3007
return {}, {}
2956
3008
2957
- callables , uncallables = partition_by_callable (current_type )
3009
+ callables , uncallables = partition_by_callable (current_type , typechecker )
2958
3010
2959
3011
if len (callables ) and len (uncallables ):
2960
3012
callable_map = {expr : UnionType .make_union (callables )} if len (callables ) else None
@@ -3083,6 +3135,7 @@ def convert_to_typetype(type_map: TypeMap) -> TypeMap:
3083
3135
3084
3136
def find_isinstance_check (node : Expression ,
3085
3137
type_map : Dict [Expression , Type ],
3138
+ typechecker : TypeChecker ,
3086
3139
) -> Tuple [TypeMap , TypeMap ]:
3087
3140
"""Find any isinstance checks (within a chain of ands). Includes
3088
3141
implicit and explicit checks for None and calls to callable.
@@ -3136,7 +3189,7 @@ def find_isinstance_check(node: Expression,
3136
3189
expr = node .args [0 ]
3137
3190
if literal (expr ) == LITERAL_TYPE :
3138
3191
vartype = type_map [expr ]
3139
- return conditional_callable_type_map (expr , vartype )
3192
+ return conditional_callable_type_map (expr , vartype , typechecker )
3140
3193
elif isinstance (node , ComparisonExpr ) and experiments .STRICT_OPTIONAL :
3141
3194
# Check for `x is None` and `x is not None`.
3142
3195
is_not = node .operators == ['is not' ]
@@ -3196,23 +3249,23 @@ def find_isinstance_check(node: Expression,
3196
3249
else_map = {ref : else_type } if not isinstance (else_type , UninhabitedType ) else None
3197
3250
return if_map , else_map
3198
3251
elif isinstance (node , OpExpr ) and node .op == 'and' :
3199
- left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map )
3200
- right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map )
3252
+ left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map , typechecker )
3253
+ right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map , typechecker )
3201
3254
3202
3255
# (e1 and e2) is true if both e1 and e2 are true,
3203
3256
# and false if at least one of e1 and e2 is false.
3204
3257
return (and_conditional_maps (left_if_vars , right_if_vars ),
3205
3258
or_conditional_maps (left_else_vars , right_else_vars ))
3206
3259
elif isinstance (node , OpExpr ) and node .op == 'or' :
3207
- left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map )
3208
- right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map )
3260
+ left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map , typechecker )
3261
+ right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map , typechecker )
3209
3262
3210
3263
# (e1 or e2) is true if at least one of e1 or e2 is true,
3211
3264
# and false if both e1 and e2 are false.
3212
3265
return (or_conditional_maps (left_if_vars , right_if_vars ),
3213
3266
and_conditional_maps (left_else_vars , right_else_vars ))
3214
3267
elif isinstance (node , UnaryExpr ) and node .op == 'not' :
3215
- left , right = find_isinstance_check (node .expr , type_map )
3268
+ left , right = find_isinstance_check (node .expr , type_map , typechecker )
3216
3269
return right , left
3217
3270
3218
3271
# Not a supported isinstance check
0 commit comments