-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[ty] Support frozen dataclasses #17974
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
0fe7255 to
6b64036
Compare
|
@sharkdp ready for some feedback - let me know if this is headed in the right direction |
|
b3947b3 to
3b1c145
Compare
carljm
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code and ecosystem results look pretty good to me. A couple comments. Thanks for the PR!
20a58fc to
09a0af5
Compare
carljm
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the updates! On looking at the new version more closely, I think we still need to adjust the priority of this error relative to other ones. Sorry I didn't comment on this more thoroughly the first time around! More detailed comment inline.
| } | ||
| } | ||
| }; | ||
| if (assignable_if_rw && ro) && emit_diagnostics { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change to prioritize "unresolved-attribute" over the read-only error looks great -- but I think I have the reverse issue now. If the assigned type is not assignable to the attribute type, and the attribute is read-only, which of those do you think should take priority? I think it would actually be fine to emit both, but if we emit only one of those two errors in that scenario, I think it should be the read-only error. It doesn't make sense to send the user on a mission to fix the assigned type, when we'll only then tell them the attribute is read-only; we should tell them it's read-only right upfront. WDYT?
The read-only error should also take precedence over the descriptor __set__ handling, because that's what happens at runtime -- even if the attribute of a frozen dataclass is a descriptor, its __set__ method is not called, you just get the read-only error.
I think this will require integrating the read-only error into the block of cases above (I think it should be the first thing we check in the block starting on 2957 for "there is an attribute of this name") rather than trying to handle it externally. I think we can also move looking up whether this is a frozen dataclass into that arm, because I don't think we need it otherwise -- both the ClassVar error and the Unbound handling don't need to check for a frozen dataclass, I don't think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't make sense to send the user on a mission
yes this definitely makes sense from a ux perspective, good call
if the attribute of a frozen dataclass is a descriptor, its set method is not called
also makes sense, thank you!
both the ClassVar error and the Unbound handling don't need to check for a frozen dataclass
makes sense ClassVar doesn't, hadn't thought that through enough - although i think the unbound handling might also need to check for a frozen dataclass. if it doesn't, the a.x = 2 assignment below won't emit a diagnostic because the x: int type declaration/meta attr is unbound for A:
from dataclasses import dataclass
@dataclass(frozen=True)
class A:
x: int
a = A(1)
a.x = 2 # no diagnostic
@dataclass(frozen=True)
class B:
x: int = 1
b = B()
b.x = 2 # emits read-only diagnosticthis wasn't super intuitive to me - i would have thought from this comment that x: int would be considered bound in both cases, but i'm probably not fully grokking whats going on here
Sorry I didn't comment on this more thoroughly
no problem! i appreciate the guidance (and patience :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that speculation or something you are seeing in your results? Because I also think (because we kind of conflate declaredness and boundness in a way we maybe shouldn't) that both A.x and B.x will actually report back Bound in that example. If that's not what you're seeing, I'd need to dig in further to understand it. It looks to me like the Unbound handling here is only for the cases where there is no such instance attribute at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's what i'm seeing in results - added a failing test case to illustrate
in this test, if the 1 is moved out of the constructor and instead set as a default value for x, it hits the other branch (now starting line 2958) and emits the diagnostic as expected
i can do a little more digging next week as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, this was my mistake; I'd failed to realize that the outer Unbound check was for looking up a class member. An x: int annotation at class level with no binding describes an instance-only attribute (in our model), so it comes back Unbound on the class. Which is why we then do an instance_member lookup. So I think the code you added (and then commented out) is necessary and correct. (I did make one small change to wrap up the dataclass work in a closure, so we only do that work if we need it.)
4892268 to
04355b9
Compare
Markdown formatting Co-authored-by: Alex Waygood <[email protected]>
04355b9 to
c9ca199
Compare
|
Thank you for the PR! Merged. |
#18347) ## Summary Related: - astral-sh/ty#111 - #17974 (comment) Previously, when validating an attribute assignment, a `__setattr__` call check was only done if the attribute wasn't found as either a class member or instance member This PR changes the `__setattr__` call check to be attempted first, prior to the "[normal mechanism](https://docs.python.org/3/reference/datamodel.html#object.__setattr__)", as a defined `__setattr__` should take precedence over setting an attribute on the instance dictionary directly. if the return type of `__setattr__` is `Never`, an `invalid-assignment` diagnostic is emitted Once this is merged, a subsequent PR will synthesize a `__setattr__` method with a `Never` return type for frozen dataclasses. ## Test Plan Existing tests + mypy_primer --------- Co-authored-by: David Peter <[email protected]>
## Summary Synthesize a `__setattr__` method with a return type of `Never` for frozen dataclasses. https://docs.python.org/3/library/dataclasses.html#frozen-instances https://docs.python.org/3/library/dataclasses.html#dataclasses.FrozenInstanceError ### Related astral-sh/ty#111 #17974 (comment) #18347 (comment) ## Test Plan New Markdown tests --------- Co-authored-by: David Peter <[email protected]>
Summary
astral-sh/ty#111
This PR adds support for
frozendataclasses. It will emit a diagnostic with a similar message to mypyNote: This does not include emitting a diagnostic if
__setattr__or__delattr__are defined on the object as per the specTest Plan
mdtest