diff options
author | Victor Stinner <vstinner@python.org> | 2023-09-26 19:34:50 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-26 19:34:50 (GMT) |
commit | b1e4f6e83e8916005caa3f751f25fb58cccbf812 (patch) | |
tree | 87131de209599f2cd2c36165b26744cf9bf19134 | |
parent | ae1d99c2ed9d44b2554129f3a85b97a31119bccc (diff) | |
download | cpython-b1e4f6e83e8916005caa3f751f25fb58cccbf812.zip cpython-b1e4f6e83e8916005caa3f751f25fb58cccbf812.tar.gz cpython-b1e4f6e83e8916005caa3f751f25fb58cccbf812.tar.bz2 |
gh-109276, gh-109508: Fix libregrtest stdout (#109903)
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.
-rw-r--r-- | Doc/using/configure.rst | 2 | ||||
-rw-r--r-- | Lib/test/libregrtest/main.py | 14 | ||||
-rw-r--r-- | Lib/test/libregrtest/setup.py | 4 | ||||
-rw-r--r-- | Lib/test/libregrtest/utils.py | 61 |
4 files changed, 27 insertions, 54 deletions
diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 8207475..a955519 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -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. diff --git a/Lib/test/libregrtest/main.py b/Lib/test/libregrtest/main.py index a93f532..e1cb22a 100644 --- a/Lib/test/libregrtest/main.py +++ b/Lib/test/libregrtest/main.py @@ -513,9 +513,11 @@ class Regrtest: 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) @@ -524,6 +526,12 @@ class Regrtest: 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) diff --git a/Lib/test/libregrtest/setup.py b/Lib/test/libregrtest/setup.py index 204f10f..f0d8d7e 100644 --- a/Lib/test/libregrtest/setup.py +++ b/Lib/test/libregrtest/setup.py @@ -11,7 +11,7 @@ from test.support.os_helper import TESTFN_UNDECODABLE, FS_NONASCII 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" @@ -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 diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py index f3f0eb5..acf3572 100644 --- a/Lib/test/libregrtest/utils.py +++ b/Lib/test/libregrtest/utils.py @@ -1,4 +1,3 @@ -import atexit import contextlib import faulthandler import locale @@ -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) @@ -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: @@ -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): |