Index: Lib/io.py =================================================================== --- Lib/io.py (revision 67119) +++ Lib/io.py (working copy) @@ -1062,8 +1062,9 @@ # We've hit max_buffer_size. We have to accept a # partial write and cut back our buffer. overage = len(self._write_buf) - self.max_buffer_size + written -= overage self._write_buf = self._write_buf[:self.max_buffer_size] - raise BlockingIOError(e.errno, e.strerror, overage) + raise BlockingIOError(e.errno, e.strerror, written) return written def truncate(self, pos=None): Index: Lib/test/test_io.py =================================================================== --- Lib/test/test_io.py (revision 67119) +++ Lib/test/test_io.py (working copy) @@ -476,6 +476,83 @@ # algorithm actually works, which we might change. Refactor # later. + def testWriteNonBlockingPreflush(self): + raw = MockNonBlockWriterIO((-1, -2)) + bufio = io.BufferedWriter(raw, 8, 16) + + data = b"a" * 10 + write_buf = bytearray(b"a" * 9) + bufio.write(data) + self.assertEqual(bufio._write_buf, write_buf) + try: + # Subsequent calls to write() try to flush the raw file. + bufio.write(data) + except io.BlockingIOError as e: + # Two more chars written at the raw level. + self.assertEqual(bufio._write_buf, write_buf[2:]) + # But write did not accept anything. + self.assertEqual(e.characters_written, 0) + else: + self.fail("BlockingIOError not raised") + + def testWriteNonBlockingOverage(self): + raw = MockNonBlockWriterIO((-1,)) + bufio = io.BufferedWriter(raw, 8, 16) + + data = b"a" * 20 + write_buf = bytearray(b"a" * 16) + try: + bufio.write(data) + except io.BlockingIOError as e: + # One char written at raw level and 16 in buffer. + self.assertEqual(bufio._write_buf, write_buf) + self.assertEqual(e.characters_written, 17) + else: + self.fail("BlockingIOError not raised") + + def testWriteNonBlockingOverageSmallWrite(self): + raw = MockNonBlockWriterIO((-1,)) + bufio = io.BufferedWriter(raw, 8, 16) + + data_1 = b"a" * 5 + # No write() call at raw level, data_1 goes straight to buffer. + bufio.write(data_1) + self.assertEqual(bufio._write_buf, data_1) + + data_2 = b"b" * 20 + write_buf = bytearray(b"a" * 4) + write_buf.extend(b"b" * 12) + try: + bufio.write(data_2) + except io.BlockingIOError as e: + self.assertEqual(bufio._write_buf, write_buf) + # One char written at raw level + # and 4 old chars + 12 new ones in buffer. + self.assertEqual(e.characters_written, 12) + else: + self.fail("BlockingIOError not raised") + + def testWriteNonBlockingOverageBigWrite(self): + raw = MockNonBlockWriterIO((-11,)) + bufio = io.BufferedWriter(raw, 8, 16) + + data_1 = b"a" * 5 + # No write() call at raw level, data_1 goes straight to buffer. + bufio.write(data_1) + self.assertEqual(bufio._write_buf, data_1) + + data_2 = b"b" * 30 + write_buf = bytearray(b"b" * 16) + try: + bufio.write(data_2) + except io.BlockingIOError as e: + self.assertEqual(bufio._write_buf, write_buf) + # Eleven chars written at raw level of which six were new + # and 16 new ones in buffer. + self.assertEqual(e.characters_written, 22) + else: + self.fail("BlockingIOError not raised") + def testFileno(self): rawio = MockRawIO((b"abc", b"d", b"efg")) bufio = io.BufferedWriter(rawio)