-
Notifications
You must be signed in to change notification settings - Fork 1.4k
[ty] Add assignability between class with __call__
class attribute and callable
#18167
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
Conversation
|
ad0cb3d
to
41b3e51
Compare
Thank you for the PR! I spent a bit of time looking at this today. It's a bit of a can of worms, because the behavior (partially) implemented in this PR is that I say "partially" above because we really need to be consistent about the signature we use for an object when deciding its subtyping/assignability vs Callable, and the signature we use when actually type-checking a call of the object. This means that I don't think we should implement this change in these subtyping/assignability arms at all; whatever we do here should be implemented at a deeper layer, in I've pushed a few initial changes here: added subtyping tests, corrected the assignability tests for the behavior I think we want, and cleaned up some pre-existing mistakes in the subtyping/assignability arms for instance vs callable. For one thing, we need to look up dunder methods (such as The tests are currently failing because I'm still considering how to best make them pass. I tried locally implementing always assuming callable types are method descriptors, because this seems to be what mypy and pyright do. It's not hard to implement this as a special case in I might still do this, but it occurred to me that we could also wait for #18242 to land, so that we can explicitly have both method-descriptor and non-method-descriptor callable types. Then we'd have the option of being even smarter here: for a callable-annotated class attribute where we see an RHS assigned to it, we could check whether that RHS is a method descriptor type or not; if it is, we could use a method-descriptor callable type for the declaration, otherwise we could use a non-method-descriptor callable type. TODO:
|
On second thought, I think it's pretty weird to interpret the exact same annotated type differently depending on the RHS of the assignment. So maybe we shouldn't do this. Probably tomorrow I'll revisit the "always assume callable types are method descriptors" option and see how that works out. |
As a first step, I extracted the simpler fixes we definitely need here into #18260 (with you credited as co author) so we can consider here what other changes we might want to make in terms of bound-method-descriptor assumptions. |
I think #18260 fixes the core issue this PR was intended to fix. E.g. the pytorch case doesn't care about bound-method descriptor or not, because it uses a |
Thanks for that amazing analysis! I definitely missed cases for functions that's not bounded methods and didn't realize how deep this rabbit hole is until I read the follow up issue and discussion. I learnt a lot, thank you so much. I will try to contribute to some more well-defined issues then. |
Summary
There are some classes that's callable but defined via
__call__
as attributes, e.g. pytorchModule
.This PR should add support for checking assignability for such classes. Not sure if we should do subtyping though.
Test Plan
I added tests into
is_assignable_to.md
. Referred to thispyright
PR and thismypy
PR.I tested multiple possible forms of having
__call__
attribute, and check if ty accepts & rejects callable types correctly.Things to Notice
A
in the test:pyright
seems to reject__call__
as instance variables when it comes to callable check, whilemypy
appears to accept it. The type ofa.__call__
checked byty
isUnknown
, though I personally think it should beUnknown | A.method1
with theself
parameter already filled in. I'm open to discussing which approach would be best here.C
in the test.pyright
doesn't allow assigningc
into callable withself
filled. However it does allow assigningb
into callable (no explicit type hint on__call__
). linkThanks for your time and feedback!