Skip to content

Commit

Permalink
pythongh-122559: Synchronize C and Python implementation of the io mo…
Browse files Browse the repository at this point in the history
…dule about pickling

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.
  • Loading branch information
serhiy-storchaka committed Aug 2, 2024
1 parent d57f8a9 commit f6e66a6
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 10 deletions.
44 changes: 44 additions & 0 deletions Lib/test/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,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:

Expand Down Expand Up @@ -3919,6 +3941,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


class MemviewBytesIO(io.BytesIO):
'''A BytesIO object whose read method returns memoryviews
Expand Down
Original file line number Diff line number Diff line change
@@ -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 :meth:`!__getstatus__`
methods that always raise :exc:`!TypeError`. This restores fine details of
behavior of Python 3.11 and older versions.
9 changes: 3 additions & 6 deletions Modules/_io/bufferedio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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}
};

Expand Down Expand Up @@ -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}
};

Expand Down Expand Up @@ -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}
};

Expand Down
3 changes: 1 addition & 2 deletions Modules/_io/fileio.c
Original file line number Diff line number Diff line change
Expand Up @@ -1204,8 +1204,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 */
};

Expand Down
3 changes: 1 addition & 2 deletions Modules/_io/textio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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}
};

Expand Down

0 comments on commit f6e66a6

Please sign in to comment.