diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2014-07-17 10:25:27 (GMT) |
---|---|---|
committer | Victor Stinner <victor.stinner@gmail.com> | 2014-07-17 10:25:27 (GMT) |
commit | cc996b57890a251cef83101d5cfcbc58179cba5f (patch) | |
tree | 6e4192cd348196825f50ddc81dad99edbcd0902b | |
parent | 38bf87c7f285b9ad03feea765ddad9f607876591 (diff) | |
download | cpython-cc996b57890a251cef83101d5cfcbc58179cba5f.zip cpython-cc996b57890a251cef83101d5cfcbc58179cba5f.tar.gz cpython-cc996b57890a251cef83101d5cfcbc58179cba5f.tar.bz2 |
asyncio, tulip issue 190: Process.communicate() must ignore BrokenPipeError
If you want to handle the BrokenPipeError, you can easily reimplement
communicate().
Add also a unit test to ensure that stdin.write() + stdin.drain() raises
BrokenPipeError.
-rw-r--r-- | Doc/library/asyncio-subprocess.rst | 7 | ||||
-rw-r--r-- | Lib/asyncio/subprocess.py | 6 | ||||
-rw-r--r-- | Lib/test/test_asyncio/test_subprocess.py | 27 |
3 files changed, 32 insertions, 8 deletions
diff --git a/Doc/library/asyncio-subprocess.rst b/Doc/library/asyncio-subprocess.rst index cba3aae..f0ffbf0 100644 --- a/Doc/library/asyncio-subprocess.rst +++ b/Doc/library/asyncio-subprocess.rst @@ -191,6 +191,10 @@ Process process, or ``None``, if no data should be sent to the child. The type of *input* must be bytes. + If a :exc:`BrokenPipeError` is raised when writing *input* into stdin, + the exception is ignored. It occurs when the process exits before all + data are written into stdin. + :meth:`communicate` returns a tuple ``(stdoutdata, stderrdata)``. Note that if you want to send data to the process's stdin, you need to @@ -205,6 +209,9 @@ Process This method is a :ref:`coroutine <coroutine>`. + .. versionchanged:: 3.4.2 + The method now ignores :exc:`BrokenPipeError`. + .. method:: kill() Kills the child. On Posix OSs the function sends :py:data:`SIGKILL` to diff --git a/Lib/asyncio/subprocess.py b/Lib/asyncio/subprocess.py index 12902f1..23d6b4d 100644 --- a/Lib/asyncio/subprocess.py +++ b/Lib/asyncio/subprocess.py @@ -143,7 +143,11 @@ class Process: if self._loop.get_debug(): logger.debug('%r communicate: feed stdin (%s bytes)', self, len(input)) - yield from self.stdin.drain() + try: + yield from self.stdin.drain() + except BrokenPipeError: + # ignore BrokenPipeError + pass if self._loop.get_debug(): logger.debug('%r communicate: close stdin', self) diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index 3204d42..e41cabe 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -11,9 +11,6 @@ if sys.platform != 'win32': # Program blocking PROGRAM_BLOCKED = [sys.executable, '-c', 'import time; time.sleep(3600)'] -# Program sleeping during 1 second -PROGRAM_SLEEP_1SEC = [sys.executable, '-c', 'import time; time.sleep(1)'] - # Program copying input to output PROGRAM_CAT = [ sys.executable, '-c', @@ -118,16 +115,32 @@ class SubprocessMixin: returncode = self.loop.run_until_complete(proc.wait()) self.assertEqual(-signal.SIGHUP, returncode) - def test_broken_pipe(self): + def prepare_broken_pipe_test(self): + # buffer large enough to feed the whole pipe buffer large_data = b'x' * support.PIPE_MAX_SIZE + # the program ends before the stdin can be feeded create = asyncio.create_subprocess_exec( - *PROGRAM_SLEEP_1SEC, + sys.executable, '-c', 'pass', stdin=subprocess.PIPE, loop=self.loop) proc = self.loop.run_until_complete(create) - with self.assertRaises(BrokenPipeError): - self.loop.run_until_complete(proc.communicate(large_data)) + return (proc, large_data) + + def test_stdin_broken_pipe(self): + proc, large_data = self.prepare_broken_pipe_test() + + # drain() must raise BrokenPipeError + proc.stdin.write(large_data) + self.assertRaises(BrokenPipeError, + self.loop.run_until_complete, proc.stdin.drain()) + self.loop.run_until_complete(proc.wait()) + + def test_communicate_ignore_broken_pipe(self): + proc, large_data = self.prepare_broken_pipe_test() + + # communicate() must ignore BrokenPipeError when feeding stdin + self.loop.run_until_complete(proc.communicate(large_data)) self.loop.run_until_complete(proc.wait()) |