https://github.com/python/cpython/commit/5ca4e34bc1aab8321911aac6d5b2b9e75ff764d8
commit: 5ca4e34bc1aab8321911aac6d5b2b9e75ff764d8
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2024-10-21T21:30:45+03:00
summary:

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.

files:
A Misc/NEWS.d/next/Library/2024-10-21-13-52-37.gh-issue-125767.0kK4lX.rst
M Doc/library/functions.rst
M Doc/whatsnew/3.14.rst
M Lib/copy.py
M Lib/copyreg.py
M Lib/test/test_super.py

diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst
index 0638df04c6ff40..290c63827ff766 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()
    <https://rhettinger.wordpress.com/2011/05/26/super-considered-super/>`_.
 
+   .. versionchanged:: 3.14
+     :class:`super` objects are now :mod:`pickleable <pickle>` and
+      :mod:`copyable <copy>`.
+
 
 .. _func-tuple:
 .. class:: tuple()
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index ad841538ccc547..d52faa614db94e 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 <pickle>` and
+  :mod:`copyable <copy>`.
+  (Contributed by Serhiy Storchaka in :gh:`125767`.)
+
 
 New modules
 ===========
diff --git a/Lib/copy.py b/Lib/copy.py
index a79976d3a658f0..f27e109973cfb7 100644
--- a/Lib/copy.py
+++ b/Lib/copy.py
@@ -106,7 +106,7 @@ def _copy_immutable(x):
           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 578392409b403c..17c5dde67c887c 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 1222ec6a3c4109..149016635522c3 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 @@ def work():
         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 00000000000000..bfda740a79d10e
--- /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 <pickle>` and
+:mod:`copyable <copy>`.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to