8000 [3.14] gh-135124: Change stdout errors in regrtest worker process (GH… · python/cpython@285c69e · GitHub
[go: up one dir, main page]

Skip to content 8000

Commit 285c69e

Browse files
[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 3d396ab) Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent 07921d4 commit 285c69e

File tree

3 files changed

+51
-12
lines changed

3 files changed

+51
-12
lines changed

Lib/test/libregrtest/main.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -543,8 +543,6 @@ def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int:
543543
self.first_runtests = runtests
544544
self.logger.set_tests(runtests)
545545

546-
setup_process()
547-
548546
if (runtests.hunt_refleak is not None) and (not self.num_workers):
549547
# gh-109739: WindowsLoadTracker thread interferes with refleak check
550548
use_load_tracker = False
@@ -721,10 +719,7 @@ def _add_python_opts(self) -> None:
721719
self._execute_python(cmd, environ)
722720

723721
def _init(self):
724-
# Set sys.stdout encoder error handler to backslashreplace,
725-
# similar to sys.stderr error handler, to avoid UnicodeEncodeError
726-
# when printing a traceback or any other non-encodable character.
727-
sys.stdout.reconfigure(errors="backslashreplace")
722+
setup_process()
728723

729724
if self.junit_filename and not os.path.isabs(self.junit_filename):
730725
self.junit_filename = os.path.abspath(self.junit_filename)

Lib/test/libregrtest/setup.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import faulthandler
22
import gc
3+
import io
34
import os
45
import random
56
import signal
@@ -52,6 +53,14 @@ def setup_process() -> None:
5253

5354
support.record_original_stdout(sys.stdout)
5455

56+
# Set sys.stdout encoder error handler to backslashreplace,
57+
# similar to sys.stderr error handler, to avoid UnicodeEncodeError
58+
# when printing a traceback or any other non-encodable character.
59+
#
60+
# Use an assertion to fix mypy error.
61+
assert isinstance(sys.stdout, io.TextIOWrapper)
62+
sys.stdout.reconfigure(errors="backslashreplace")
63+
5564
# Some times __path__ and __file__ are not absolute (e.g. while running from
5665
# Lib/) and, if we change the CWD to run the tests in a temporary dir, some
5766
# imports might fail. This affects only the modules imported before os.chdir().

Lib/test/test_regrtest.py

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -768,13 +768,16 @@ def run_command(self, args, input=None, exitcode=0, **kw):
768768
self.fail(msg)
769769
return proc
770770

771-
def run_python(self, args, **kw):
771+
def run_python(self, args, isolated=True, **kw):
772772
extraargs = []
773773
if 'uops' in sys._xoptions:
774774
# Pass -X uops along
775775
extraargs.extend(['-X', 'uops'])
776-
args = [sys.executable, *extraargs, '-X', 'faulthandler', '-I', *args]
777-
proc = self.run_command(args, **kw)
776+
cmd = [sys.executable, *extraargs, '-X', 'faulthandler']
777+
if isolated:
778+
cmd.append('-I')
779+
cmd.extend(args)
780+
proc = self.run_command(cmd, **kw)
778781
return proc.stdout
779782

780783

@@ -831,8 +834,8 @@ def check_output(self, output):
831834
self.check_executed_tests(output, self.tests,
832835
randomize=True, stats=len(self.tests))
833836

834-
def run_tests(self, args, env=None):
835-
output = self.run_python(args, env=env)
837+
def run_tests(self, args, env=None, isolated=True):
838+
output = self.run_python(args, env=env, isolated=isolated)
836839
self.check_output(output)
837840

838841
def test_script_regrtest(self):
@@ -2276,7 +2279,6 @@ def test_pass(self):
22762279
def test_xml(self):
22772280
code = textwrap.dedent(r"""
22782281
import unittest
2279-
from test import support
22802282
22812283
class VerboseTests(unittest.TestCase):
22822284
def test_failed(self):
@@ -2311,6 +2313,39 @@ def test_failed(self):
23112313
for out in testcase.iter('system-out'):
23122314
self.assertEqual(out.text, r"abc \x1b def")
23132315

2316+
def test_nonascii(self):
2317+
code = textwrap.dedent(r"""
2318+
import unittest
2319+
2320+
class NonASCIITests(unittest.TestCase):
2321+
def test_docstring(self):
2322+
'''docstring:\u20ac'''
2323+
2324+
def test_subtest(self):
2325+
with self.subTest(param='subtest:\u20ac'):
2326+
pass
2327+
2328+
def test_skip(self):
2329+
self.skipTest('skipped:\u20ac')
2330+
""")
2331+
testname = self.create_test(code=code)
2332+
2333+
env = dict(os.environ)
2334+
env['PYTHONIOENCODING'] = 'ascii'
2335+
2336+
def check(output):
2337+
self.check_executed_tests(output, testname, stats=TestStats(3, 0, 1))
2338+
self.assertIn(r'docstring:\u20ac', output)
2339+
self.assertIn(r'skipped:\u20ac', output)
2340+
2341+
# Run sequentially
2342+
output = self.run_tests('-v', testname, env=env, isolated=False)
2343+
check(output)
2344+
2345+
# Run in parallel
2346+
output = self.run_tests('-j1', '-v', testname, env=env, isolated=False)
2347+
check(output)
2348+
23142349

23152350
class TestUtils(unittest.TestCase):
23162351
def test_format_duration(self):

0 commit comments

Comments
 (0)
0