From: Serhiy Storchaka Date: Sun, 4 May 2025 16:04:09 +0000 (+0300) Subject: [3.13] gh-122559: Synchronize C and Python implementation of the io module about... X-Git-Tag: v3.13.4~172 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=973e2d3e29d4994bf01683e607d2a448d3d49f4f;p=thirdparty%2FPython%2Fcpython.git [3.13] gh-122559: Synchronize C and Python implementation of the io module about pickling (GH-122628) (GH-133381) 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. (cherry picked from commit e9253ebf74433de5ae6d7f1bce693a3a1173b3b1) --- diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 112495512e19..4f06fd9db032 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -1349,6 +1349,28 @@ class CommonBufferedTests: 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: @@ -3932,6 +3954,28 @@ class TextIOWrapperTest(unittest.TestCase): 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 + class MemviewBytesIO(io.BytesIO): '''A BytesIO object whose read method returns memoryviews 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 000000000000..4ef9daa1bac2 --- /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 e45323c93a17..050878c6da6e 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -2530,8 +2530,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} }; @@ -2590,8 +2589,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} }; @@ -2708,8 +2706,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 b5129ffcbffd..545eb4acf9b1 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -1178,8 +1178,7 @@ static PyMethodDef fileio_methods[] = { _IO_FILEIO_FILENO_METHODDEF _IO_FILEIO_ISATTY_METHODDEF {"_dealloc_warn", (PyCFunction)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 c162d8106ec1..e10689d05a84 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -3350,8 +3350,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} };