[Python-checkins] cpython (3.4): asyncio, tulip issue 190: Process.communicate() must ignore BrokenPipeError

victor.stinner python-checkins at python.org
Thu Jul 17 12:48:44 CEST 2014


http://hg.python.org/cpython/rev/244ab7f41065
changeset:   91713:244ab7f41065
branch:      3.4
parent:      91711:0045eec1e247
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Thu Jul 17 12:25:27 2014 +0200
summary:
  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.

files:
  Doc/library/asyncio-subprocess.rst       |   7 +++
  Lib/asyncio/subprocess.py                |   6 ++-
  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
--- a/Doc/library/asyncio-subprocess.rst
+++ b/Doc/library/asyncio-subprocess.rst
@@ -191,6 +191,10 @@
       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 @@
 
       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
--- a/Lib/asyncio/subprocess.py
+++ b/Lib/asyncio/subprocess.py
@@ -143,7 +143,11 @@
         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
--- a/Lib/test/test_asyncio/test_subprocess.py
+++ b/Lib/test/test_asyncio/test_subprocess.py
@@ -11,9 +11,6 @@
 # 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 @@
         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())
 
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list