This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author josh.r
Recipients eric.smith, jfuruness, josh.r
Date 2021-10-19.21:28:37
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1634678917.75.0.508428359028.issue45520@roundup.psfhosted.org>
In-reply-to
Content
You're right that in non-dataclass scenarios, you'd just use __slots__.

The slots=True thing was necessary for any case where any of the dataclass's attributes have default values (my_int: int = 0), or are defined with fields (my_list: list = field(default_factory=list)). The problem is that __slots__ is implemented by, after the class definition ends, creating descriptors on the class to access the data stored at known offsets in the underlying PyObject structure. Those descriptors themselves being class attributes means that when the type definition machinery tries to use __slots__ to create them, it finds conflicting class attributes (the defaults/fields) that already exist and explodes.

Adding support for slots=True means it does two things:

1. It completely defines the class without slots, extracts the stuff it needs to make the dataclass separately, then deletes it from the class definition namespace and makes a *new* class with __slots__ defined (so no conflict occurs)
2. It checks if the dataclass is also frozen, and applies alternate __getstate__/__setstate__ methods that are compatible with a frozen, slotted dataclass

#2 is what fixes this bug (while #1 makes it possible to use the full range of dataclass features without sacrificing the ability to use __slots__). If you need this to work in 3.9, you could borrow the 3.10 implementations that make this work for frozen dataclasses to explicitly define __getstate__/__setstate__ for your frozen slotted dataclasses:

def __getstate__(self):
    return [getattr(self, f.name) for f in fields(self)]


def __setstate__(self, state):
    for field, value in zip(fields(self), state):
        # use setattr because dataclass may be frozen
        object.__setattr__(self, field.name, value)

I'm not closing this since backporting just the fix for frozen slotted dataclasses (without backporting the full slots=True functionality that's a new feature) is possibly within scope for a bugfix release of 3.9 (it wouldn't change the behavior of working code, and fixes broken code that might reasonably be expected to work).
History
Date User Action Args
2021-10-19 21:28:38josh.rsetrecipients: + josh.r, eric.smith, jfuruness
2021-10-19 21:28:37josh.rsetmessageid: <1634678917.75.0.508428359028.issue45520@roundup.psfhosted.org>
2021-10-19 21:28:37josh.rlinkissue45520 messages
2021-10-19 21:28:37josh.rcreate