-
Notifications
You must be signed in to change notification settings - Fork 1.4k
[ty] Implement DataClassInstance
protocol for dataclasses.
#18018
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
|
This PR does silence the false positives mentioned in the original issue, but does not fully model the return type of |
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.
Thank you very much! This looks good.. just a minor question regarding the return type and the type qualifier.
3b213f8
to
fa095e5
Compare
Thanks for reviewing. I've changed the type as you mentioned. I've also added a I do have one question though. I wanted to add another assertion to the tests, something like this: from ty_extensions import static_assert, is_subtype_of
from _typeshed import DataclassInstance
@dataclass
class Foo:
x: int
static_assert(is_subtype_of(Foo, DataclassInstance)) This assertion however, failed. I am not sure why. I grepped for any other examples of |
Thanks for the review Alex! I've addressed your comments. |
@AlexWaygood might know more about why we don't use/support this, so far. |
I updated the test assertion now that #17998 has been merged. |
Thank you! |
It seems to be because we still incorrectly emit an error if the attribute is accessed on instances of dataclasses rather than dataclass class objects themselves: @dataclass
class Foo: ...
reveal_type(Foo.__dataclass_fields__) # revealed: dict[str, Field[Any]]
# error: Type `Foo` has no attribute `__dataclass_fields__`
reveal_type(Foo().__dataclass_fields__) # revealed: Unknown |
Oh! I thought there was a problem with the |
Oh makes sense! I will dig into the code to understand more, but conceptually, why does |
…eep-dish * origin/main: [ty] __file__ is always a string inside a Python module (#18071) [ty] Recognize submodules in self-referential imports (#18005) [ty] Add a note to the diagnostic if a new builtin is used on an old Python version (#18068) [ty] Add tests for `else` branches of `hasattr()` narrowing (#18067) [ty] Improve diagnostics for `assert_type` and `assert_never` (#18050) [ty] contribution guide (#18061) [ty] Implement `DataClassInstance` protocol for dataclasses. (#18018) [ruff_python_ast] Fix redundant visitation of test expressions in elif clause statements (#18064)
Yes, this is true at runtime, and ty should understand it too. Generally we do, but in this PR we do not, because the special case for a member lookup of So to fix this, either we also need a special case for |
On second look, I also think that the assertion made in the tests here is wrong: the class object |
I did not understand this. Which assertion specifically? Assuming we do add a synthesized member to I understand that the class object |
This one:
I don't think so. The members and types match, but the type qualifiers do not: class DataclassInstance(Protocol):
__dataclass_fields__: ClassVar[dict[str, Field[Any]]] I'm not sure where that is specified, but I assume the from dataclasses import dataclass
@dataclass
class C: ...
c = C()
type(c).__dataclass_fields__ # this exists But for class objects, this is not true: type(C).__dataclass_fields # this does not exist. `type(C)` is `type`. Note that dataclass class objects can still be passed to I was already working on a fix (#18115) when I saw your message. |
This makes sense. I also could not find this anywhere, but is the reasoning here that, the constraints: (1) members annotated with .. together imply that it must be present in the type of But thanks for the explanation and the fix, and I'm sorry that you had to rework this :( |
No worries! Thank you for the initial implementation. |
…sh#18018) Fixes: astral-sh/ty#92 ## Summary We currently get a `invalid-argument-type` error when using `dataclass.fields` on a dataclass, because we do not synthesize the `__dataclass_fields__` member. This PR fixes this diagnostic. Note that we do not yet model the `Field` type correctly. After that is done, we can assign a more precise `tuple[Field, ...]` type to this new member. ## Test Plan New mdtest. --------- Co-authored-by: David Peter <[email protected]>
Fixes: astral-sh/ty#92
Summary
We currently get a
invalid-argument-type
error when usingdataclass.fields
on a dataclass, because we do not synthesize the__dataclass_fields__
member.This PR fixes this diagnostic.
Note that we do not yet model the
Field
type correctly. After that is done, we can assign a more precisetuple[Field, ...]
type to this new member.Test Plan
New mdtest.