https://github.com/python/cpython/commit/e9253ebf74433de5ae6d7f1bce693a3a1173b3b1 commit: e9253ebf74433de5ae6d7f1bce693a3a1173b3b1 branch: main author: Serhiy Storchaka <storch...@gmail.com> committer: serhiy-storchaka <storch...@gmail.com> date: 2025-05-04T14:06:49Z summary:
gh-122559: Synchronize C and Python implementation of the io module about pickling (GH-122628) In the C implementation, remove __reduce__ and __reduce_ex__ methods that always raise TypeError and restore __getstate__ methods that always raise TypeErrori. This restores fine details of the pre-3.12 behavior and unifies both implementations. files: A Misc/NEWS.d/next/Library/2024-08-02-20-01-36.gh-issue-122559.2JlJr3.rst M Lib/test/test_io.py M Modules/_io/bufferedio.c M Modules/_io/fileio.c M Modules/_io/textio.c diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 545643aa455a5b..5a8f1949baaa98 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -1373,6 +1373,28 @@ def test_readonly_attributes(self): with self.assertRaises(AttributeError): buf.raw = x + def test_pickling_subclass(self): + global MyBufferedIO + class MyBufferedIO(self.tp): + def __init__(self, raw, tag): + super().__init__(raw) + self.tag = tag + def __getstate__(self): + return self.tag, self.raw.getvalue() + def __setstate__(slf, state): + tag, value = state + slf.__init__(self.BytesIO(value), tag) + + raw = self.BytesIO(b'data') + buf = MyBufferedIO(raw, tag='ham') + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=proto): + pickled = pickle.dumps(buf, proto) + newbuf = pickle.loads(pickled) + self.assertEqual(newbuf.raw.getvalue(), b'data') + self.assertEqual(newbuf.tag, 'ham') + del MyBufferedIO + class SizeofTest: @@ -3950,6 +3972,28 @@ def test_issue35928(self): f.write(res) self.assertEqual(res + f.readline(), 'foo\nbar\n') + def test_pickling_subclass(self): + global MyTextIO + class MyTextIO(self.TextIOWrapper): + def __init__(self, raw, tag): + super().__init__(raw) + self.tag = tag + def __getstate__(self): + return self.tag, self.buffer.getvalue() + def __setstate__(slf, state): + tag, value = state + slf.__init__(self.BytesIO(value), tag) + + raw = self.BytesIO(b'data') + txt = MyTextIO(raw, 'ham') + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + with self.subTest(protocol=proto): + pickled = pickle.dumps(txt, proto) + newtxt = pickle.loads(pickled) + self.assertEqual(newtxt.buffer.getvalue(), b'data') + self.assertEqual(newtxt.tag, 'ham') + del MyTextIO + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") def test_read_non_blocking(self): import os diff --git a/Misc/NEWS.d/next/Library/2024-08-02-20-01-36.gh-issue-122559.2JlJr3.rst b/Misc/NEWS.d/next/Library/2024-08-02-20-01-36.gh-issue-122559.2JlJr3.rst new file mode 100644 index 00000000000000..4ef9daa1bac26f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-08-02-20-01-36.gh-issue-122559.2JlJr3.rst @@ -0,0 +1,6 @@ +Remove :meth:`!__reduce__` and :meth:`!__reduce_ex__` methods that always +raise :exc:`TypeError` in the C implementation of :class:`io.FileIO`, +:class:`io.BufferedReader`, :class:`io.BufferedWriter` and +:class:`io.BufferedRandom` and replace them with default +:meth:`!__getstate__` methods that raise :exc:`!TypeError`. +This restores fine details of behavior of Python 3.11 and older versions. diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 2189b1f3800415..4724e97982f349 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -2555,8 +2555,7 @@ static PyMethodDef bufferedreader_methods[] = { _IO__BUFFERED_TRUNCATE_METHODDEF _IO__BUFFERED___SIZEOF___METHODDEF - {"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS}, - {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O}, + {"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS}, {NULL, NULL} }; @@ -2615,8 +2614,7 @@ static PyMethodDef bufferedwriter_methods[] = { _IO__BUFFERED_TELL_METHODDEF _IO__BUFFERED___SIZEOF___METHODDEF - {"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS}, - {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O}, + {"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS}, {NULL, NULL} }; @@ -2733,8 +2731,7 @@ static PyMethodDef bufferedrandom_methods[] = { _IO_BUFFEREDWRITER_WRITE_METHODDEF _IO__BUFFERED___SIZEOF___METHODDEF - {"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS}, - {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O}, + {"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS}, {NULL, NULL} }; diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 0c5424954be1f7..8fcb27049d6c7c 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -1262,8 +1262,7 @@ static PyMethodDef fileio_methods[] = { _IO_FILEIO_ISATTY_METHODDEF {"_isatty_open_only", _io_FileIO_isatty_open_only, METH_NOARGS}, {"_dealloc_warn", fileio_dealloc_warn, METH_O, NULL}, - {"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS}, - {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O}, + {"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index a5b2ca7240a55f..86328e46a7b131 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -3366,8 +3366,7 @@ static PyMethodDef textiowrapper_methods[] = { _IO_TEXTIOWRAPPER_TELL_METHODDEF _IO_TEXTIOWRAPPER_TRUNCATE_METHODDEF - {"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS}, - {"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O}, + {"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS}, {NULL, NULL} }; _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com