-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Fix TypeGuard with call on temporary object #19577
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Fix TypeGuard with call on temporary object #19577
Conversation
…python#19575 Add a regression test to check-typeguard.test for TypeGuard narrowing on temporary objects using __call__. This documents the current bug (see python#19575).
This comment has been minimized.
This comment has been minimized.
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅ |
I'm quite confused both by the changes in this PR and by the original code. In my mind, typeguards here should be an algorithm of:
Without any of this special casing for |
Thanks for the PR! What do you think of the following additional diff. This takes into account A5rocks' suggestion, avoids exceptions for control flow (which is slow in mypyc), and makes a change to look at CallableType results as well. Looks like we can't quite get rid of the RefExpr case without some tests involving overloads and generics failing... diff --git a/mypy/checker.py b/mypy/checker.py
index afe7c7740..1460e844a 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -6183,23 +6183,20 @@ class TypeChecker(NodeVisitor[None], TypeCheckerSharedApi):
attr = try_getting_str_literals(node.args[1], self.lookup_type(node.args[1]))
if literal(expr) == LITERAL_TYPE and attr and len(attr) == 1:
return self.hasattr_type_maps(expr, self.lookup_type(expr), attr[0])
- elif isinstance(node.callee, (RefExpr, CallExpr)):
- # We support both named callables (RefExpr) and temporaries (CallExpr).
- # For temporaries (e.g., E()(x)), we extract type_is/type_guard from the __call__ method.
- # For named callables (e.g., is_int(x)), we extract type_is/type_guard directly from the RefExpr.
+ else:
type_is, type_guard = None, None
- try:
- called_type = get_proper_type(self.lookup_type(node.callee))
- except KeyError:
- called_type = None
- # TODO: there are some more cases in check_call() to handle.
- # If the callee is an instance, try to extract TypeGuard/TypeIs from its __call__ method.
- if called_type and isinstance(called_type, Instance):
- call = find_member("__call__", called_type, called_type, is_operator=True)
- if call is not None:
- called_type = get_proper_type(call)
- if isinstance(called_type, CallableType):
- type_is, type_guard = called_type.type_is, called_type.type_guard
+ called_type = self.lookup_type_or_none(node.callee)
+ if called_type is not None:
+ called_type = get_proper_type(called_type)
+ # TODO: there are some more cases in check_call() to handle.
+ # If the callee is an instance, try to extract TypeGuard/TypeIs from its __call__ method.
+ if isinstance(called_type, Instance):
+ call = find_member("__call__", called_type, called_type, is_operator=True)
+ if call is not None:
+ called_type = get_proper_type(call)
+ if isinstance(called_type, CallableType):
+ type_is, type_guard = called_type.type_is, called_type.type_guard
+
# If the callee is a RefExpr, extract TypeGuard/TypeIs directly.
if isinstance(node.callee, RefExpr):
type_is, type_guard = node.callee.type_is, node.callee.type_guard |
Fixes #19575 by adding support for TypeGaurd/TypeIs when they are used on methods off of classes which were not saved to a variable.
Solution adapted from copilot answer here and then refined: saulshanabrook#1
X[int](y)