tuple(f.name for f in std_init_fields))
if slots:
- cls = _add_slots(cls)
+ cls = _add_slots(cls, frozen)
abc.update_abstractmethods(cls)
return cls
-def _add_slots(cls):
+# _dataclass_getstate and _dataclass_setstate are needed for pickling frozen
+# classes with slots. These could be slighly more performant if we generated
+# the code instead of iterating over fields. But that can be a project for
+# another day, if performance becomes an issue.
+def _dataclass_getstate(self):
+ return [getattr(self, f.name) for f in fields(self)]
+
+
+def _dataclass_setstate(self, state):
+ for field, value in zip(fields(self), state):
+ # use setattr because dataclass may be frozen
+ object.__setattr__(self, field.name, value)
+
+
+def _add_slots(cls, is_frozen):
# Need to create a new class, since we can't set __slots__
# after a class has been created.
if qualname is not None:
cls.__qualname__ = qualname
+ if is_frozen:
+ # Need this for pickling frozen classes with slots.
+ cls.__getstate__ = _dataclass_getstate
+ cls.__setstate__ = _dataclass_setstate
+
return cls
self.assertFalse(hasattr(A, "__slots__"))
self.assertTrue(hasattr(B, "__slots__"))
+ # Can't be local to test_frozen_pickle.
+ @dataclass(frozen=True, slots=True)
+ class FrozenSlotsClass:
+ foo: str
+ bar: int
+
+ def test_frozen_pickle(self):
+ # bpo-43999
+
+ assert self.FrozenSlotsClass.__slots__ == ("foo", "bar")
+ p = pickle.dumps(self.FrozenSlotsClass("a", 1))
+ assert pickle.loads(p) == self.FrozenSlotsClass("a", 1)
+
class TestDescriptors(unittest.TestCase):
def test_set_name(self):