Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0d35741

Browse files
committed
(Merge 3.4) 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.
2 parents 68f4116 + cc996b5 commit 0d35741

3 files changed

Lines changed: 32 additions & 8 deletions

File tree

Doc/library/asyncio-subprocess.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ Process
191191
process, or ``None``, if no data should be sent to the child. The type
192192
of *input* must be bytes.
193193

194+
If a :exc:`BrokenPipeError` is raised when writing *input* into stdin,
195+
the exception is ignored. It occurs when the process exits before all
196+
data are written into stdin.
197+
194198
:meth:`communicate` returns a tuple ``(stdoutdata, stderrdata)``.
195199

196200
Note that if you want to send data to the process's stdin, you need to
@@ -205,6 +209,9 @@ Process
205209

206210
This method is a :ref:`coroutine <coroutine>`.
207211

212+
.. versionchanged:: 3.4.2
213+
The method now ignores :exc:`BrokenPipeError`.
214+
208215
.. method:: kill()
209216

210217
Kills the child. On Posix OSs the function sends :py:data:`SIGKILL` to

Lib/asyncio/subprocess.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,11 @@ def _feed_stdin(self, input):
143143
if self._loop.get_debug():
144144
logger.debug('%r communicate: feed stdin (%s bytes)',
145145
self, len(input))
146-
yield from self.stdin.drain()
146+
try:
147+
yield from self.stdin.drain()
148+
except BrokenPipeError:
149+
# ignore BrokenPipeError
150+
pass
147151

148152
if self._loop.get_debug():
149153
logger.debug('%r communicate: close stdin', self)

Lib/test/test_asyncio/test_subprocess.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@
1111
# Program blocking
1212
PROGRAM_BLOCKED = [sys.executable, '-c', 'import time; time.sleep(3600)']
1313

14-
# Program sleeping during 1 second
15-
PROGRAM_SLEEP_1SEC = [sys.executable, '-c', 'import time; time.sleep(1)']
16-
1714
# Program copying input to output
1815
PROGRAM_CAT = [
1916
sys.executable, '-c',
@@ -118,16 +115,32 @@ def test_send_signal(self):
118115
returncode = self.loop.run_until_complete(proc.wait())
119116
self.assertEqual(-signal.SIGHUP, returncode)
120117

121-
def test_broken_pipe(self):
118+
def prepare_broken_pipe_test(self):
119+
# buffer large enough to feed the whole pipe buffer
122120
large_data = b'x' * support.PIPE_MAX_SIZE
123121

122+
# the program ends before the stdin can be feeded
124123
create = asyncio.create_subprocess_exec(
125-
*PROGRAM_SLEEP_1SEC,
124+
sys.executable, '-c', 'pass',
126125
stdin=subprocess.PIPE,
127126
loop=self.loop)
128127
proc = self.loop.run_until_complete(create)
129-
with self.assertRaises(BrokenPipeError):
130-
self.loop.run_until_complete(proc.communicate(large_data))
128+
return (proc, large_data)
129+
130+
def test_stdin_broken_pipe(self):
131+
proc, large_data = self.prepare_broken_pipe_test()
132+
133+
# drain() must raise BrokenPipeError
134+
proc.stdin.write(large_data)
135+
self.assertRaises(BrokenPipeError,
136+
self.loop.run_until_complete, proc.stdin.drain())
137+
self.loop.run_until_complete(proc.wait())
138+
139+
def test_communicate_ignore_broken_pipe(self):
140+
proc, large_data = self.prepare_broken_pipe_test()
141+
142+
# communicate() must ignore BrokenPipeError when feeding stdin
143+
self.loop.run_until_complete(proc.communicate(large_data))
131144
self.loop.run_until_complete(proc.wait())
132145

133146

0 commit comments

Comments
 (0)