summaryrefslogtreecommitdiff
path: root/Lib/test/test_io.py
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2011-11-21 20:16:44 +0100
committerAntoine Pitrou <solipsis@pitrou.net>2011-11-21 20:16:44 +0100
commit5aa7df320f35cb0d5acb81ff313b1cc02480c9b8 (patch)
treea8ae2a0cee952722cb23c7af038f6fd5bd3a10cb /Lib/test/test_io.py
parent9d38b0dcb2a0ca6fcaaff96642e98448f55633a3 (diff)
downloadcpython-git-5aa7df320f35cb0d5acb81ff313b1cc02480c9b8.tar.gz
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.
Diffstat (limited to 'Lib/test/test_io.py')
-rw-r--r--Lib/test/test_io.py77
1 files changed, 74 insertions, 3 deletions
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index 003a4f34ba..b9972f3ac5 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -43,6 +43,10 @@ try:
import threading
except ImportError:
threading = None
+try:
+ import fcntl
+except ImportError:
+ fcntl = None
__metaclass__ = type
bytes = support.py3k_bytes
@@ -228,9 +232,14 @@ class MockNonBlockWriterIO:
except ValueError:
pass
else:
- self._blocker_char = None
- self._write_stack.append(b[:n])
- raise self.BlockingIOError(0, "test blocking", n)
+ if n > 0:
+ # write data up to the first blocker
+ self._write_stack.append(b[:n])
+ return n
+ else:
+ # cancel blocker and indicate would block
+ self._blocker_char = None
+ return None
self._write_stack.append(b)
return len(b)
@@ -2610,6 +2619,68 @@ class MiscIOTest(unittest.TestCase):
# baseline "io" module.
self._check_abc_inheritance(io)
+ @unittest.skipUnless(fcntl, 'fcntl required for this test')
+ def test_nonblock_pipe_write_bigbuf(self):
+ self._test_nonblock_pipe_write(16*1024)
+
+ @unittest.skipUnless(fcntl, 'fcntl required for this test')
+ def test_nonblock_pipe_write_smallbuf(self):
+ self._test_nonblock_pipe_write(1024)
+
+ def _set_non_blocking(self, fd):
+ flags = fcntl.fcntl(fd, fcntl.F_GETFL)
+ self.assertNotEqual(flags, -1)
+ res = fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
+ self.assertEqual(res, 0)
+
+ def _test_nonblock_pipe_write(self, bufsize):
+ sent = []
+ received = []
+ r, w = os.pipe()
+ self._set_non_blocking(r)
+ self._set_non_blocking(w)
+
+ # To exercise all code paths in the C implementation we need
+ # to play with buffer sizes. For instance, if we choose a
+ # buffer size less than or equal to _PIPE_BUF (4096 on Linux)
+ # then we will never get a partial write of the buffer.
+ rf = self.open(r, mode='rb', closefd=True, buffering=bufsize)
+ wf = self.open(w, mode='wb', closefd=True, buffering=bufsize)
+
+ with rf, wf:
+ for N in 9999, 73, 7574:
+ try:
+ i = 0
+ while True:
+ msg = bytes([i % 26 + 97]) * N
+ sent.append(msg)
+ wf.write(msg)
+ i += 1
+
+ except self.BlockingIOError as e:
+ self.assertEqual(e.args[0], errno.EAGAIN)
+ sent[-1] = sent[-1][:e.characters_written]
+ received.append(rf.read())
+ msg = b'BLOCKED'
+ wf.write(msg)
+ sent.append(msg)
+
+ while True:
+ try:
+ wf.flush()
+ break
+ except self.BlockingIOError as e:
+ self.assertEqual(e.args[0], errno.EAGAIN)
+ self.assertEqual(e.characters_written, 0)
+ received.append(rf.read())
+
+ received += iter(rf.read, None)
+
+ sent, received = b''.join(sent), b''.join(received)
+ self.assertTrue(sent == received)
+ self.assertTrue(wf.closed)
+ self.assertTrue(rf.closed)
+
class CMiscIOTest(MiscIOTest):
io = io