summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2024-10-21 18:30:45 (GMT)
committerGitHub <noreply@github.com>2024-10-21 18:30:45 (GMT)
commit5ca4e34bc1aab8321911aac6d5b2b9e75ff764d8 (patch)
treed4f4dc4e80de809d695247c749a3ac50340ef78e /Lib
parentde5a6c7c7d00ac37d66cba9849202b374e9cdfb7 (diff)
downloadcpython-5ca4e34bc1aab8321911aac6d5b2b9e75ff764d8.zip
cpython-5ca4e34bc1aab8321911aac6d5b2b9e75ff764d8.tar.gz
cpython-5ca4e34bc1aab8321911aac6d5b2b9e75ff764d8.tar.bz2
gh-125767: Fix pickling and copying of super objects (GH-125781)
Previously, copying a super object returned a copy of the instance invoking super(). Pickling a super object could pickle the instance invoking super() or fail, depending on its type and protocol. Now deep copying returns a new super object and pickling pickles the super object. Shallow copying returns the same super object.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/copy.py2
-rw-r--r--Lib/copyreg.py5
-rw-r--r--Lib/test/test_super.py70
3 files changed, 76 insertions, 1 deletions
diff --git a/Lib/copy.py b/Lib/copy.py
index a79976d..f27e109 100644
--- a/Lib/copy.py
+++ b/Lib/copy.py
@@ -106,7 +106,7 @@ for t in (types.NoneType, int, float, bool, complex, str, tuple,
bytes, frozenset, type, range, slice, property,
types.BuiltinFunctionType, types.EllipsisType,
types.NotImplementedType, types.FunctionType, types.CodeType,
- weakref.ref):
+ weakref.ref, super):
d[t] = _copy_immutable
d[list] = list.copy
diff --git a/Lib/copyreg.py b/Lib/copyreg.py
index 5783924..17c5dde 100644
--- a/Lib/copyreg.py
+++ b/Lib/copyreg.py
@@ -36,6 +36,11 @@ def pickle_union(obj):
pickle(type(int | str), pickle_union)
+def pickle_super(obj):
+ return super, (obj.__thisclass__, obj.__self__)
+
+pickle(super, pickle_super)
+
# Support for pickling new-style objects
def _reconstructor(cls, base, state):
diff --git a/Lib/test/test_super.py b/Lib/test/test_super.py
index 1222ec6..1490166 100644
--- a/Lib/test/test_super.py
+++ b/Lib/test/test_super.py
@@ -1,5 +1,7 @@
"""Unit tests for zero-argument super() & related machinery."""
+import copy
+import pickle
import textwrap
import threading
import unittest
@@ -539,6 +541,74 @@ class TestSuper(unittest.TestCase):
for thread in threads:
thread.join()
+ def test_special_methods(self):
+ for e in E(), E:
+ s = super(C, e)
+ self.assertEqual(s.__reduce__, e.__reduce__)
+ self.assertEqual(s.__reduce_ex__, e.__reduce_ex__)
+ self.assertEqual(s.__getstate__, e.__getstate__)
+ self.assertFalse(hasattr(s, '__getnewargs__'))
+ self.assertFalse(hasattr(s, '__getnewargs_ex__'))
+ self.assertFalse(hasattr(s, '__setstate__'))
+ self.assertFalse(hasattr(s, '__copy__'))
+ self.assertFalse(hasattr(s, '__deepcopy__'))
+
+ def test_pickling(self):
+ e = E()
+ e.x = 1
+ s = super(C, e)
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.subTest(proto=proto):
+ u = pickle.loads(pickle.dumps(s, proto))
+ self.assertEqual(u.f(), s.f())
+ self.assertIs(type(u), type(s))
+ self.assertIs(type(u.__self__), E)
+ self.assertEqual(u.__self__.x, 1)
+ self.assertIs(u.__thisclass__, C)
+ self.assertIs(u.__self_class__, E)
+
+ s = super(C, E)
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.subTest(proto=proto):
+ u = pickle.loads(pickle.dumps(s, proto))
+ self.assertEqual(u.cm(), s.cm())
+ self.assertEqual(u.f, s.f)
+ self.assertIs(type(u), type(s))
+ self.assertIs(u.__self__, E)
+ self.assertIs(u.__thisclass__, C)
+ self.assertIs(u.__self_class__, E)
+
+ def test_shallow_copying(self):
+ s = super(C, E())
+ self.assertIs(copy.copy(s), s)
+ s = super(C, E)
+ self.assertIs(copy.copy(s), s)
+
+ def test_deep_copying(self):
+ e = E()
+ e.x = [1]
+ s = super(C, e)
+ u = copy.deepcopy(s)
+ self.assertEqual(u.f(), s.f())
+ self.assertIs(type(u), type(s))
+ self.assertIsNot(u, s)
+ self.assertIs(type(u.__self__), E)
+ self.assertIsNot(u.__self__, e)
+ self.assertIsNot(u.__self__.x, e.x)
+ self.assertEqual(u.__self__.x, [1])
+ self.assertIs(u.__thisclass__, C)
+ self.assertIs(u.__self_class__, E)
+
+ s = super(C, E)
+ u = copy.deepcopy(s)
+ self.assertEqual(u.cm(), s.cm())
+ self.assertEqual(u.f, s.f)
+ self.assertIsNot(u, s)
+ self.assertIs(type(u), type(s))
+ self.assertIs(u.__self__, E)
+ self.assertIs(u.__thisclass__, C)
+ self.assertIs(u.__self_class__, E)
+
if __name__ == "__main__":
unittest.main()