https://github.com/python/cpython/commit/73c93265634257b1488262097e024c1727260cfd
commit: 73c93265634257b1488262097e024c1727260cfd
branch: main
author: Zackery Spytz <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2024-01-08T12:33:34+02:00
summary:

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.

files:
A Misc/NEWS.d/next/Library/2020-10-03-23-47-28.bpo-35928.E0iPAa.rst
M Lib/_pyio.py
M Lib/test/test_io.py
M Modules/_io/textio.c

diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index 32698abac78d25..df2c29bfa9caee 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -2198,8 +2198,9 @@ def write(self, s):
         self.buffer.write(b)
         if self._line_buffering and (haslf or "\r" in s):
             self.flush()
-        self._set_decoded_chars('')
-        self._snapshot = None
+        if self._snapshot is not None:
+            self._set_decoded_chars('')
+            self._snapshot = None
         if self._decoder:
             self._decoder.reset()
         return length
@@ -2513,8 +2514,9 @@ def read(self, size=None):
             # Read everything.
             result = (self._get_decoded_chars() +
                       decoder.decode(self.buffer.read(), final=True))
-            self._set_decoded_chars('')
-            self._snapshot = None
+            if self._snapshot is not None:
+                self._set_decoded_chars('')
+                self._snapshot = None
             return result
         else:
             # Keep reading chunks until we have size characters to return.
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index 1d78876f2a1c84..ca31b9dad2631a 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -3880,6 +3880,14 @@ def test_issue25862(self):
         t.write('x')
         t.tell()
 
+    def test_issue35928(self):
+        p = self.BufferedRWPair(self.BytesIO(b'foo\nbar\n'), self.BytesIO())
+        f = self.TextIOWrapper(p)
+        res = f.readline()
+        self.assertEqual(res, 'foo\n')
+        f.write(res)
+        self.assertEqual(res + f.readline(), 'foo\nbar\n')
+
 
 class MemviewBytesIO(io.BytesIO):
     '''A BytesIO object whose read method returns memoryviews
diff --git a/Misc/NEWS.d/next/Library/2020-10-03-23-47-28.bpo-35928.E0iPAa.rst 
b/Misc/NEWS.d/next/Library/2020-10-03-23-47-28.bpo-35928.E0iPAa.rst
new file mode 100644
index 00000000000000..c63e616458a356
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-10-03-23-47-28.bpo-35928.E0iPAa.rst
@@ -0,0 +1,2 @@
+:class:`io.TextIOWrapper` now correctly handles the decoding buffer after
+``read()`` and ``write()``.
diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c
index 4507930c14bb50..e93c3e091549db 100644
--- a/Modules/_io/textio.c
+++ b/Modules/_io/textio.c
@@ -1762,8 +1762,10 @@ _io_TextIOWrapper_write_impl(textio *self, PyObject 
*text)
         }
     }
 
-    textiowrapper_set_decoded_chars(self, NULL);
-    Py_CLEAR(self->snapshot);
+    if (self->snapshot != NULL) {
+        textiowrapper_set_decoded_chars(self, NULL);
+        Py_CLEAR(self->snapshot);
+    }
 
     if (self->decoder) {
         ret = PyObject_CallMethodNoArgs(self->decoder, &_Py_ID(reset));
@@ -1999,8 +2001,10 @@ _io_TextIOWrapper_read_impl(textio *self, Py_ssize_t n)
         if (result == NULL)
             goto fail;
 
-        textiowrapper_set_decoded_chars(self, NULL);
-        Py_CLEAR(self->snapshot);
+        if (self->snapshot != NULL) {
+            textiowrapper_set_decoded_chars(self, NULL);
+            Py_CLEAR(self->snapshot);
+        }
         return result;
     }
     else {

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to