@@ -6218,21 +6218,26 @@ def find_isinstance_check_helper(
6218
6218
attr = try_getting_str_literals (node .args [1 ], self .lookup_type (node .args [1 ]))
6219
6219
if literal (expr ) == LITERAL_TYPE and attr and len (attr ) == 1 :
6220
6220
return self .hasattr_type_maps (expr , self .lookup_type (expr ), attr [0 ])
6221
- elif isinstance (node .callee , RefExpr ):
6222
- if node .callee .type_guard is not None or node .callee .type_is is not None :
6221
+ else :
6222
+ type_is , type_guard = None , None
6223
+ called_type = self .lookup_type_or_none (node .callee )
6224
+ if called_type is not None :
6225
+ called_type = get_proper_type (called_type )
6226
+ # TODO: there are some more cases in check_call() to handle.
6227
+ # If the callee is an instance, try to extract TypeGuard/TypeIs from its __call__ method.
6228
+ if isinstance (called_type , Instance ):
6229
+ call = find_member ("__call__" , called_type , called_type , is_operator = True )
6230
+ if call is not None :
6231
+ called_type = get_proper_type (call )
6232
+ if isinstance (called_type , CallableType ):
6233
+ type_is , type_guard = called_type .type_is , called_type .type_guard
6234
+
6235
+ # If the callee is a RefExpr, extract TypeGuard/TypeIs directly.
6236
+ if isinstance (node .callee , RefExpr ):
6237
+ type_is , type_guard = node .callee .type_is , node .callee .type_guard
6238
+ if type_guard is not None or type_is is not None :
6223
6239
# TODO: Follow *args, **kwargs
6224
6240
if node .arg_kinds [0 ] != nodes .ARG_POS :
6225
- # the first argument might be used as a kwarg
6226
- called_type = get_proper_type (self .lookup_type (node .callee ))
6227
-
6228
- # TODO: there are some more cases in check_call() to handle.
6229
- if isinstance (called_type , Instance ):
6230
- call = find_member (
6231
- "__call__" , called_type , called_type , is_operator = True
6232
- )
6233
- if call is not None :
6234
- called_type = get_proper_type (call )
6235
-
6236
6241
# *assuming* the overloaded function is correct, there's a couple cases:
6237
6242
# 1) The first argument has different names, but is pos-only. We don't
6238
6243
# care about this case, the argument must be passed positionally.
@@ -6245,9 +6250,7 @@ def find_isinstance_check_helper(
6245
6250
# we want the idx-th variable to be narrowed
6246
6251
expr = collapse_walrus (node .args [idx ])
6247
6252
else :
6248
- kind = (
6249
- "guard" if node .callee .type_guard is not None else "narrower"
6250
- )
6253
+ kind = "guard" if type_guard is not None else "narrower"
6251
6254
self .fail (
6252
6255
message_registry .TYPE_GUARD_POS_ARG_REQUIRED .format (kind ), node
6253
6256
)
@@ -6258,15 +6261,15 @@ def find_isinstance_check_helper(
6258
6261
# considered "always right" (i.e. even if the types are not overlapping).
6259
6262
# Also note that a care must be taken to unwrap this back at read places
6260
6263
# where we use this to narrow down declared type.
6261
- if node . callee . type_guard is not None :
6262
- return {expr : TypeGuardedType (node . callee . type_guard )}, {}
6264
+ if type_guard is not None :
6265
+ return {expr : TypeGuardedType (type_guard )}, {}
6263
6266
else :
6264
- assert node . callee . type_is is not None
6267
+ assert type_is is not None
6265
6268
return conditional_types_to_typemaps (
6266
6269
expr ,
6267
6270
* self .conditional_types_with_intersection (
6268
6271
self .lookup_type (expr ),
6269
- [TypeRange (node . callee . type_is , is_upper_bound = False )],
6272
+ [TypeRange (type_is , is_upper_bound = False )],
6270
6273
expr ,
6271
6274
consider_runtime_isinstance = False ,
6272
6275
),
0 commit comments