mirror of
https://github.com/python/cpython.git
synced 2025-08-29 13:15:11 +00:00
Issue #13322: Fix BufferedWriter.write() to ensure that BlockingIOError is
raised when the wrapped raw file is non-blocking and the write would block. Previous code assumed that the raw write() would raise BlockingIOError, but RawIOBase.write() is defined to returned None when the call would block. Patch by sbt.
This commit is contained in:
parent
a04b39b261
commit
58fcf9f801
5 changed files with 152 additions and 62 deletions
51
Lib/_pyio.py
51
Lib/_pyio.py
|
@ -6,6 +6,7 @@ import os
|
|||
import abc
|
||||
import codecs
|
||||
import warnings
|
||||
import errno
|
||||
# Import _thread instead of threading to reduce startup cost
|
||||
try:
|
||||
from _thread import allocate_lock as Lock
|
||||
|
@ -720,8 +721,11 @@ class _BufferedIOMixin(BufferedIOBase):
|
|||
|
||||
def close(self):
|
||||
if self.raw is not None and not self.closed:
|
||||
self.flush()
|
||||
self.raw.close()
|
||||
try:
|
||||
# may raise BlockingIOError or BrokenPipeError etc
|
||||
self.flush()
|
||||
finally:
|
||||
self.raw.close()
|
||||
|
||||
def detach(self):
|
||||
if self.raw is None:
|
||||
|
@ -1080,13 +1084,9 @@ class BufferedWriter(_BufferedIOMixin):
|
|||
# XXX we can implement some more tricks to try and avoid
|
||||
# partial writes
|
||||
if len(self._write_buf) > self.buffer_size:
|
||||
# We're full, so let's pre-flush the buffer
|
||||
try:
|
||||
self._flush_unlocked()
|
||||
except BlockingIOError as e:
|
||||
# We can't accept anything else.
|
||||
# XXX Why not just let the exception pass through?
|
||||
raise BlockingIOError(e.errno, e.strerror, 0)
|
||||
# We're full, so let's pre-flush the buffer. (This may
|
||||
# raise BlockingIOError with characters_written == 0.)
|
||||
self._flush_unlocked()
|
||||
before = len(self._write_buf)
|
||||
self._write_buf.extend(b)
|
||||
written = len(self._write_buf) - before
|
||||
|
@ -1117,24 +1117,23 @@ class BufferedWriter(_BufferedIOMixin):
|
|||
def _flush_unlocked(self):
|
||||
if self.closed:
|
||||
raise ValueError("flush of closed file")
|
||||
written = 0
|
||||
try:
|
||||
while self._write_buf:
|
||||
try:
|
||||
n = self.raw.write(self._write_buf)
|
||||
except IOError as e:
|
||||
if e.errno != EINTR:
|
||||
raise
|
||||
continue
|
||||
if n > len(self._write_buf) or n < 0:
|
||||
raise IOError("write() returned incorrect number of bytes")
|
||||
del self._write_buf[:n]
|
||||
written += n
|
||||
except BlockingIOError as e:
|
||||
n = e.characters_written
|
||||
while self._write_buf:
|
||||
try:
|
||||
n = self.raw.write(self._write_buf)
|
||||
except BlockingIOError:
|
||||
raise RuntimeError("self.raw should implement RawIOBase: it "
|
||||
"should not raise BlockingIOError")
|
||||
except IOError as e:
|
||||
if e.errno != EINTR:
|
||||
raise
|
||||
continue
|
||||
if n is None:
|
||||
raise BlockingIOError(
|
||||
errno.EAGAIN,
|
||||
"write could not complete without blocking", 0)
|
||||
if n > len(self._write_buf) or n < 0:
|
||||
raise IOError("write() returned incorrect number of bytes")
|
||||
del self._write_buf[:n]
|
||||
written += n
|
||||
raise BlockingIOError(e.errno, e.strerror, written)
|
||||
|
||||
def tell(self):
|
||||
return _BufferedIOMixin.tell(self) + len(self._write_buf)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue