@@ -2809,7 +2809,7 @@ def function_type(self, func: FuncBase) -> FunctionLike:
2809
2809
return function_type (func , self .named_type ('builtins.function' ))
2810
2810
2811
2811
def find_isinstance_check (self , n : Expression ) -> 'Tuple[TypeMap, TypeMap]' :
2812
- return find_isinstance_check (n , self .type_map )
2812
+ return find_isinstance_check (n , self .type_map , self )
2813
2813
2814
2814
def push_type_map (self , type_map : 'TypeMap' ) -> None :
2815
2815
if type_map is None :
@@ -2883,7 +2883,45 @@ def conditional_type_map(expr: Expression,
2883
2883
return {}, {}
2884
2884
2885
2885
2886
- def partition_by_callable (type : Type ) -> Tuple [List [Type ], List [Type ]]:
2886
+ def intersect_instance_callable (type : Instance , callable_type : CallableType ) -> Type :
2887
+ """Creates a fake type that represents the intersection of an
2888
+ Instance and a CallableType.
2889
+
2890
+ It operates by creating a bare-minimum dummy TypeInfo that
2891
+ subclasses type and adds a __call__ method matching callable_type."""
2892
+
2893
+ # Build the fake ClassDef and TypeInfo together.
2894
+ # The ClassDef is full of lies and doesn't actually contain a body.
2895
+ cdef = ClassDef ("<callable subtype of {}>" .format (type .type .name ()), Block ([]))
2896
+ info = TypeInfo (SymbolTable (), cdef , '<dummy>' )
2897
+ cdef .info = info
2898
+ info .bases = [type ]
2899
+ info .calculate_mro ()
2900
+
2901
+ # Build up a fake FuncDef so we can populate the symbol table.
2902
+ func_def = FuncDef ('__call__' , [], Block ([]), callable_type )
2903
+ func_def .info = info
2904
+ info .names ['__call__' ] = SymbolTableNode (MDEF , func_def , callable_type )
2905
+
2906
+ return Instance (info , [])
2907
+
2908
+
2909
+ def make_fake_callable (type : Instance , typechecker : TypeChecker ) -> Type :
2910
+ """Produce a new type that makes type Callable with a generic callable type."""
2911
+
2912
+ fallback = typechecker .named_type ('builtins.function' )
2913
+ callable_type = CallableType ([AnyType (TypeOfAny .explicit ),
2914
+ AnyType (TypeOfAny .explicit )],
2915
+ [nodes .ARG_STAR , nodes .ARG_STAR2 ],
2916
+ [None , None ],
2917
+ ret_type = AnyType (TypeOfAny .explicit ),
2918
+ fallback = fallback ,
2919
+ is_ellipsis_args = True )
2920
+
2921
+ return intersect_instance_callable (type , callable_type )
2922
+
2923
+
2924
+ def partition_by_callable (type : Type , typechecker : TypeChecker ) -> Tuple [List [Type ], List [Type ]]:
2887
2925
"""Takes in a type and partitions that type into callable subtypes and
2888
2926
uncallable subtypes.
2889
2927
@@ -2904,29 +2942,43 @@ def partition_by_callable(type: Type) -> Tuple[List[Type], List[Type]]:
2904
2942
callables = []
2905
2943
uncallables = []
2906
2944
for subtype in type .relevant_items ():
2907
- subcallables , subuncallables = partition_by_callable (subtype )
2945
+ subcallables , subuncallables = partition_by_callable (subtype , typechecker )
2908
2946
callables .extend (subcallables )
2909
2947
uncallables .extend (subuncallables )
2910
2948
return callables , uncallables
2911
2949
2912
2950
if isinstance (type , TypeVarType ):
2913
- return partition_by_callable (type .erase_to_union_or_bound ())
2951
+ # We could do better probably?
2952
+ # Refine the the type variable's bound as our type in the case that
2953
+ # callable() is true. This unfortuantely loses the information that
2954
+ # the type is a type variable in that branch.
2955
+ # This matches what is done for isinstance, but it may be possible to
2956
+ # do better.
2957
+ # If it is possible for the false branch to execute, return the original
2958
+ # type to avoid losing type information.
2959
+ callables , uncallables = partition_by_callable (type .erase_to_union_or_bound (), typechecker )
2960
+ uncallables = [type ] if len (uncallables ) else []
2961
+ return callables , uncallables
2914
2962
2915
2963
if isinstance (type , Instance ):
2916
2964
method = type .type .get_method ('__call__' )
2917
2965
if method and method .type :
2918
- callables , uncallables = partition_by_callable (method .type )
2966
+ callables , uncallables = partition_by_callable (method .type , typechecker )
2919
2967
if len (callables ) and not len (uncallables ):
2920
2968
# Only consider the type callable if its __call__ method is
2921
2969
# definitely callable.
2922
2970
return [type ], []
2923
- return [], [type ]
2924
2971
2925
- return [], [type ]
2972
+ ret = make_fake_callable (type , typechecker )
2973
+ return [ret ], [type ]
2974
+
2975
+ # We don't know how properly make the type callable.
2976
+ return [type ], [type ]
2926
2977
2927
2978
2928
2979
def conditional_callable_type_map (expr : Expression ,
2929
2980
current_type : Optional [Type ],
2981
+ typechecker : TypeChecker ,
2930
2982
) -> Tuple [TypeMap , TypeMap ]:
2931
2983
"""Takes in an expression and the current type of the expression.
2932
2984
@@ -2940,7 +2992,7 @@ def conditional_callable_type_map(expr: Expression,
2940
2992
if isinstance (current_type , AnyType ):
2941
2993
return {}, {}
2942
2994
2943
- callables , uncallables = partition_by_callable (current_type )
2995
+ callables , uncallables = partition_by_callable (current_type , typechecker )
2944
2996
2945
2997
if len (callables ) and len (uncallables ):
2946
2998
callable_map = {expr : UnionType .make_union (callables )} if len (callables ) else None
@@ -3069,6 +3121,7 @@ def convert_to_typetype(type_map: TypeMap) -> TypeMap:
3069
3121
3070
3122
def find_isinstance_check (node : Expression ,
3071
3123
type_map : Dict [Expression , Type ],
3124
+ typechecker : TypeChecker ,
3072
3125
) -> Tuple [TypeMap , TypeMap ]:
3073
3126
"""Find any isinstance checks (within a chain of ands). Includes
3074
3127
implicit and explicit checks for None and calls to callable.
@@ -3122,7 +3175,7 @@ def find_isinstance_check(node: Expression,
3122
3175
expr = node .args [0 ]
3123
3176
if literal (expr ) == LITERAL_TYPE :
3124
3177
vartype = type_map [expr ]
3125
- return conditional_callable_type_map (expr , vartype )
3178
+ return conditional_callable_type_map (expr , vartype , typechecker )
3126
3179
elif isinstance (node , ComparisonExpr ) and experiments .STRICT_OPTIONAL :
3127
3180
# Check for `x is None` and `x is not None`.
3128
3181
is_not = node .operators == ['is not' ]
@@ -3182,23 +3235,23 @@ def find_isinstance_check(node: Expression,
3182
3235
else_map = {ref : else_type } if not isinstance (else_type , UninhabitedType ) else None
3183
3236
return if_map , else_map
3184
3237
elif isinstance (node , OpExpr ) and node .op == 'and' :
3185
- left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map )
3186
- right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map )
3238
+ left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map , typechecker )
3239
+ right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map , typechecker )
3187
3240
3188
3241
# (e1 and e2) is true if both e1 and e2 are true,
3189
3242
# and false if at least one of e1 and e2 is false.
3190
3243
return (and_conditional_maps (left_if_vars , right_if_vars ),
3191
3244
or_conditional_maps (left_else_vars , right_else_vars ))
3192
3245
elif isinstance (node , OpExpr ) and node .op == 'or' :
3193
- left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map )
3194
- right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map )
3246
+ left_if_vars , left_else_vars = find_isinstance_check (node .left , type_map , typechecker )
3247
+ right_if_vars , right_else_vars = find_isinstance_check (node .right , type_map , typechecker )
3195
3248
3196
3249
# (e1 or e2) is true if at least one of e1 or e2 is true,
3197
3250
# and false if both e1 and e2 are false.
3198
3251
return (or_conditional_maps (left_if_vars , right_if_vars ),
3199
3252
and_conditional_maps (left_else_vars , right_else_vars ))
3200
3253
elif isinstance (node , UnaryExpr ) and node .op == 'not' :
3201
- left , right = find_isinstance_check (node .expr , type_map )
3254
+ left , right = find_isinstance_check (node .expr , type_map , typechecker )
3202
3255
return right , left
3203
3256
3204
3257
# Not a supported isinstance check
0 commit comments