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

Skip to content

bpo-31804: multiprocessing calls flush on sys.stdout at exit even if … #5492

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions Lib/multiprocessing/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,14 @@ def _bootstrap(self):
finally:
threading._shutdown()
util.info('process exiting with exitcode %d' % exitcode)
sys.stdout.flush()
sys.stderr.flush()
try:
sys.stdout.flush()
except (AttributeError, ValueError):
pass
try:
sys.stderr.flush()
except (AttributeError, ValueError):
pass

return exitcode

Expand Down
65 changes: 45 additions & 20 deletions Lib/test/_test_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,26 +583,52 @@ def test_wait_for_threads(self):
proc.join()
self.assertTrue(evt.is_set())

@classmethod
def _test_error_on_stdio_flush(self, evt):
@staticmethod
def closeIO(stream_name, evt, type_):
if type_ == 'threads':
# it is safe to close std streams in another process, but if thread
# are used, we must not close the original std streams
closed_stream = _file_like(io.StringIO())
closed_stream.close()
setattr(sys, stream_name, closed_stream)
else:
# using StringIO is not the same as using a real closed file,
# because a closed StringIO throws no exception when its flush
# method is called
getattr(sys, stream_name).close()
evt.set()

def test_error_on_stdio_flush(self):
streams = [io.StringIO(), None]
streams[0].close()
@staticmethod
def removeIO(stream_name, evt, type_):
setattr(sys, stream_name, None)
evt.set()

def test_closed_stdio(self):
"""
bpo-28326: multiprocessing.Process depends on sys.stdout being open
"""
self.run_process(self.closeIO)

def test_no_stdio(self):
"""
bpo-31804: set sys.stdio and sys.stderr to None, instead of
changing the Python interpreter to pythonw.exe. (OS independence)
"""
self.run_process(self.removeIO)

def run_process(self, target):
for stream_name in ('stdout', 'stderr'):
for stream in streams:
old_stream = getattr(sys, stream_name)
setattr(sys, stream_name, stream)
try:
evt = self.Event()
proc = self.Process(target=self._test_error_on_stdio_flush,
args=(evt,))
proc.start()
proc.join()
self.assertTrue(evt.is_set())
finally:
old_stream = getattr(sys, stream_name)
evt = self.Event()
proc = self.Process(target=target, args=(stream_name, evt, self.TYPE))
try:
proc.start()
proc.join()
finally:
if self.TYPE == 'threads':
setattr(sys, stream_name, old_stream)
self.assertTrue(evt.is_set())
self.assertEqual(proc.exitcode, 0)

@classmethod
def _sleep_and_set_event(self, evt, delay=0.0):
Expand Down Expand Up @@ -653,10 +679,6 @@ def test_forkserver_sigkill(self):
self.check_forkserver_death(signal.SIGKILL)


#
#
#

class _UpperCaser(multiprocessing.Process):

def __init__(self):
Expand Down Expand Up @@ -3852,6 +3874,9 @@ def flush(self):
self._delegate.write(''.join(self.cache))
self._cache = []

def close(self):
self._delegate.close()

class TestStdinBadfiledescriptor(unittest.TestCase):

def test_queue_in_process(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
bugfix: Process return value is 0 when multiprocessing is used with closed
stdout or stderr streams, or if stdout or stderr is None (e.g. using
pythonw.exe on windows)