[3.6] bpo-31804: Fix multiprocessing.Process with broken standard streams (GH-6079) (GH-6081)
In some conditions the standard streams will be None or closed in the child process (for example if using "pythonw" instead of "python" on Windows). Avoid failing with a non-0 exit code in those conditions.
Report and initial patch by poxthegreat..
(cherry picked from commit e756f66c83
)
This commit is contained in:
parent
20ac11a9fb
commit
069b8d20be
|
@ -14,14 +14,7 @@ class Popen(object):
|
|||
method = 'fork'
|
||||
|
||||
def __init__(self, process_obj):
|
||||
try:
|
||||
sys.stdout.flush()
|
||||
except (AttributeError, ValueError):
|
||||
pass
|
||||
try:
|
||||
sys.stderr.flush()
|
||||
except (AttributeError, ValueError):
|
||||
pass
|
||||
util._flush_std_streams()
|
||||
self.returncode = None
|
||||
self._launch(process_obj)
|
||||
|
||||
|
|
|
@ -274,8 +274,7 @@ class BaseProcess(object):
|
|||
traceback.print_exc()
|
||||
finally:
|
||||
util.info('process exiting with exitcode %d' % exitcode)
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
util._flush_std_streams()
|
||||
|
||||
return exitcode
|
||||
|
||||
|
|
|
@ -388,6 +388,20 @@ def _close_stdin():
|
|||
except (OSError, ValueError):
|
||||
pass
|
||||
|
||||
#
|
||||
# Flush standard streams, if any
|
||||
#
|
||||
|
||||
def _flush_std_streams():
|
||||
try:
|
||||
sys.stdout.flush()
|
||||
except (AttributeError, ValueError):
|
||||
pass
|
||||
try:
|
||||
sys.stderr.flush()
|
||||
except (AttributeError, ValueError):
|
||||
pass
|
||||
|
||||
#
|
||||
# Start a program with only specified fds kept open
|
||||
#
|
||||
|
|
|
@ -427,10 +427,19 @@ class _TestProcess(BaseTestCase):
|
|||
close_queue(q)
|
||||
|
||||
@classmethod
|
||||
def _test_error_on_stdio_flush(self, evt):
|
||||
def _test_error_on_stdio_flush(self, evt, break_std_streams={}):
|
||||
for stream_name, action in break_std_streams.items():
|
||||
if action == 'close':
|
||||
stream = io.StringIO()
|
||||
stream.close()
|
||||
else:
|
||||
assert action == 'remove'
|
||||
stream = None
|
||||
setattr(sys, stream_name, None)
|
||||
evt.set()
|
||||
|
||||
def test_error_on_stdio_flush(self):
|
||||
def test_error_on_stdio_flush_1(self):
|
||||
# Check that Process works with broken standard streams
|
||||
streams = [io.StringIO(), None]
|
||||
streams[0].close()
|
||||
for stream_name in ('stdout', 'stderr'):
|
||||
|
@ -444,6 +453,24 @@ class _TestProcess(BaseTestCase):
|
|||
proc.start()
|
||||
proc.join()
|
||||
self.assertTrue(evt.is_set())
|
||||
self.assertEqual(proc.exitcode, 0)
|
||||
finally:
|
||||
setattr(sys, stream_name, old_stream)
|
||||
|
||||
def test_error_on_stdio_flush_2(self):
|
||||
# Same as test_error_on_stdio_flush_1(), but standard streams are
|
||||
# broken by the child process
|
||||
for stream_name in ('stdout', 'stderr'):
|
||||
for action in ('close', 'remove'):
|
||||
old_stream = getattr(sys, stream_name)
|
||||
try:
|
||||
evt = self.Event()
|
||||
proc = self.Process(target=self._test_error_on_stdio_flush,
|
||||
args=(evt, {stream_name: action}))
|
||||
proc.start()
|
||||
proc.join()
|
||||
self.assertTrue(evt.is_set())
|
||||
self.assertEqual(proc.exitcode, 0)
|
||||
finally:
|
||||
setattr(sys, stream_name, old_stream)
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Avoid failing in multiprocessing.Process if the standard streams are closed
|
||||
or None at exit.
|
Loading…
Reference in New Issue