summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2022-06-10 06:59:39 -0700
committerGitHub <noreply@github.com>2022-06-10 15:59:39 +0200
commita0c7df09689769f3016dc9145eb56341aac631a9 (patch)
tree7fb9409bcb5f978e628449c4efd4e52a77bb2154
parent2ad51c636ae44ef847bf8d7ae687f739dfeade41 (diff)
downloadcpython-git-a0c7df09689769f3016dc9145eb56341aac631a9.tar.gz
gh-90494: Reject 6th element of the __reduce__() tuple (GH-93609) (GH-93632)
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>
-rw-r--r--Lib/copy.py2
-rw-r--r--Lib/pickle.py2
-rw-r--r--Lib/test/test_copy.py22
-rw-r--r--Misc/NEWS.d/next/Library/2022-06-08-20-11-02.gh-issue-90494.LIZT85.rst3
4 files changed, 27 insertions, 2 deletions
diff --git a/Lib/copy.py b/Lib/copy.py
index 69bac980be..1b276afe08 100644
--- a/Lib/copy.py
+++ b/Lib/copy.py
@@ -258,7 +258,7 @@ def _keep_alive(x, memo):
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)
diff --git a/Lib/pickle.py b/Lib/pickle.py
index e7f30f2261..f027e04320 100644
--- a/Lib/pickle.py
+++ b/Lib/pickle.py
@@ -619,7 +619,7 @@ class _Pickler:
"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):
diff --git a/Lib/test/test_copy.py b/Lib/test/test_copy.py
index f1ca8cb254..f4d91c1686 100644
--- a/Lib/test/test_copy.py
+++ b/Lib/test/test_copy.py
@@ -678,6 +678,28 @@ class TestCopy(unittest.TestCase):
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"]
diff --git a/Misc/NEWS.d/next/Library/2022-06-08-20-11-02.gh-issue-90494.LIZT85.rst b/Misc/NEWS.d/next/Library/2022-06-08-20-11-02.gh-issue-90494.LIZT85.rst
new file mode 100644
index 0000000000..9541676879
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-06-08-20-11-02.gh-issue-90494.LIZT85.rst
@@ -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.