From c644e7c39f7adf0ed783e128b0278665133bf523 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 9 May 2014 00:24:50 +0200 Subject: [PATCH] Issue #21396: Fix TextIOWrapper(..., write_through=True) to not force a flush() on the underlying binary stream. Patch by akira. --- Lib/test/test_io.py | 32 ++++++++++++++++++++++++++++++++ Lib/test/test_subprocess.py | 1 + Misc/ACKS | 1 + Misc/NEWS | 6 ++++++ Modules/_io/textio.c | 9 +++++---- 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 267537fdeb7..ef1e05622bb 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -2615,6 +2615,38 @@ class TextIOWrapperTest(unittest.TestCase): txt.write('5') self.assertEqual(b''.join(raw._write_stack), b'123\n45') + def test_bufio_write_through(self): + # Issue #21396: write_through=True doesn't force a flush() + # on the underlying binary buffered object. + flush_called, write_called = [], [] + class BufferedWriter(self.BufferedWriter): + def flush(self, *args, **kwargs): + flush_called.append(True) + return super().flush(*args, **kwargs) + def write(self, *args, **kwargs): + write_called.append(True) + return super().write(*args, **kwargs) + + rawio = self.BytesIO() + data = b"a" + bufio = BufferedWriter(rawio, len(data)*2) + textio = self.TextIOWrapper(bufio, encoding='ascii', + write_through=True) + # write to the buffered io but don't overflow the buffer + text = data.decode('ascii') + textio.write(text) + + # buffer.flush is not called with write_through=True + self.assertFalse(flush_called) + # buffer.write *is* called with write_through=True + self.assertTrue(write_called) + self.assertEqual(rawio.getvalue(), b"") # no flush + + write_called = [] # reset + textio.write(text * 10) # total content is larger than bufio buffer + self.assertTrue(write_called) + self.assertEqual(rawio.getvalue(), data * 11) # all flushed + def test_read_nonbytes(self): # Issue #17106 # Crash when underlying read() returns non-bytes diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 4c8d4937321..32ffb5fa4ba 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -786,6 +786,7 @@ class ProcessTestCase(BaseTestCase): stdout=subprocess.PIPE, universal_newlines=1) p.stdin.write("line1\n") + p.stdin.flush() self.assertEqual(p.stdout.readline(), "line1\n") p.stdin.write("line3\n") p.stdin.close() diff --git a/Misc/ACKS b/Misc/ACKS index 4068836cf1c..75fb70657bb 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -24,6 +24,7 @@ Jim Ahlstrom Farhan Ahmad Matthew Ahrens Nir Aides +Akira Yaniv Aknin Jyrki Alakuijala Steve Alexander diff --git a/Misc/NEWS b/Misc/NEWS index 96f3cb78775..29634fcf407 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -7,6 +7,12 @@ What's New in Python 3.4.1? Release date: TBA +Library +------- + +- Issue #21396: Fix TextIOWrapper(..., write_through=True) to not force a + flush() on the underlying binary stream. Patch by akira. + Tests ----- diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 5739bc4d01d..ba5789d3c73 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1297,7 +1297,7 @@ textiowrapper_write(textio *self, PyObject *args) PyObject *b; Py_ssize_t textlen; int haslf = 0; - int needflush = 0; + int needflush = 0, text_needflush = 0; CHECK_INITIALIZED(self); @@ -1331,8 +1331,8 @@ textiowrapper_write(textio *self, PyObject *args) } if (self->write_through) - needflush = 1; - else if (self->line_buffering && + text_needflush = 1; + if (self->line_buffering && (haslf || PyUnicode_FindChar(text, '\r', 0, PyUnicode_GET_LENGTH(text), 1) != -1)) needflush = 1; @@ -1363,7 +1363,8 @@ textiowrapper_write(textio *self, PyObject *args) } self->pending_bytes_count += PyBytes_GET_SIZE(b); Py_DECREF(b); - if (self->pending_bytes_count > self->chunk_size || needflush) { + if (self->pending_bytes_count > self->chunk_size || needflush || + text_needflush) { if (_textiowrapper_writeflush(self) < 0) return NULL; }