copy.copy() and copy.deepcopy() now always raise a TypeError if
__reduce__() returns a tuple with length 6 instead of silently ignore
the 6th item or produce incorrect result.
(cherry picked from commit
a365dd64c2a1f0d142540d5031003f24986f489f)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
def _reconstruct(x, memo, func, args,
state=None, listiter=None, dictiter=None,
- deepcopy=deepcopy):
+ *, deepcopy=deepcopy):
deep = memo is not None
if deep and args:
args = (deepcopy(arg, memo) for arg in args)
"persistent IDs in protocol 0 must be ASCII strings")
def save_reduce(self, func, args, state=None, listitems=None,
- dictitems=None, state_setter=None, obj=None):
+ dictitems=None, state_setter=None, *, obj=None):
# This API is called by some subclasses
if not isinstance(args, tuple):
self.assertIsNot(x, y)
self.assertIsNot(x["foo"], y["foo"])
+ def test_reduce_6tuple(self):
+ def state_setter(*args, **kwargs):
+ self.fail("shouldn't call this")
+ class C:
+ def __reduce__(self):
+ return C, (), self.__dict__, None, None, state_setter
+ x = C()
+ with self.assertRaises(TypeError):
+ copy.copy(x)
+ with self.assertRaises(TypeError):
+ copy.deepcopy(x)
+
+ def test_reduce_6tuple_none(self):
+ class C:
+ def __reduce__(self):
+ return C, (), self.__dict__, None, None, None
+ x = C()
+ with self.assertRaises(TypeError):
+ copy.copy(x)
+ with self.assertRaises(TypeError):
+ copy.deepcopy(x)
+
def test_copy_slots(self):
class C(object):
__slots__ = ["foo"]
--- /dev/null
+:func:`copy.copy` and :func:`copy.deepcopy` now always raise a TypeError if
+``__reduce__()`` returns a tuple with length 6 instead of silently ignore
+the 6th item or produce incorrect result.