Skip to content

Commit 48ed840

Browse files
1 parent 0f6606f commit 48ed840

File tree

1 file changed

+65
-0
lines changed

1 file changed

+65
-0
lines changed

mypy/checker.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6156,6 +6156,9 @@ def find_isinstance_check_helper(
61566156

61576157
if isinstance(node, CallExpr) and len(node.args) != 0:
61586158
expr = collapse_walrus(node.args[0])
6159+
print("[TypeGuard Debug] --- find_isinstance_check_helper ---")
6160+
print(f"[TypeGuard Debug] {node=}")
6161+
print(f"[TypeGuard Debug] {node.callee=}")
61596162
if refers_to_fullname(node.callee, "builtins.isinstance"):
61606163
if len(node.args) != 2: # the error will be reported elsewhere
61616164
return {}, {}
@@ -6236,6 +6239,68 @@ def find_isinstance_check_helper(
62366239
consider_runtime_isinstance=False,
62376240
),
62386241
)
6242+
elif isinstance(node.callee, CallExpr):
6243+
# Handle case where callee is a call expression like E()(x)
6244+
# where E() returns an object with __call__ method that has TypeGuard
6245+
if len(node.args) == 0:
6246+
return {}, {}
6247+
callee_type = get_proper_type(self.lookup_type(node.callee))
6248+
if isinstance(callee_type, Instance):
6249+
call_member = find_member(
6250+
"__call__", callee_type, callee_type, is_operator=True
6251+
)
6252+
if call_member is not None:
6253+
call_type = get_proper_type(call_member)
6254+
# Check if the __call__ method has type_guard or type_is
6255+
if isinstance(call_type, CallableType) and (
6256+
call_type.type_guard is not None or call_type.type_is is not None
6257+
):
6258+
# Handle keyword arguments similar to RefExpr case
6259+
expr = collapse_walrus(node.args[0]) # Default to first positional arg
6260+
if node.arg_kinds[0] != nodes.ARG_POS:
6261+
# the first argument might be used as a kwarg
6262+
if isinstance(call_type, (CallableType, Overloaded)):
6263+
if isinstance(call_type, Overloaded):
6264+
# Use first overload for argument name lookup
6265+
first_callable = call_type.items[0]
6266+
else:
6267+
first_callable = call_type
6268+
6269+
if first_callable.arg_names:
6270+
name = first_callable.arg_names[0]
6271+
if name in node.arg_names:
6272+
idx = node.arg_names.index(name)
6273+
# we want the idx-th variable to be narrowed
6274+
expr = collapse_walrus(node.args[idx])
6275+
else:
6276+
kind = (
6277+
"guard"
6278+
if call_type.type_guard is not None
6279+
else "narrower"
6280+
)
6281+
self.fail(
6282+
message_registry.TYPE_GUARD_POS_ARG_REQUIRED.format(
6283+
kind
6284+
),
6285+
node,
6286+
)
6287+
return {}, {}
6288+
6289+
if literal(expr) == LITERAL_TYPE:
6290+
# Apply the same TypeGuard narrowing logic
6291+
if call_type.type_guard is not None:
6292+
return {expr: TypeGuardedType(call_type.type_guard)}, {}
6293+
else:
6294+
assert call_type.type_is is not None
6295+
return conditional_types_to_typemaps(
6296+
expr,
6297+
*self.conditional_types_with_intersection(
6298+
self.lookup_type(expr),
6299+
[TypeRange(call_type.type_is, is_upper_bound=False)],
6300+
expr,
6301+
consider_runtime_isinstance=False,
6302+
),
6303+
)
62396304
elif isinstance(node, ComparisonExpr):
62406305
return self.comparison_type_narrowing_helper(node)
62416306
elif isinstance(node, AssignmentExpr):

0 commit comments

Comments
 (0)