]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-80109: Fix io.TextIOWrapper dropping the internal buffer during write() (GH-22535)
authorZackery Spytz <zspytz@gmail.com>
Mon, 8 Jan 2024 10:33:34 +0000 (02:33 -0800)
committerGitHub <noreply@github.com>
Mon, 8 Jan 2024 10:33:34 +0000 (12:33 +0200)
io.TextIOWrapper was dropping the internal decoding buffer
during read() and write() calls.

Lib/_pyio.py
Lib/test/test_io.py
Misc/NEWS.d/next/Library/2020-10-03-23-47-28.bpo-35928.E0iPAa.rst [new file with mode: 0644]
Modules/_io/textio.c

index 32698abac78d25adad515bb3d425769122f8c566..df2c29bfa9caeed653d382902b15e6ab7f428734 100644 (file)
@@ -2198,8 +2198,9 @@ class TextIOWrapper(TextIOBase):
         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 @@ class TextIOWrapper(TextIOBase):
             # 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.
index 1d78876f2a1c844bed5d2eee31496f3ae660e666..ca31b9dad2631a266bba460f7fb4d10e1011b67a 100644 (file)
@@ -3880,6 +3880,14 @@ class TextIOWrapperTest(unittest.TestCase):
         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 (file)
index 0000000..c63e616
--- /dev/null
@@ -0,0 +1,2 @@
+:class:`io.TextIOWrapper` now correctly handles the decoding buffer after
+``read()`` and ``write()``.
index 4507930c14bb50d9d83e42c52c57141daeabf8a4..e93c3e091549db5f7b73b4b68092c5e8c6ae0068 100644 (file)
@@ -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 {