diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2022-06-09 07:12:43 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-09 07:12:43 (GMT) |
commit | a365dd64c2a1f0d142540d5031003f24986f489f (patch) | |
tree | 136b63bf5621d9289baae429f2221dfed2c9558c | |
parent | cffa4f7854eb11e21c25026b24e9ccd14763c8d3 (diff) | |
download | cpython-a365dd64c2a1f0d142540d5031003f24986f489f.zip cpython-a365dd64c2a1f0d142540d5031003f24986f489f.tar.gz cpython-a365dd64c2a1f0d142540d5031003f24986f489f.tar.bz2 |
gh-90494: Reject 6th element of the __reduce__() tuple (GH-93609)
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.
-rw-r--r-- | Lib/copy.py | 2 | ||||
-rw-r--r-- | Lib/pickle.py | 2 | ||||
-rw-r--r-- | Lib/test/test_copy.py | 22 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2022-06-08-20-11-02.gh-issue-90494.LIZT85.rst | 3 |
4 files changed, 27 insertions, 2 deletions
diff --git a/Lib/copy.py b/Lib/copy.py index 69bac98..1b276af 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 e7f30f2..f027e04 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 f1ca8cb..f4d91c1 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 0000000..9541676 --- /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. |