changeset: 90594:37d0c41ed8ad parent: 90592:17689e43839a parent: 90593:39f2a78f4357 user: Antoine Pitrou date: Fri May 09 00:31:32 2014 +0200 files: Lib/test/test_io.py Misc/ACKS Misc/NEWS Modules/_io/textio.c description: Issue #21396: Fix TextIOWrapper(..., write_through=True) to not force a flush() on the underlying binary stream. Patch by akira. diff -r 17689e43839a -r 37d0c41ed8ad Lib/test/test_io.py --- a/Lib/test/test_io.py Thu May 08 23:08:51 2014 +0100 +++ b/Lib/test/test_io.py Fri May 09 00:31:32 2014 +0200 @@ -2615,6 +2615,38 @@ 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 -r 17689e43839a -r 37d0c41ed8ad Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py Thu May 08 23:08:51 2014 +0100 +++ b/Lib/test/test_subprocess.py Fri May 09 00:31:32 2014 +0200 @@ -786,6 +786,7 @@ 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 -r 17689e43839a -r 37d0c41ed8ad Misc/NEWS --- a/Misc/NEWS Thu May 08 23:08:51 2014 +0100 +++ b/Misc/NEWS Fri May 09 00:31:32 2014 +0200 @@ -69,6 +69,9 @@ Library ------- +- Issue #21396: Fix TextIOWrapper(..., write_through=True) to not force a + flush() on the underlying binary stream. Patch by akira. + - Issue #18314: Unlink now removes junctions on Windows. Patch by Kim Gräsman - Issue #21088: Bugfix for curses.window.addch() regression in 3.4.0. diff -r 17689e43839a -r 37d0c41ed8ad Modules/_io/textio.c --- a/Modules/_io/textio.c Thu May 08 23:08:51 2014 +0100 +++ b/Modules/_io/textio.c Fri May 09 00:31:32 2014 +0200 @@ -1297,7 +1297,7 @@ 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 @@ } 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 @@ } 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; }