You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Dataclass "init" method should expect the type from a custom descriptor's "set" method #13856
If a @dataclass field is initialized with a descriptor as a default value, mypy should use the value type of the __set__ method for the descriptor when determining the type of the corresponding parameter within the synthesized __init__ method.
Types are also evaluated incorrectly in this case.
fromdataclassesimportdataclassfromtypingimportAny, castclassMyDescriptor:
def__get__(self, __obj: object|None, __owner: Any) ->"int | MyDescriptor":
if__objisNone:
returnselfreturncast(Any, __obj)._xdef__set__(self, __obj: object|None, __value: int) ->None:
if__objisnotNone:
cast(Any, __obj)._x=__value@dataclassclassFoo:
y: MyDescriptor=MyDescriptor()
f=Foo(3) # Currently a false positive error in mypyprint(f.y) # 3f.y=4print(f.y) # 4print(Foo.y) # MyDescriptor objectreveal_type(f.y) # int | MyDescriptor, which is wrongreveal_type(Foo.y) # int | MyDescriptor, which is wrong
To Reproduce
Run mypy against the above script.
Expected Behavior
No Errors
Actual Behavior
test.py:23: error: Argument 1 to "Foo" has incompatible type "int"; expected "MyDescriptor"
test.py:30: note: Revealed type is "Union[builtins.int, test.MyDescriptor]"
test.py:31: note: Revealed type is "Union[builtins.int, test.MyDescriptor]"
Found 1 error in 1 file (checked 1 source file)
Your Environment
OS: Ubuntu 22.04.1
Mypy version used: 0.982
Mypy command-line flags: None
Mypy configuration options from mypy.ini (and other config files): None
I think this confusion goes a bit deeper than __set__; Mypy seems to have the wrong idea about dataclass-like types in general non-dataclass PEP 681 classes such as attrs. Consider this brief comparison of attrs and dataclasses:
Side-by-side, type-check and output looks like this (emojis added to indicate agreement / disagreement between type-time and runtime):
t.py:27: note: Revealed type is "t.DecoGet" Runtime type is 'Deco' ❌ attrs / instance / no default
t.py:28: note: Revealed type is "t.DecoGet" Runtime type is 'Deco' ❌ attrs / instance / default
t.py:30: note: Revealed type is "t.Deco" Runtime type is 'Deco' ✅ dataclasses / instance / no default
t.py:31: note: Revealed type is "t.DecoGet" Runtime type is 'DecoGet' ✅ dataclasses / instance / default
t.py:33: note: Revealed type is "t.DecoGet" Runtime type is 'member_descriptor' ❌ attrs / class / no default
t.py:34: note: Revealed type is "t.DecoGet" Runtime type is 'member_descriptor' ❌ attrs / class / default
t.py:37: note: Revealed type is "t.DecoGet" skipped ❌ dataclasses / class / no default
t.py:40: note: Revealed type is "t.DecoGet" Runtime type is 'DecoGet' ✅ dataclasses / class / default
Updated to include annotations about which one's which, which shows that the problem is with attrs' annotations more than it is with dataclasses, although "attribute error" vs. descriptor-type is not great.
Activity
pappasam commentedon Oct 11, 2022
@erictraut fyi, I migrated your issue from pyright here
debonte commentedon Mar 10, 2023
FYI, the behavior of
dataclasswith descriptor-typed fields was discussed in python/cpython#91330 and is documented here.glyph commentedon Mar 10, 2023
I think this confusion goes a bit deeper than
__set__; Mypy seems to have the wrong idea aboutdataclass-like types in generalnon-dataclass PEP 681 classes such as attrs. Consider this brief comparison of attrs and dataclasses:Side-by-side, type-check and output looks like this (emojis added to indicate agreement / disagreement between type-time and runtime):
Updated to include annotations about which one's which, which shows that the problem is with attrs' annotations more than it is with dataclasses, although "attribute error" vs. descriptor-type is not great.
glyph commentedon Mar 10, 2023
As I was playing with this I discovered a more specific problem in this neighborhood with Callable #14869
document no-pep681 workarounds
document no-pep681 workarounds
feat: avoid use of Ref, make use of descriptor to add reactive behavior
feat: avoid use of Ref, make use of descriptor to add reactive behavior
pappasam commentedon Apr 18, 2023
Just tested out with
mypy 1.2and this appears to have been resolved!