diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
index 0638df04c6f..290c63827ff 100644
--- a/Doc/library/functions.rst
+++ b/Doc/library/functions.rst
@@ -2032,6 +2032,10 @@ are always available. They are listed here in alphabetical order.
:func:`super`, see `guide to using super()
`_.
+ .. versionchanged:: 3.14
+ :class:`super` objects are now :mod:`pickleable ` and
+ :mod:`copyable `.
+
.. _func-tuple:
.. class:: tuple()
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index ad841538ccc..d52faa614db 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -190,6 +190,10 @@ Other language changes
They raise an error if the argument is a string.
(Contributed by Serhiy Storchaka in :gh:`84978`.)
+* :class:`super` objects are now :mod:`pickleable ` and
+ :mod:`copyable `.
+ (Contributed by Serhiy Storchaka in :gh:`125767`.)
+
New modules
===========
diff --git a/Lib/copy.py b/Lib/copy.py
index a79976d3a65..f27e109973c 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 578392409b4..17c5dde67c8 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 1222ec6a3c4..14901663552 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()
diff --git a/Misc/NEWS.d/next/Library/2024-10-21-13-52-37.gh-issue-125767.0kK4lX.rst b/Misc/NEWS.d/next/Library/2024-10-21-13-52-37.gh-issue-125767.0kK4lX.rst
new file mode 100644
index 00000000000..bfda740a79d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-10-21-13-52-37.gh-issue-125767.0kK4lX.rst
@@ -0,0 +1,2 @@
+:class:`super` objects are now :mod:`pickleable ` and
+:mod:`copyable `.