[Python-checkins] [3.11] gh-104035: Do not ignore user-defined `__{get, set}state__` in slotted frozen dataclasses (GH-104041) (#104044)
carljm
webhook-mailer at python.org
Mon May 1 12:02:49 EDT 2023
https://github.com/python/cpython/commit/0df7c3a466c4d5b65ce619952009477fba1e91e9
commit: 0df7c3a466c4d5b65ce619952009477fba1e91e9
branch: 3.11
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: carljm <carl at oddbird.net>
date: 2023-05-01T10:02:41-06:00
summary:
[3.11] gh-104035: Do not ignore user-defined `__{get,set}state__` in slotted frozen dataclasses (GH-104041) (#104044)
gh-104035: Do not ignore user-defined `__{get,set}state__` in slotted frozen dataclasses (GH-104041)
(cherry picked from commit 99aab610622fc4b4c4fe56b77c0760cf77066a53)
Co-authored-by: Nikita Sobolev <mail at sobolevn.me>
files:
A Misc/NEWS.d/next/Library/2023-05-01-16-43-28.gh-issue-104035.MrJBw8.rst
M Lib/dataclasses.py
M Lib/test/test_dataclasses.py
diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index a7e0547705ca..148daf45330c 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -1191,8 +1191,10 @@ def _add_slots(cls, is_frozen, weakref_slot):
if is_frozen:
# Need this for pickling frozen classes with slots.
- cls.__getstate__ = _dataclass_getstate
- cls.__setstate__ = _dataclass_setstate
+ if '__getstate__' not in cls_dict:
+ cls.__getstate__ = _dataclass_getstate
+ if '__setstate__' not in cls_dict:
+ cls.__setstate__ = _dataclass_setstate
return cls
diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py
index 1aaa78db4b0a..4714d0bca861 100644
--- a/Lib/test/test_dataclasses.py
+++ b/Lib/test/test_dataclasses.py
@@ -3068,6 +3068,74 @@ def test_frozen_pickle(self):
self.assertIsNot(obj, p)
self.assertEqual(obj, p)
+ @dataclass(frozen=True, slots=True)
+ class FrozenSlotsGetStateClass:
+ foo: str
+ bar: int
+
+ getstate_called: bool = field(default=False, compare=False)
+
+ def __getstate__(self):
+ object.__setattr__(self, 'getstate_called', True)
+ return [self.foo, self.bar]
+
+ @dataclass(frozen=True, slots=True)
+ class FrozenSlotsSetStateClass:
+ foo: str
+ bar: int
+
+ setstate_called: bool = field(default=False, compare=False)
+
+ def __setstate__(self, state):
+ object.__setattr__(self, 'setstate_called', True)
+ object.__setattr__(self, 'foo', state[0])
+ object.__setattr__(self, 'bar', state[1])
+
+ @dataclass(frozen=True, slots=True)
+ class FrozenSlotsAllStateClass:
+ foo: str
+ bar: int
+
+ getstate_called: bool = field(default=False, compare=False)
+ setstate_called: bool = field(default=False, compare=False)
+
+ def __getstate__(self):
+ object.__setattr__(self, 'getstate_called', True)
+ return [self.foo, self.bar]
+
+ def __setstate__(self, state):
+ object.__setattr__(self, 'setstate_called', True)
+ object.__setattr__(self, 'foo', state[0])
+ object.__setattr__(self, 'bar', state[1])
+
+ def test_frozen_slots_pickle_custom_state(self):
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.subTest(proto=proto):
+ obj = self.FrozenSlotsGetStateClass('a', 1)
+ dumped = pickle.dumps(obj, protocol=proto)
+
+ self.assertTrue(obj.getstate_called)
+ self.assertEqual(obj, pickle.loads(dumped))
+
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.subTest(proto=proto):
+ obj = self.FrozenSlotsSetStateClass('a', 1)
+ obj2 = pickle.loads(pickle.dumps(obj, protocol=proto))
+
+ self.assertTrue(obj2.setstate_called)
+ self.assertEqual(obj, obj2)
+
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.subTest(proto=proto):
+ obj = self.FrozenSlotsAllStateClass('a', 1)
+ dumped = pickle.dumps(obj, protocol=proto)
+
+ self.assertTrue(obj.getstate_called)
+
+ obj2 = pickle.loads(dumped)
+ self.assertTrue(obj2.setstate_called)
+ self.assertEqual(obj, obj2)
+
def test_slots_with_default_no_init(self):
# Originally reported in bpo-44649.
@dataclass(slots=True)
diff --git a/Misc/NEWS.d/next/Library/2023-05-01-16-43-28.gh-issue-104035.MrJBw8.rst b/Misc/NEWS.d/next/Library/2023-05-01-16-43-28.gh-issue-104035.MrJBw8.rst
new file mode 100644
index 000000000000..8c8e3d6ba5fb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-05-01-16-43-28.gh-issue-104035.MrJBw8.rst
@@ -0,0 +1,2 @@
+Do not ignore user-defined ``__getstate__`` and ``__setstate__`` methods for
+slotted frozen dataclasses.
More information about the Python-checkins
mailing list