]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-135124: Change stdout errors in regrtest worker process (GH-135138) (#135168)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 5 Jun 2025 09:42:38 +0000 (11:42 +0200)
committerGitHub <noreply@github.com>
Thu, 5 Jun 2025 09:42:38 +0000 (09:42 +0000)
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>
Lib/test/libregrtest/main.py
Lib/test/libregrtest/setup.py
Lib/test/test_regrtest.py

index 713cbedb299706c37b743803b16f684201e4e2bb..0d9c059a93872d044b2eb8566709999b748b377a 100644 (file)
@@ -543,8 +543,6 @@ class Regrtest:
         self.first_runtests = runtests
         self.logger.set_tests(runtests)
 
-        setup_process()
-
         if (runtests.hunt_refleak is not None) and (not self.num_workers):
             # gh-109739: WindowsLoadTracker thread interferes with refleak check
             use_load_tracker = False
@@ -721,10 +719,7 @@ class Regrtest:
         self._execute_python(cmd, environ)
 
     def _init(self):
-        # 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.
-        sys.stdout.reconfigure(errors="backslashreplace")
+        setup_process()
 
         if self.junit_filename and not os.path.isabs(self.junit_filename):
             self.junit_filename = os.path.abspath(self.junit_filename)
index c3d1f60a40066573a9a68d354ca5857c5505db15..9bfc414cd615c8b1c03d06eadd04f6c25102863a 100644 (file)
@@ -1,5 +1,6 @@
 import faulthandler
 import gc
+import io
 import os
 import random
 import signal
@@ -52,6 +53,14 @@ def setup_process() -> None:
 
     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
     # 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().
index 8f4fc09442e083b67344e44eabc01178391c2ba0..49ca0f8a2419bf862b8f3960ecdce429f7dc243f 100644 (file)
@@ -768,13 +768,16 @@ class BaseTestCase(unittest.TestCase):
             self.fail(msg)
         return proc
 
-    def run_python(self, args, **kw):
+    def run_python(self, args, isolated=True, **kw):
         extraargs = []
         if 'uops' in sys._xoptions:
             # Pass -X uops along
             extraargs.extend(['-X', 'uops'])
-        args = [sys.executable, *extraargs, '-X', 'faulthandler', '-I', *args]
-        proc = self.run_command(args, **kw)
+        cmd = [sys.executable, *extraargs, '-X', 'faulthandler']
+        if isolated:
+            cmd.append('-I')
+        cmd.extend(args)
+        proc = self.run_command(cmd, **kw)
         return proc.stdout
 
 
@@ -831,8 +834,8 @@ class ProgramsTestCase(BaseTestCase):
         self.check_executed_tests(output, self.tests,
                                   randomize=True, stats=len(self.tests))
 
-    def run_tests(self, args, env=None):
-        output = self.run_python(args, env=env)
+    def run_tests(self, args, env=None, isolated=True):
+        output = self.run_python(args, env=env, isolated=isolated)
         self.check_output(output)
 
     def test_script_regrtest(self):
@@ -2276,7 +2279,6 @@ class ArgsTestCase(BaseTestCase):
     def test_xml(self):
         code = textwrap.dedent(r"""
             import unittest
-            from test import support
 
             class VerboseTests(unittest.TestCase):
                 def test_failed(self):
@@ -2311,6 +2313,39 @@ class ArgsTestCase(BaseTestCase):
         for out in testcase.iter('system-out'):
             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):
     def test_format_duration(self):