Skip to content

Commit 73c9326

Browse files
authored
gh-80109: Fix io.TextIOWrapper dropping the internal buffer during write() (GH-22535)
io.TextIOWrapper was dropping the internal decoding buffer during read() and write() calls.
1 parent e5d0316 commit 73c9326

File tree

4 files changed

+24
-8
lines changed

4 files changed

+24
-8
lines changed

Lib/_pyio.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2198,8 +2198,9 @@ def write(self, s):
21982198
self.buffer.write(b)
21992199
if self._line_buffering and (haslf or "\r" in s):
22002200
self.flush()
2201-
self._set_decoded_chars('')
2202-
self._snapshot = None
2201+
if self._snapshot is not None:
2202+
self._set_decoded_chars('')
2203+
self._snapshot = None
22032204
if self._decoder:
22042205
self._decoder.reset()
22052206
return length
@@ -2513,8 +2514,9 @@ def read(self, size=None):
25132514
# Read everything.
25142515
result = (self._get_decoded_chars() +
25152516
decoder.decode(self.buffer.read(), final=True))
2516-
self._set_decoded_chars('')
2517-
self._snapshot = None
2517+
if self._snapshot is not None:
2518+
self._set_decoded_chars('')
2519+
self._snapshot = None
25182520
return result
25192521
else:
25202522
# Keep reading chunks until we have size characters to return.

Lib/test/test_io.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3880,6 +3880,14 @@ def test_issue25862(self):
38803880
t.write('x')
38813881
t.tell()
38823882

3883+
def test_issue35928(self):
3884+
p = self.BufferedRWPair(self.BytesIO(b'foo\nbar\n'), self.BytesIO())
3885+
f = self.TextIOWrapper(p)
3886+
res = f.readline()
3887+
self.assertEqual(res, 'foo\n')
3888+
f.write(res)
3889+
self.assertEqual(res + f.readline(), 'foo\nbar\n')
3890+
38833891

38843892
class MemviewBytesIO(io.BytesIO):
38853893
'''A BytesIO object whose read method returns memoryviews
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:class:`io.TextIOWrapper` now correctly handles the decoding buffer after
2+
``read()`` and ``write()``.

Modules/_io/textio.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1762,8 +1762,10 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject *text)
17621762
}
17631763
}
17641764

1765-
textiowrapper_set_decoded_chars(self, NULL);
1766-
Py_CLEAR(self->snapshot);
1765+
if (self->snapshot != NULL) {
1766+
textiowrapper_set_decoded_chars(self, NULL);
1767+
Py_CLEAR(self->snapshot);
1768+
}
17671769

17681770
if (self->decoder) {
17691771
ret = PyObject_CallMethodNoArgs(self->decoder, &_Py_ID(reset));
@@ -1999,8 +2001,10 @@ _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n)
19992001
if (result == NULL)
20002002
goto fail;
20012003

2002-
textiowrapper_set_decoded_chars(self, NULL);
2003-
Py_CLEAR(self->snapshot);
2004+
if (self->snapshot != NULL) {
2005+
textiowrapper_set_decoded_chars(self, NULL);
2006+
Py_CLEAR(self->snapshot);
2007+
}
20042008
return result;
20052009
}
20062010
else {

0 commit comments

Comments
 (0)