Skip to content

gh-109276, gh-109508: Fix libregrtest stdout #109903

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

Merged
merged 1 commit into from
Sep 26, 2023
Merged
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
2 changes: 1 addition & 1 deletion Doc/using/configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -965,7 +965,7 @@ Main Makefile targets
this the default target of the ``make`` command (``make all`` or just
``make``).

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

* ``TESTOPTS``: additional regrtest command line options.
Expand Down
14 changes: 11 additions & 3 deletions Lib/test/libregrtest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,9 +513,11 @@ def _reexecute_python(self):
print_warning(f"Failed to reexecute Python: {exc!r}\n"
f"Command: {cmd_text}")

def main(self, tests: TestList | None = None):
if self.want_reexec and self.ci_mode:
self._reexecute_python()
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")

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

self.tmp_dir = get_temp_dir(self.tmp_dir)

def main(self, tests: TestList | None = None):
if self.want_reexec and self.ci_mode:
self._reexecute_python()

self._init()

if self.want_cleanup:
cleanup_temp_dir(self.tmp_dir)
sys.exit(0)
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/libregrtest/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from .runtests import RunTests
from .utils import (
setup_unraisable_hook, setup_threading_excepthook, fix_umask,
replace_stdout, adjust_rlimit_nofile)
adjust_rlimit_nofile)


UNICODE_GUARD_ENV = "PYTHONREGRTEST_UNICODE_GUARD"
Expand Down Expand Up @@ -49,7 +49,7 @@ def setup_process():
faulthandler.register(signum, chain=True, file=stderr_fd)

adjust_rlimit_nofile()
replace_stdout()

support.record_original_stdout(sys.stdout)

# Some times __path__ and __file__ are not absolute (e.g. while running from
Expand Down
61 changes: 13 additions & 48 deletions Lib/test/libregrtest/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import atexit
import contextlib
import faulthandler
import locale
Expand Down Expand Up @@ -495,32 +494,6 @@ def normalize_test_name(test_full_name, *, is_error=False):
return short_name


def replace_stdout():
"""Set stdout encoder error handler to backslashreplace (as stderr error
handler) to avoid UnicodeEncodeError when printing a traceback"""
stdout = sys.stdout
try:
fd = stdout.fileno()
except ValueError:
# On IDLE, sys.stdout has no file descriptor and is not a TextIOWrapper
# object. Leaving sys.stdout unchanged.
#
# Catch ValueError to catch io.UnsupportedOperation on TextIOBase
# and ValueError on a closed stream.
return

sys.stdout = open(fd, 'w',
encoding=stdout.encoding,
errors="backslashreplace",
closefd=False,
newline='\n')

def restore_stdout():
sys.stdout.close()
sys.stdout = stdout
atexit.register(restore_stdout)


def adjust_rlimit_nofile():
"""
On macOS the default fd limit (RLIMIT_NOFILE) is sometimes too low (256)
Expand Down Expand Up @@ -548,20 +521,12 @@ def adjust_rlimit_nofile():


def display_header(use_resources: tuple[str, ...]):
encoding = sys.stdout.encoding

# Print basic platform information
print("==", platform.python_implementation(), *sys.version.split())
print("==", platform.platform(aliased=True),
"%s-endian" % sys.byteorder)
print("== Python build:", ' '.join(get_build_info()))

cwd = os.getcwd()
# gh-109508: support.os_helper.FS_NONASCII, used by get_work_dir(), cannot
# be encoded to the filesystem encoding on purpose, escape non-encodable
# characters with backslashreplace error handler.
formatted_cwd = cwd.encode(encoding, "backslashreplace").decode(encoding)
print("== cwd:", formatted_cwd)
print("== cwd:", os.getcwd())

cpu_count = os.cpu_count()
if cpu_count:
Expand All @@ -588,18 +553,18 @@ def display_header(use_resources: tuple[str, ...]):
sanitizers.append("memory")
if ubsan:
sanitizers.append("undefined behavior")
if not sanitizers:
return

print(f"== sanitizers: {', '.join(sanitizers)}")
for sanitizer, env_var in (
(asan, "ASAN_OPTIONS"),
(msan, "MSAN_OPTIONS"),
(ubsan, "UBSAN_OPTIONS"),
):
options= os.environ.get(env_var)
if sanitizer and options is not None:
print(f"== {env_var}={options!r}")
if sanitizers:
print(f"== sanitizers: {', '.join(sanitizers)}")
for sanitizer, env_var in (
(asan, "ASAN_OPTIONS"),
(msan, "MSAN_OPTIONS"),
(ubsan, "UBSAN_OPTIONS"),
):
options= os.environ.get(env_var)
if sanitizer and options is not None:
print(f"== {env_var}={options!r}")

print(flush=True)


def cleanup_temp_dir(tmp_dir: StrPath):
Expand Down