Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

gh-122559: Synchronize C and Python implementation of the io module about pickling #122628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 4, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
gh-122559: Synchronize C and Python implementation of the io module a…
…bout 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
commit f6e66a6faf689a2f2d6ea1f65d70dafe6eaabc77
44 changes: 44 additions & 0 deletions 44 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.
serhiy-storchaka marked this conversation as resolved.
Show resolved Hide resolved
9 changes: 3 additions & 6 deletions 9 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 3 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 3 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
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.