Index: Lib/io.py =================================================================== --- Lib/io.py (revision 67439) +++ Lib/io.py (working copy) @@ -1042,11 +1042,41 @@ self._flush_unlocked() except BlockingIOError as e: # We can't accept anything else. - # XXX Why not just let the exception pass through? + # Reraise this with 0 in the written field as none of the + # data passed to this call has been written. raise BlockingIOError(e.errno, e.strerror, 0) before = len(self._write_buf) - self._write_buf.extend(b) - written = len(self._write_buf) - before + bytes_to_consume = self.max_buffer_size - before + # b is an iterable of ints, it won't always support len(). + if hasattr(b, '__len__') and len(b) > bytes_to_consume: + try: + chunk = memoryview(b)[:bytes_to_consume] + except TypeError: + # No buffer API? Make intermediate slice copies instead. + chunk = b[:bytes_to_consume] + # Loop over the data, flushing it to the underlying raw IO + # stream in self.max_buffer_size chunks. + written = 0 + while chunk: + self._write_buf.extend(chunk) + try: + self._flush_unlocked() + except BlockingIOError as e: + written += e.characters_written + raise BlockingIOError(e.errno, e.strerror, written) + written += len(chunk) + if isinstance(chunk, memoryview): + chunk = memoryview(b)[written: + written + self.max_buffer_size] + else: + chunk = b[written:written + self.max_buffer_size] + assert not self._write_buf, "_write_buf should be empty" + else: + # This could go beyond self.max_buffer_size as we don't know + # the length of b. The alternative of iterating over it one + # byte at a time in python would be slow. + self._write_buf.extend(b) + written = len(self._write_buf) - before if len(self._write_buf) > self.buffer_size: try: self._flush_unlocked()