gh-90494: Reject 6th element of the __reduce__() tuple (GH-93609) (GH-93631)

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 a365dd64c2)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
Miss Islington (bot) 2022-06-10 07:00:19 -07:00 committed by GitHub
parent 516d90eb21
commit c3045d809c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 2 deletions

View File

@ -258,7 +258,7 @@ def _keep_alive(x, memo):
def _reconstruct(x, memo, func, args, def _reconstruct(x, memo, func, args,
state=None, listiter=None, dictiter=None, state=None, listiter=None, dictiter=None,
deepcopy=deepcopy): *, deepcopy=deepcopy):
deep = memo is not None deep = memo is not None
if deep and args: if deep and args:
args = (deepcopy(arg, memo) for arg in args) args = (deepcopy(arg, memo) for arg in args)

View File

@ -619,7 +619,7 @@ class _Pickler:
"persistent IDs in protocol 0 must be ASCII strings") "persistent IDs in protocol 0 must be ASCII strings")
def save_reduce(self, func, args, state=None, listitems=None, 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 # This API is called by some subclasses
if not isinstance(args, tuple): if not isinstance(args, tuple):

View File

@ -678,6 +678,28 @@ class TestCopy(unittest.TestCase):
self.assertIsNot(x, y) self.assertIsNot(x, y)
self.assertIsNot(x["foo"], y["foo"]) 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): def test_copy_slots(self):
class C(object): class C(object):
__slots__ = ["foo"] __slots__ = ["foo"]

View File

@ -0,0 +1,3 @@
: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.