Skip to content

Commit 5ca4e34

Browse files
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.
1 parent de5a6c7 commit 5ca4e34

File tree

6 files changed

+86
-1
lines changed

6 files changed

+86
-1
lines changed

Doc/library/functions.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2032,6 +2032,10 @@ are always available. They are listed here in alphabetical order.
20322032
:func:`super`, see `guide to using super()
20332033
<https://rhettinger.wordpress.com/2011/05/26/super-considered-super/>`_.
20342034

2035+
.. versionchanged:: 3.14
2036+
:class:`super` objects are now :mod:`pickleable <pickle>` and
2037+
:mod:`copyable <copy>`.
2038+
20352039

20362040
.. _func-tuple:
20372041
.. class:: tuple()

Doc/whatsnew/3.14.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ Other language changes
190190
They raise an error if the argument is a string.
191191
(Contributed by Serhiy Storchaka in :gh:`84978`.)
192192

193+
* :class:`super` objects are now :mod:`pickleable <pickle>` and
194+
:mod:`copyable <copy>`.
195+
(Contributed by Serhiy Storchaka in :gh:`125767`.)
196+
193197

194198
New modules
195199
===========

Lib/copy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def _copy_immutable(x):
106106
bytes, frozenset, type, range, slice, property,
107107
types.BuiltinFunctionType, types.EllipsisType,
108108
types.NotImplementedType, types.FunctionType, types.CodeType,
109-
weakref.ref):
109+
weakref.ref, super):
110110
d[t] = _copy_immutable
111111

112112
d[list] = list.copy

Lib/copyreg.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ def pickle_union(obj):
3636

3737
pickle(type(int | str), pickle_union)
3838

39+
def pickle_super(obj):
40+
return super, (obj.__thisclass__, obj.__self__)
41+
42+
pickle(super, pickle_super)
43+
3944
# Support for pickling new-style objects
4045

4146
def _reconstructor(cls, base, state):

Lib/test/test_super.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Unit tests for zero-argument super() & related machinery."""
22

3+
import copy
4+
import pickle
35
import textwrap
46
import threading
57
import unittest
@@ -539,6 +541,74 @@ def work():
539541
for thread in threads:
540542
thread.join()
541543

544+
def test_special_methods(self):
545+
for e in E(), E:
546+
s = super(C, e)
547+
self.assertEqual(s.__reduce__, e.__reduce__)
548+
self.assertEqual(s.__reduce_ex__, e.__reduce_ex__)
549+
self.assertEqual(s.__getstate__, e.__getstate__)
550+
self.assertFalse(hasattr(s, '__getnewargs__'))
551+
self.assertFalse(hasattr(s, '__getnewargs_ex__'))
552+
self.assertFalse(hasattr(s, '__setstate__'))
553+
self.assertFalse(hasattr(s, '__copy__'))
554+
self.assertFalse(hasattr(s, '__deepcopy__'))
555+
556+
def test_pickling(self):
557+
e = E()
558+
e.x = 1
559+
s = super(C, e)
560+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
561+
with self.subTest(proto=proto):
562+
u = pickle.loads(pickle.dumps(s, proto))
563+
self.assertEqual(u.f(), s.f())
564+
self.assertIs(type(u), type(s))
565+
self.assertIs(type(u.__self__), E)
566+
self.assertEqual(u.__self__.x, 1)
567+
self.assertIs(u.__thisclass__, C)
568+
self.assertIs(u.__self_class__, E)
569+
570+
s = super(C, E)
571+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
572+
with self.subTest(proto=proto):
573+
u = pickle.loads(pickle.dumps(s, proto))
574+
self.assertEqual(u.cm(), s.cm())
575+
self.assertEqual(u.f, s.f)
576+
self.assertIs(type(u), type(s))
577+
self.assertIs(u.__self__, E)
578+
self.assertIs(u.__thisclass__, C)
579+
self.assertIs(u.__self_class__, E)
580+
581+
def test_shallow_copying(self):
582+
s = super(C, E())
583+
self.assertIs(copy.copy(s), s)
584+
s = super(C, E)
585+
self.assertIs(copy.copy(s), s)
586+
587+
def test_deep_copying(self):
588+
e = E()
589+
e.x = [1]
590+
s = super(C, e)
591+
u = copy.deepcopy(s)
592+
self.assertEqual(u.f(), s.f())
593+
self.assertIs(type(u), type(s))
594+
self.assertIsNot(u, s)
595+
self.assertIs(type(u.__self__), E)
596+
self.assertIsNot(u.__self__, e)
597+
self.assertIsNot(u.__self__.x, e.x)
598+
self.assertEqual(u.__self__.x, [1])
599+
self.assertIs(u.__thisclass__, C)
600+
self.assertIs(u.__self_class__, E)
601+
602+
s = super(C, E)
603+
u = copy.deepcopy(s)
604+
self.assertEqual(u.cm(), s.cm())
605+
self.assertEqual(u.f, s.f)
606+
self.assertIsNot(u, s)
607+
self.assertIs(type(u), type(s))
608+
self.assertIs(u.__self__, E)
609+
self.assertIs(u.__thisclass__, C)
610+
self.assertIs(u.__self_class__, E)
611+
542612

543613
if __name__ == "__main__":
544614
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:class:`super` objects are now :mod:`pickleable <pickle>` and
2+
:mod:`copyable <copy>`.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy