mirror of https://github.com/python/cpython
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.
This commit is contained in:
parent
de5a6c7c7d
commit
5ca4e34bc1
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
===========
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
:class:`super` objects are now :mod:`pickleable <pickle>` and
|
||||
:mod:`copyable <copy>`.
|
Loading…
Reference in New Issue