Skip to content

Commit f6e66a6

Browse files
pythongh-122559: Synchronize C and Python implementation of the io module 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.
1 parent d57f8a9 commit f6e66a6

File tree

5 files changed

+55
-10
lines changed

5 files changed

+55
-10
lines changed

Lib/test/test_io.py

+44
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,28 @@ def test_readonly_attributes(self):
13361336
with self.assertRaises(AttributeError):
13371337
buf.raw = x
13381338

1339+
def test_pickling_subclass(self):
1340+
global MyBufferedIO
1341+
class MyBufferedIO(self.tp):
1342+
def __init__(self, raw, tag):
1343+
super().__init__(raw)
1344+
self.tag = tag
1345+
def __getstate__(self):
1346+
return self.tag, self.raw.getvalue()
1347+
def __setstate__(slf, state):
1348+
tag, value = state
1349+
slf.__init__(self.BytesIO(value), tag)
1350+
1351+
raw = self.BytesIO(b'data')
1352+
buf = MyBufferedIO(raw, tag='ham')
1353+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1354+
with self.subTest(protocol=proto):
1355+
pickled = pickle.dumps(buf, proto)
1356+
newbuf = pickle.loads(pickled)
1357+
self.assertEqual(newbuf.raw.getvalue(), b'data')
1358+
self.assertEqual(newbuf.tag, 'ham')
1359+
del MyBufferedIO
1360+
13391361

13401362
class SizeofTest:
13411363

@@ -3919,6 +3941,28 @@ def test_issue35928(self):
39193941
f.write(res)
39203942
self.assertEqual(res + f.readline(), 'foo\nbar\n')
39213943

3944+
def test_pickling_subclass(self):
3945+
global MyTextIO
3946+
class MyTextIO(self.TextIOWrapper):
3947+
def __init__(self, raw, tag):
3948+
super().__init__(raw)
3949+
self.tag = tag
3950+
def __getstate__(self):
3951+
return self.tag, self.buffer.getvalue()
3952+
def __setstate__(slf, state):
3953+
tag, value = state
3954+
slf.__init__(self.BytesIO(value), tag)
3955+
3956+
raw = self.BytesIO(b'data')
3957+
txt = MyTextIO(raw, 'ham')
3958+
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
3959+
with self.subTest(protocol=proto):
3960+
pickled = pickle.dumps(txt, proto)
3961+
newtxt = pickle.loads(pickled)
3962+
self.assertEqual(newtxt.buffer.getvalue(), b'data')
3963+
self.assertEqual(newtxt.tag, 'ham')
3964+
del MyTextIO
3965+
39223966

39233967
class MemviewBytesIO(io.BytesIO):
39243968
'''A BytesIO object whose read method returns memoryviews
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Remove :meth:`!__reduce__` and :meth:`!__reduce_ex__` methods that always
2+
raise :exc:`TypeError` in the C implementation of :class:`io.FileIO`,
3+
:class:`io.BufferedReader`, :class:`io.BufferedWriter` and
4+
:class:`io.BufferedRandom` and replace them with :meth:`!__getstatus__`
5+
methods that always raise :exc:`!TypeError`. This restores fine details of
6+
behavior of Python 3.11 and older versions.

Modules/_io/bufferedio.c

+3-6
Original file line numberDiff line numberDiff line change
@@ -2530,8 +2530,7 @@ static PyMethodDef bufferedreader_methods[] = {
25302530
_IO__BUFFERED_TRUNCATE_METHODDEF
25312531
_IO__BUFFERED___SIZEOF___METHODDEF
25322532

2533-
{"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS},
2534-
{"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O},
2533+
{"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS},
25352534
{NULL, NULL}
25362535
};
25372536

@@ -2590,8 +2589,7 @@ static PyMethodDef bufferedwriter_methods[] = {
25902589
_IO__BUFFERED_TELL_METHODDEF
25912590
_IO__BUFFERED___SIZEOF___METHODDEF
25922591

2593-
{"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS},
2594-
{"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O},
2592+
{"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS},
25952593
{NULL, NULL}
25962594
};
25972595

@@ -2708,8 +2706,7 @@ static PyMethodDef bufferedrandom_methods[] = {
27082706
_IO_BUFFEREDWRITER_WRITE_METHODDEF
27092707
_IO__BUFFERED___SIZEOF___METHODDEF
27102708

2711-
{"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS},
2712-
{"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O},
2709+
{"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS},
27132710
{NULL, NULL}
27142711
};
27152712

Modules/_io/fileio.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -1204,8 +1204,7 @@ static PyMethodDef fileio_methods[] = {
12041204
_IO_FILEIO_FILENO_METHODDEF
12051205
_IO_FILEIO_ISATTY_METHODDEF
12061206
{"_dealloc_warn", (PyCFunction)fileio_dealloc_warn, METH_O, NULL},
1207-
{"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS},
1208-
{"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O},
1207+
{"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS},
12091208
{NULL, NULL} /* sentinel */
12101209
};
12111210

Modules/_io/textio.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -3350,8 +3350,7 @@ static PyMethodDef textiowrapper_methods[] = {
33503350
_IO_TEXTIOWRAPPER_TELL_METHODDEF
33513351
_IO_TEXTIOWRAPPER_TRUNCATE_METHODDEF
33523352

3353-
{"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS},
3354-
{"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O},
3353+
{"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS},
33553354
{NULL, NULL}
33563355
};
33573356

0 commit comments

Comments
 (0)