[3.14] gh-135124: Change stdout errors in regrtest worker process (GH-135138) (#135168)
gh-135124: Change stdout errors in regrtest worker process (GH-135138) Set sys.stdout encoder error handler to backslashreplace in regrtest workers to avoid UnicodeEncodeError when printing a traceback or any other non-encodable character. Move the code from the Regrtest class to setup_process(). Call setup_process() earlier, before displaying regrtest headers. (cherry picked from commit 3d396ab7591d544ac8bc1fb49615b4e867ca1c83) Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
07921d4169
commit
285c69ea38
@ -543,8 +543,6 @@ class Regrtest:
|
|||||||
self.first_runtests = runtests
|
self.first_runtests = runtests
|
||||||
self.logger.set_tests(runtests)
|
self.logger.set_tests(runtests)
|
||||||
|
|
||||||
setup_process()
|
|
||||||
|
|
||||||
if (runtests.hunt_refleak is not None) and (not self.num_workers):
|
if (runtests.hunt_refleak is not None) and (not self.num_workers):
|
||||||
# gh-109739: WindowsLoadTracker thread interferes with refleak check
|
# gh-109739: WindowsLoadTracker thread interferes with refleak check
|
||||||
use_load_tracker = False
|
use_load_tracker = False
|
||||||
@ -721,10 +719,7 @@ class Regrtest:
|
|||||||
self._execute_python(cmd, environ)
|
self._execute_python(cmd, environ)
|
||||||
|
|
||||||
def _init(self):
|
def _init(self):
|
||||||
# Set sys.stdout encoder error handler to backslashreplace,
|
setup_process()
|
||||||
# similar to sys.stderr error handler, to avoid UnicodeEncodeError
|
|
||||||
# when printing a traceback or any other non-encodable character.
|
|
||||||
sys.stdout.reconfigure(errors="backslashreplace")
|
|
||||||
|
|
||||||
if self.junit_filename and not os.path.isabs(self.junit_filename):
|
if self.junit_filename and not os.path.isabs(self.junit_filename):
|
||||||
self.junit_filename = os.path.abspath(self.junit_filename)
|
self.junit_filename = os.path.abspath(self.junit_filename)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import faulthandler
|
import faulthandler
|
||||||
import gc
|
import gc
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import signal
|
import signal
|
||||||
@ -52,6 +53,14 @@ def setup_process() -> None:
|
|||||||
|
|
||||||
support.record_original_stdout(sys.stdout)
|
support.record_original_stdout(sys.stdout)
|
||||||
|
|
||||||
|
# Set sys.stdout encoder error handler to backslashreplace,
|
||||||
|
# similar to sys.stderr error handler, to avoid UnicodeEncodeError
|
||||||
|
# when printing a traceback or any other non-encodable character.
|
||||||
|
#
|
||||||
|
# Use an assertion to fix mypy error.
|
||||||
|
assert isinstance(sys.stdout, io.TextIOWrapper)
|
||||||
|
sys.stdout.reconfigure(errors="backslashreplace")
|
||||||
|
|
||||||
# Some times __path__ and __file__ are not absolute (e.g. while running from
|
# Some times __path__ and __file__ are not absolute (e.g. while running from
|
||||||
# Lib/) and, if we change the CWD to run the tests in a temporary dir, some
|
# Lib/) and, if we change the CWD to run the tests in a temporary dir, some
|
||||||
# imports might fail. This affects only the modules imported before os.chdir().
|
# imports might fail. This affects only the modules imported before os.chdir().
|
||||||
|
@ -768,13 +768,16 @@ class BaseTestCase(unittest.TestCase):
|
|||||||
self.fail(msg)
|
self.fail(msg)
|
||||||
return proc
|
return proc
|
||||||
|
|
||||||
def run_python(self, args, **kw):
|
def run_python(self, args, isolated=True, **kw):
|
||||||
extraargs = []
|
extraargs = []
|
||||||
if 'uops' in sys._xoptions:
|
if 'uops' in sys._xoptions:
|
||||||
# Pass -X uops along
|
# Pass -X uops along
|
||||||
extraargs.extend(['-X', 'uops'])
|
extraargs.extend(['-X', 'uops'])
|
||||||
args = [sys.executable, *extraargs, '-X', 'faulthandler', '-I', *args]
|
cmd = [sys.executable, *extraargs, '-X', 'faulthandler']
|
||||||
proc = self.run_command(args, **kw)
|
if isolated:
|
||||||
|
cmd.append('-I')
|
||||||
|
cmd.extend(args)
|
||||||
|
proc = self.run_command(cmd, **kw)
|
||||||
return proc.stdout
|
return proc.stdout
|
||||||
|
|
||||||
|
|
||||||
@ -831,8 +834,8 @@ class ProgramsTestCase(BaseTestCase):
|
|||||||
self.check_executed_tests(output, self.tests,
|
self.check_executed_tests(output, self.tests,
|
||||||
randomize=True, stats=len(self.tests))
|
randomize=True, stats=len(self.tests))
|
||||||
|
|
||||||
def run_tests(self, args, env=None):
|
def run_tests(self, args, env=None, isolated=True):
|
||||||
output = self.run_python(args, env=env)
|
output = self.run_python(args, env=env, isolated=isolated)
|
||||||
self.check_output(output)
|
self.check_output(output)
|
||||||
|
|
||||||
def test_script_regrtest(self):
|
def test_script_regrtest(self):
|
||||||
@ -2276,7 +2279,6 @@ class ArgsTestCase(BaseTestCase):
|
|||||||
def test_xml(self):
|
def test_xml(self):
|
||||||
code = textwrap.dedent(r"""
|
code = textwrap.dedent(r"""
|
||||||
import unittest
|
import unittest
|
||||||
from test import support
|
|
||||||
|
|
||||||
class VerboseTests(unittest.TestCase):
|
class VerboseTests(unittest.TestCase):
|
||||||
def test_failed(self):
|
def test_failed(self):
|
||||||
@ -2311,6 +2313,39 @@ class ArgsTestCase(BaseTestCase):
|
|||||||
for out in testcase.iter('system-out'):
|
for out in testcase.iter('system-out'):
|
||||||
self.assertEqual(out.text, r"abc \x1b def")
|
self.assertEqual(out.text, r"abc \x1b def")
|
||||||
|
|
||||||
|
def test_nonascii(self):
|
||||||
|
code = textwrap.dedent(r"""
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class NonASCIITests(unittest.TestCase):
|
||||||
|
def test_docstring(self):
|
||||||
|
'''docstring:\u20ac'''
|
||||||
|
|
||||||
|
def test_subtest(self):
|
||||||
|
with self.subTest(param='subtest:\u20ac'):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_skip(self):
|
||||||
|
self.skipTest('skipped:\u20ac')
|
||||||
|
""")
|
||||||
|
testname = self.create_test(code=code)
|
||||||
|
|
||||||
|
env = dict(os.environ)
|
||||||
|
env['PYTHONIOENCODING'] = 'ascii'
|
||||||
|
|
||||||
|
def check(output):
|
||||||
|
self.check_executed_tests(output, testname, stats=TestStats(3, 0, 1))
|
||||||
|
self.assertIn(r'docstring:\u20ac', output)
|
||||||
|
self.assertIn(r'skipped:\u20ac', output)
|
||||||
|
|
||||||
|
# Run sequentially
|
||||||
|
output = self.run_tests('-v', testname, env=env, isolated=False)
|
||||||
|
check(output)
|
||||||
|
|
||||||
|
# Run in parallel
|
||||||
|
output = self.run_tests('-j1', '-v', testname, env=env, isolated=False)
|
||||||
|
check(output)
|
||||||
|
|
||||||
|
|
||||||
class TestUtils(unittest.TestCase):
|
class TestUtils(unittest.TestCase):
|
||||||
def test_format_duration(self):
|
def test_format_duration(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user