dataclass instance attribute type wrong for field with descriptor type and no default
- Dominant language
- Python
- Stars
- 15.6k
- Forks
- 1.8k
- Avg merge
- 12h 13m
- Merged PRs (30d)
- 52
Description
The inferred type, when accessing an attribute of descriptor type on an instance of a dataclass, wrongly considers the descriptor behavior, instead of being the descriptor type itself. Thus, the inferred type is different that the [actual runtime behavior](https://docs.python.org/3/reference/datamodel.html#implementing-descriptors) - which can be very confusing.
This minimal example shows the failing `assert_type` and the successful `assert` at runtime:
```python
from dataclasses import dataclass
from typing import assert_type
class Prop:
def __get__(self, *_) -> int:
return 0
@dataclass
class Broken:
prop: Prop
def __call__(self):
assert_type(self.prop, Prop) # this fails
assert isinstance(self.prop, Prop) # works at runtime
# assert actual runtime behavior
p = Prop()
b = Broken(prop=p)
assert b.prop is p # direct access
b() # assert from within
```
It works for a comparable manual implementation.
```python
class Works:
def __init__(self, prop: Prop):
self.prop = prop
def __call__(self):
assert_type(self.prop, Prop) # works
assert isinstance(self.prop, Prop)
```
On a plain class this is expected behavior, so this behavior is specific to dataclass' special handling.
```python
class ExpectedViaHint:
prop: Prop
def __call__(self):
assert_type(self.prop, int) # works (except during runtime - it's missing an actual implementation)
class ExpectedViaAssign:
prop = Prop()
def __call__(self):
assert_type(self.prop, int) # works
```
It is principally unrelated to the use of a descriptor as the default value for a dataclass field, but I suspect that some logic of this (intentionally?) bleeds over to this case.
```python
@dataclass
class Unrelated:
prop: int = Prop()
```
This seems to be a notoriously difficult topic, but I haven't seen this particular issue raised.
**Versions**
* python: 3.13.3
* pyright: 1.1.407
Contributor guide
Research direction
Run the minimal Python example with pyright 1.1.407 and compare the failing assert_type result with the runtime assertions. Trace pyright's dataclass and descriptor handling, then update the behavior so an instance field annotated as Prop is inferred as Prop and verify the example passes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100