mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
Fixes Issue #26373: subprocess.Popen.communicate now correctly ignores
BrokenPipeError when the child process dies before .communicate() is called in more (all?) circumstances.
This commit is contained in:
parent
ead9bfc5c3
commit
1ef8c7e886
3 changed files with 69 additions and 5 deletions
|
@ -1,4 +1,5 @@
|
|||
import unittest
|
||||
from unittest import mock
|
||||
from test.support import script_helper
|
||||
from test import support
|
||||
import subprocess
|
||||
|
@ -1240,6 +1241,52 @@ class ProcessTestCase(BaseTestCase):
|
|||
fds_after_exception = os.listdir(fd_directory)
|
||||
self.assertEqual(fds_before_popen, fds_after_exception)
|
||||
|
||||
def test_communicate_BrokenPipeError_stdin_close(self):
|
||||
# By not setting stdout or stderr or a timeout we force the fast path
|
||||
# that just calls _stdin_write() internally due to our mock.
|
||||
proc = subprocess.Popen([sys.executable, '-c', 'pass'])
|
||||
with proc, mock.patch.object(proc, 'stdin') as mock_proc_stdin:
|
||||
mock_proc_stdin.close.side_effect = BrokenPipeError
|
||||
proc.communicate() # Should swallow BrokenPipeError from close.
|
||||
mock_proc_stdin.close.assert_called_with()
|
||||
|
||||
def test_communicate_BrokenPipeError_stdin_write(self):
|
||||
# By not setting stdout or stderr or a timeout we force the fast path
|
||||
# that just calls _stdin_write() internally due to our mock.
|
||||
proc = subprocess.Popen([sys.executable, '-c', 'pass'])
|
||||
with proc, mock.patch.object(proc, 'stdin') as mock_proc_stdin:
|
||||
mock_proc_stdin.write.side_effect = BrokenPipeError
|
||||
proc.communicate(b'stuff') # Should swallow the BrokenPipeError.
|
||||
mock_proc_stdin.write.assert_called_once_with(b'stuff')
|
||||
mock_proc_stdin.close.assert_called_once_with()
|
||||
|
||||
def test_communicate_BrokenPipeError_stdin_flush(self):
|
||||
# Setting stdin and stdout forces the ._communicate() code path.
|
||||
# python -h exits faster than python -c pass (but spams stdout).
|
||||
proc = subprocess.Popen([sys.executable, '-h'],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
with proc, mock.patch.object(proc, 'stdin') as mock_proc_stdin, \
|
||||
open('/dev/null', 'wb') as dev_null:
|
||||
mock_proc_stdin.flush.side_effect = BrokenPipeError
|
||||
# because _communicate registers a selector using proc.stdin...
|
||||
mock_proc_stdin.fileno.return_value = dev_null.fileno()
|
||||
# _communicate() should swallow BrokenPipeError from flush.
|
||||
proc.communicate(b'stuff')
|
||||
mock_proc_stdin.flush.assert_called_once_with()
|
||||
|
||||
def test_communicate_BrokenPipeError_stdin_close_with_timeout(self):
|
||||
# Setting stdin and stdout forces the ._communicate() code path.
|
||||
# python -h exits faster than python -c pass (but spams stdout).
|
||||
proc = subprocess.Popen([sys.executable, '-h'],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
with proc, mock.patch.object(proc, 'stdin') as mock_proc_stdin:
|
||||
mock_proc_stdin.close.side_effect = BrokenPipeError
|
||||
# _communicate() should swallow BrokenPipeError from close.
|
||||
proc.communicate(timeout=999)
|
||||
mock_proc_stdin.close.assert_called_once_with()
|
||||
|
||||
|
||||
class RunFuncTestCase(BaseTestCase):
|
||||
def run_python(self, code, **kwargs):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue