[issue45520] Frozen dataclass deep copy doesn't work with __slots__

Justin Furuness report at bugs.python.org
Wed Oct 20 17:01:44 EDT 2021


Justin Furuness <jfuruness at gmail.com> added the comment:

Thank you for the in-depth explanation. That all makes sense to me, I have
run into the __slots__ with defaults issues before, I'll be sure to try out
these fixes. I appreciate you taking the time.

Thanks,
Justin

On Tue, Oct 19, 2021 at 5:28 PM Josh Rosenberg <report at bugs.python.org>
wrote:

>
> Josh Rosenberg <shadowranger+python at gmail.com> added the comment:
>
> 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).
>
> ----------
>
> _______________________________________
> Python tracker <report at bugs.python.org>
> <https://bugs.python.org/issue45520>
> _______________________________________
>

----------

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue45520>
_______________________________________


More information about the Python-bugs-list mailing list