microsoft / microsoft/pyright

dataclass instance attribute type wrong for field with descriptor type and no default

Open
#11,117 1 comment 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.