Skip to content

Commit b5c891b

Browse files
committed
gh-109276, gh-109508: Fix libregrtest stdout
Remove replace_stdout(): call sys.stdout.reconfigure() instead of set the error handler to backslashreplace. display_header() logs an empty line and flush stdout. Remove encoding workaround in display_header() since stdout error handler is now set to backslashreplace earlier.
1 parent fbfec56 commit b5c891b

File tree

4 files changed

+27
-54
lines changed

4 files changed

+27
-54
lines changed

Doc/using/configure.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ Main Makefile targets
965965
this the default target of the ``make`` command (``make all`` or just
966966
``make``).
967967

968-
* ``make test``: Build Python and run the Python test suite with ``--slow-ci``
968+
* ``make test``: Build Python and run the Python test suite with ``--fast-ci``
969969
option. Variables:
970970

971971
* ``TESTOPTS``: additional regrtest command line options.

Lib/test/libregrtest/main.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -513,9 +513,11 @@ def _reexecute_python(self):
513513
print_warning(f"Failed to reexecute Python: {exc!r}\n"
514514
f"Command: {cmd_text}")
515515

516-
def main(self, tests: TestList | None = None):
517-
if self.want_reexec and self.ci_mode:
518-
self._reexecute_python()
516+
def _init(self):
517+
# Set sys.stdout encoder error handler to backslashreplace,
518+
# similar to sys.stderr error handler, to avoid UnicodeEncodeError
519+
# when printing a traceback or any other non-encodable character.
520+
sys.stdout.reconfigure(errors="backslashreplace")
519521

520522
if self.junit_filename and not os.path.isabs(self.junit_filename):
521523
self.junit_filename = os.path.abspath(self.junit_filename)
@@ -524,6 +526,12 @@ def main(self, tests: TestList | None = None):
524526

525527
self.tmp_dir = get_temp_dir(self.tmp_dir)
526528

529+
def main(self, tests: TestList | None = None):
530+
if self.want_reexec and self.ci_mode:
531+
self._reexecute_python()
532+
533+
self._init()
534+
527535
if self.want_cleanup:
528536
cleanup_temp_dir(self.tmp_dir)
529537
sys.exit(0)

Lib/test/libregrtest/setup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from .runtests import RunTests
1212
from .utils import (
1313
setup_unraisable_hook, setup_threading_excepthook, fix_umask,
14-
replace_stdout, adjust_rlimit_nofile)
14+
adjust_rlimit_nofile)
1515

1616

1717
UNICODE_GUARD_ENV = "PYTHONREGRTEST_UNICODE_GUARD"
@@ -49,7 +49,7 @@ def setup_process():
4949
faulthandler.register(signum, chain=True, file=stderr_fd)
5050

5151
adjust_rlimit_nofile()
52-
replace_stdout()
52+
5353
support.record_original_stdout(sys.stdout)
5454

5555
# Some times __path__ and __file__ are not absolute (e.g. while running from

Lib/test/libregrtest/utils.py

+13-48
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import atexit
21
import contextlib
32
import faulthandler
43
import locale
@@ -495,32 +494,6 @@ def normalize_test_name(test_full_name, *, is_error=False):
495494
return short_name
496495

497496

498-
def replace_stdout():
499-
"""Set stdout encoder error handler to backslashreplace (as stderr error
500-
handler) to avoid UnicodeEncodeError when printing a traceback"""
501-
stdout = sys.stdout
502-
try:
503-
fd = stdout.fileno()
504-
except ValueError:
505-
# On IDLE, sys.stdout has no file descriptor and is not a TextIOWrapper
506-
# object. Leaving sys.stdout unchanged.
507-
#
508-
# Catch ValueError to catch io.UnsupportedOperation on TextIOBase
509-
# and ValueError on a closed stream.
510-
return
511-
512-
sys.stdout = open(fd, 'w',
513-
encoding=stdout.encoding,
514-
errors="backslashreplace",
515-
closefd=False,
516-
newline='\n')
517-
518-
def restore_stdout():
519-
sys.stdout.close()
520-
sys.stdout = stdout
521-
atexit.register(restore_stdout)
522-
523-
524497
def adjust_rlimit_nofile():
525498
"""
526499
On macOS the default fd limit (RLIMIT_NOFILE) is sometimes too low (256)
@@ -548,20 +521,12 @@ def adjust_rlimit_nofile():
548521

549522

550523
def display_header(use_resources: tuple[str, ...]):
551-
encoding = sys.stdout.encoding
552-
553524
# Print basic platform information
554525
print("==", platform.python_implementation(), *sys.version.split())
555526
print("==", platform.platform(aliased=True),
556527
"%s-endian" % sys.byteorder)
557528
print("== Python build:", ' '.join(get_build_info()))
558-
559-
cwd = os.getcwd()
560-
# gh-109508: support.os_helper.FS_NONASCII, used by get_work_dir(), cannot
561-
# be encoded to the filesystem encoding on purpose, escape non-encodable
562-
# characters with backslashreplace error handler.
563-
formatted_cwd = cwd.encode(encoding, "backslashreplace").decode(encoding)
564-
print("== cwd:", formatted_cwd)
529+
print("== cwd:", os.getcwd())
565530

566531
cpu_count = os.cpu_count()
567532
if cpu_count:
@@ -588,18 +553,18 @@ def display_header(use_resources: tuple[str, ...]):
588553
sanitizers.append("memory")
589554
if ubsan:
590555
sanitizers.append("undefined behavior")
591-
if not sanitizers:
592-
return
593-
594-
print(f"== sanitizers: {', '.join(sanitizers)}")
595-
for sanitizer, env_var in (
596-
(asan, "ASAN_OPTIONS"),
597-
(msan, "MSAN_OPTIONS"),
598-
(ubsan, "UBSAN_OPTIONS"),
599-
):
600-
options= os.environ.get(env_var)
601-
if sanitizer and options is not None:
602-
print(f"== {env_var}={options!r}")
556+
if sanitizers:
557+
print(f"== sanitizers: {', '.join(sanitizers)}")
558+
for sanitizer, env_var in (
559+
(asan, "ASAN_OPTIONS"),
560+
(msan, "MSAN_OPTIONS"),
561+
(ubsan, "UBSAN_OPTIONS"),
562+
):
563+
options= os.environ.get(env_var)
564+
if sanitizer and options is not None:
565+
print(f"== {env_var}={options!r}")
566+
567+
print(flush=True)
603568

604569

605570
def cleanup_temp_dir(tmp_dir: StrPath):

0 commit comments

Comments
 (0)