bpo-31804: Fix multiprocessing.Process with broken standard streams (#6079)
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.
This commit is contained in:
parent
9fb8415759
commit
e756f66c83
|
@ -14,14 +14,7 @@ class Popen(object):
|
||||||
method = 'fork'
|
method = 'fork'
|
||||||
|
|
||||||
def __init__(self, process_obj):
|
def __init__(self, process_obj):
|
||||||
try:
|
util._flush_std_streams()
|
||||||
sys.stdout.flush()
|
|
||||||
except (AttributeError, ValueError):
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
sys.stderr.flush()
|
|
||||||
except (AttributeError, ValueError):
|
|
||||||
pass
|
|
||||||
self.returncode = None
|
self.returncode = None
|
||||||
self.finalizer = None
|
self.finalizer = None
|
||||||
self._launch(process_obj)
|
self._launch(process_obj)
|
||||||
|
|
|
@ -314,8 +314,7 @@ class BaseProcess(object):
|
||||||
finally:
|
finally:
|
||||||
threading._shutdown()
|
threading._shutdown()
|
||||||
util.info('process exiting with exitcode %d' % exitcode)
|
util.info('process exiting with exitcode %d' % exitcode)
|
||||||
sys.stdout.flush()
|
util._flush_std_streams()
|
||||||
sys.stderr.flush()
|
|
||||||
|
|
||||||
return exitcode
|
return exitcode
|
||||||
|
|
||||||
|
|
|
@ -391,6 +391,20 @@ def _close_stdin():
|
||||||
except (OSError, ValueError):
|
except (OSError, ValueError):
|
||||||
pass
|
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
|
# Start a program with only specified fds kept open
|
||||||
#
|
#
|
||||||
|
|
|
@ -584,10 +584,19 @@ class _TestProcess(BaseTestCase):
|
||||||
self.assertTrue(evt.is_set())
|
self.assertTrue(evt.is_set())
|
||||||
|
|
||||||
@classmethod
|
@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()
|
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 = [io.StringIO(), None]
|
||||||
streams[0].close()
|
streams[0].close()
|
||||||
for stream_name in ('stdout', 'stderr'):
|
for stream_name in ('stdout', 'stderr'):
|
||||||
|
@ -601,6 +610,24 @@ class _TestProcess(BaseTestCase):
|
||||||
proc.start()
|
proc.start()
|
||||||
proc.join()
|
proc.join()
|
||||||
self.assertTrue(evt.is_set())
|
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:
|
finally:
|
||||||
setattr(sys, stream_name, old_stream)
|
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