From 4ad090d40872507689b4292c33a75d1468382b92 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 11 Dec 2024 21:01:46 +0200 Subject: [PATCH 01/17] Add test class helper to force no terminal colour --- Lib/test/support/__init__.py | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 42e7b876594fa7..173b270f06a170 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -60,6 +60,7 @@ "skip_on_s390x", "without_optimizer", "force_not_colorized", + "force_not_colorized_test_class", "BrokenIter", "in_systemd_nspawn_sync_suppressed", "run_no_yield_async_fn", "run_yielding_async_fn", "async_yield", @@ -2856,6 +2857,44 @@ def wrapper(*args, **kwargs): return wrapper + +def force_not_colorized_test_class(cls): + """Force the terminal not to be colorized.""" + original_setup = cls.setUp + original_teardown = cls.tearDown + + @functools.wraps(cls.setUp) + def setUp_wrapper(self, *args, **kwargs): + import _colorize + + self._original_fn = _colorize.can_colorize + self._variables: dict[str, str | None] = { + "PYTHON_COLORS": None, + "FORCE_COLOR": None, + "NO_COLOR": None, + } + for key in self._variables: + self._variables[key] = os.environ.pop(key, None) + os.environ["NO_COLOR"] = "1" + _colorize.can_colorize = lambda: False + return original_setup(self, *args, **kwargs) + + @functools.wraps(cls.tearDown) + def tearDown_wrapper(self, *args, **kwargs): + import _colorize + + _colorize.can_colorize = self._original_fn + del os.environ["NO_COLOR"] + for key, value in self._variables.items(): + if value is not None: + os.environ[key] = value + return original_teardown(self, *args, **kwargs) + + cls.setUp = setUp_wrapper + cls.tearDown = tearDown_wrapper + return cls + + def initialized_with_pyrepl(): """Detect whether PyREPL was used during Python initialization.""" # If the main module has a __file__ attribute it's a Python module, which means PyREPL. From ab76e1b7aa2f1e92a65d3124c4e5d43fd9e0f106 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Wed, 11 Dec 2024 23:33:01 +0200 Subject: [PATCH 02/17] Refactor --- Lib/test/support/__init__.py | 67 ++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 173b270f06a170..db29f0428a19ba 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -17,6 +17,7 @@ import types import unittest import warnings +from collections.abc import Callable __all__ = [ @@ -2833,31 +2834,45 @@ def is_slot_wrapper(name, value): yield name, True +def _disable_terminal_color() -> Callable[[], bool]: + import _colorize + + original_fn = _colorize.can_colorize + variables: dict[str, str | None] = { + "PYTHON_COLORS": None, + "FORCE_COLOR": None, + "NO_COLOR": None, + } + for key in variables: + variables[key] = os.environ.pop(key, None) + os.environ["NO_COLOR"] = "1" + _colorize.can_colorize = lambda: False + return original_fn, variables + + +def _re_enable_terminal_color( + original_fn: Callable[[], bool], variables: dict[str, str | None] +): + import _colorize + + _colorize.can_colorize = original_fn + del os.environ["NO_COLOR"] + for key, value in variables.items(): + if value is not None: + os.environ[key] = value + + def force_not_colorized(func): """Force the terminal not to be colorized.""" @functools.wraps(func) def wrapper(*args, **kwargs): - import _colorize - original_fn = _colorize.can_colorize - variables: dict[str, str | None] = { - "PYTHON_COLORS": None, "FORCE_COLOR": None, "NO_COLOR": None - } try: - for key in variables: - variables[key] = os.environ.pop(key, None) - os.environ["NO_COLOR"] = "1" - _colorize.can_colorize = lambda: False + original_fn, variables = _disable_terminal_color() return func(*args, **kwargs) finally: - _colorize.can_colorize = original_fn - del os.environ["NO_COLOR"] - for key, value in variables.items(): - if value is not None: - os.environ[key] = value + _re_enable_terminal_color(original_fn, variables) return wrapper - - def force_not_colorized_test_class(cls): """Force the terminal not to be colorized.""" original_setup = cls.setUp @@ -2865,29 +2880,13 @@ def force_not_colorized_test_class(cls): @functools.wraps(cls.setUp) def setUp_wrapper(self, *args, **kwargs): - import _colorize + self._original_fn, self._variables = _disable_terminal_color() - self._original_fn = _colorize.can_colorize - self._variables: dict[str, str | None] = { - "PYTHON_COLORS": None, - "FORCE_COLOR": None, - "NO_COLOR": None, - } - for key in self._variables: - self._variables[key] = os.environ.pop(key, None) - os.environ["NO_COLOR"] = "1" - _colorize.can_colorize = lambda: False return original_setup(self, *args, **kwargs) @functools.wraps(cls.tearDown) def tearDown_wrapper(self, *args, **kwargs): - import _colorize - - _colorize.can_colorize = self._original_fn - del os.environ["NO_COLOR"] - for key, value in self._variables.items(): - if value is not None: - os.environ[key] = value + _re_enable_terminal_color(self._original_fn, self._variables) return original_teardown(self, *args, **kwargs) cls.setUp = setUp_wrapper From bb90f8924934ccdfecf5573fae0251d22f26a2e7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sat, 4 Jan 2025 16:54:07 +0200 Subject: [PATCH 03/17] Default to stdout isatty for colour detection instead of stderr --- Lib/_colorize.py | 6 +++--- Lib/test/test_code_module.py | 4 ++-- Lib/test/test_traceback.py | 9 ++++++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Lib/_colorize.py b/Lib/_colorize.py index f609901887a26b..be609c6d90a853 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -49,7 +49,7 @@ def can_colorize() -> bool: if os.environ.get("TERM") == "dumb": return False - if not hasattr(sys.stderr, "fileno"): + if not hasattr(sys.stdout, "fileno"): return False if sys.platform == "win32": @@ -62,6 +62,6 @@ def can_colorize() -> bool: return False try: - return os.isatty(sys.stderr.fileno()) + return os.isatty(sys.stdout.fileno()) except io.UnsupportedOperation: - return sys.stderr.isatty() + return sys.stdout.isatty() diff --git a/Lib/test/test_code_module.py b/Lib/test/test_code_module.py index 37c7bc772ed8c7..11dce808c9415e 100644 --- a/Lib/test/test_code_module.py +++ b/Lib/test/test_code_module.py @@ -5,8 +5,7 @@ from textwrap import dedent from contextlib import ExitStack from unittest import mock -from test.support import import_helper - +from test.support import force_not_colorized_test_class, import_helper code = import_helper.import_module('code') @@ -30,6 +29,7 @@ def mock_sys(self): del self.sysmod.ps2 +@force_not_colorized_test_class class TestInteractiveConsole(unittest.TestCase, MockSys): maxDiff = None diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 31f0a61d6a9d59..abdfc4638f2e9c 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -21,7 +21,7 @@ from test.support.os_helper import TESTFN, unlink from test.support.script_helper import assert_python_ok, assert_python_failure from test.support.import_helper import forget -from test.support import force_not_colorized +from test.support import force_not_colorized, force_not_colorized_test_class import json import textwrap @@ -1712,6 +1712,7 @@ def f(): @requires_debug_ranges() +@force_not_colorized_test_class class PurePythonTracebackErrorCaretTests( PurePythonExceptionFormattingMixin, TracebackErrorLocationCaretTestBase, @@ -1725,6 +1726,7 @@ class PurePythonTracebackErrorCaretTests( @cpython_only @requires_debug_ranges() +@force_not_colorized_test_class class CPythonTracebackErrorCaretTests( CAPIExceptionFormattingMixin, TracebackErrorLocationCaretTestBase, @@ -1736,6 +1738,7 @@ class CPythonTracebackErrorCaretTests( @cpython_only @requires_debug_ranges() +@force_not_colorized_test_class class CPythonTracebackLegacyErrorCaretTests( CAPIExceptionFormattingLegacyMixin, TracebackErrorLocationCaretTestBase, @@ -2149,10 +2152,12 @@ def test_print_exception_bad_type_python(self): boundaries = re.compile( '(%s|%s)' % (re.escape(cause_message), re.escape(context_message))) +@force_not_colorized_test_class class TestTracebackFormat(unittest.TestCase, TracebackFormatMixin): pass @cpython_only +@force_not_colorized_test_class class TestFallbackTracebackFormat(unittest.TestCase, TracebackFormatMixin): DEBUG_RANGES = False def setUp(self) -> None: @@ -2940,6 +2945,7 @@ def f(): self.assertEqual(report, expected) +@force_not_colorized_test_class class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): # # This checks reporting through the 'traceback' module, with both @@ -2956,6 +2962,7 @@ def get_report(self, e): return s +@force_not_colorized_test_class class CExcReportingTests(BaseExceptionReportingTests, unittest.TestCase): # # This checks built-in reporting by the interpreter. From 172f8c389934bbdfcc0dbf7b9b2ad2839e1c88e2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 7 Jan 2025 21:49:07 +0200 Subject: [PATCH 04/17] Add NEWS --- .../next/Library/2025-01-07-21-48-32.gh-issue-128498.n6jtlW.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-01-07-21-48-32.gh-issue-128498.n6jtlW.rst diff --git a/Misc/NEWS.d/next/Library/2025-01-07-21-48-32.gh-issue-128498.n6jtlW.rst b/Misc/NEWS.d/next/Library/2025-01-07-21-48-32.gh-issue-128498.n6jtlW.rst new file mode 100644 index 00000000000000..812f3df2a4ef39 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-07-21-48-32.gh-issue-128498.n6jtlW.rst @@ -0,0 +1,2 @@ +Default to stdout isatty for colour detection instead of stderr. Patch by +Hugo van Kemenade. From b7855ade9a85b26bcf4c4b9c10d9a495808a7653 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:38:51 +0200 Subject: [PATCH 05/17] Fix merge conflict --- Lib/test/support/__init__.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 137679d7aafd5e..fb98b3e243c3be 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2857,26 +2857,6 @@ def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper -def force_not_colorized_test_class(cls): - """Force the terminal not to be colorized.""" - original_setup = cls.setUp - original_teardown = cls.tearDown - - @functools.wraps(cls.setUp) - def setUp_wrapper(self, *args, **kwargs): - self._original_fn, self._variables = _disable_terminal_color() - - return original_setup(self, *args, **kwargs) - - @functools.wraps(cls.tearDown) - def tearDown_wrapper(self, *args, **kwargs): - _re_enable_terminal_color(self._original_fn, self._variables) - return original_teardown(self, *args, **kwargs) - - cls.setUp = setUp_wrapper - cls.tearDown = tearDown_wrapper - return cls - def force_not_colorized_test_class(cls): """Force the terminal not to be colorized for the entire test class.""" From 8e24cc89d243dd2814b77361a8b87a3f6ad79cc2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:39:46 +0200 Subject: [PATCH 06/17] Remove unused import --- Lib/test/support/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index fb98b3e243c3be..ee9520a8838625 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -17,7 +17,6 @@ import types import unittest import warnings -from collections.abc import Callable __all__ = [ From 2b88c85e729fdc7cbdf5390c95ed4373768d1c69 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 9 Jan 2025 12:44:38 +0200 Subject: [PATCH 07/17] Check can_colorize using same output stream as traceback --- Lib/_colorize.py | 9 ++++++--- Lib/traceback.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Lib/_colorize.py b/Lib/_colorize.py index be609c6d90a853..f0fbf2880f893c 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -33,7 +33,10 @@ def get_colors(colorize: bool = False) -> ANSIColors: return NoColors -def can_colorize() -> bool: +def can_colorize(*, file=None) -> bool: + if file is None: + file = sys.stdout + if not sys.flags.ignore_environment: if os.environ.get("PYTHON_COLORS") == "0": return False @@ -49,7 +52,7 @@ def can_colorize() -> bool: if os.environ.get("TERM") == "dumb": return False - if not hasattr(sys.stdout, "fileno"): + if not hasattr(file, "fileno"): return False if sys.platform == "win32": @@ -62,6 +65,6 @@ def can_colorize() -> bool: return False try: - return os.isatty(sys.stdout.fileno()) + return os.isatty(file.fileno()) except io.UnsupportedOperation: return sys.stdout.isatty() diff --git a/Lib/traceback.py b/Lib/traceback.py index 6367c00e4d4b86..3ed06af15a0a89 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -135,7 +135,7 @@ def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \ def _print_exception_bltin(exc, /): file = sys.stderr if sys.stderr is not None else sys.__stderr__ - colorize = _colorize.can_colorize() + colorize = _colorize.can_colorize(file=file) return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file, colorize=colorize) From 4aa46c5f94f8302be294a94d850e063f414a7117 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:44:55 +0200 Subject: [PATCH 08/17] Check can_colorize using same output stream as unittest --- Lib/doctest.py | 2 +- Lib/unittest/result.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/doctest.py b/Lib/doctest.py index bb281fc483c41c..e02e73ed722f7e 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -1558,7 +1558,7 @@ def out(s): save_displayhook = sys.displayhook sys.displayhook = sys.__displayhook__ saved_can_colorize = _colorize.can_colorize - _colorize.can_colorize = lambda: False + _colorize.can_colorize = lambda *args, **kwargs: False color_variables = {"PYTHON_COLORS": None, "FORCE_COLOR": None} for key in color_variables: color_variables[key] = os.environ.pop(key, None) diff --git a/Lib/unittest/result.py b/Lib/unittest/result.py index 97262735aa8311..e2f0353fead032 100644 --- a/Lib/unittest/result.py +++ b/Lib/unittest/result.py @@ -191,7 +191,8 @@ def _exc_info_to_string(self, err, test): capture_locals=self.tb_locals, compact=True) from _colorize import can_colorize - msgLines = list(tb_e.format(colorize=can_colorize())) + file = self.stream if hasattr(self, "stream") else None + msgLines = list(tb_e.format(colorize=can_colorize(file=file))) if self.buffer: output = sys.stdout.getvalue() From d0ce62201d6dd1670ae0de977c5fa141aa622bb5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:31:47 +0200 Subject: [PATCH 09/17] Check get_colors using the same output stream --- Lib/_colorize.py | 7 +++++-- Lib/test/libregrtest/single.py | 4 ++-- Lib/test/support/__init__.py | 2 +- Lib/unittest/runner.py | 6 ++++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Lib/_colorize.py b/Lib/_colorize.py index f0fbf2880f893c..0dd8db4aa6e1ef 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -26,8 +26,11 @@ class ANSIColors: setattr(NoColors, attr, "") -def get_colors(colorize: bool = False) -> ANSIColors: - if colorize or can_colorize(): +def get_colors(colorize: bool = False, *, file=None) -> ANSIColors: + if file is None: + file = sys.stdout + + if colorize or can_colorize(file=file): return ANSIColors() else: return NoColors diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index 0e174f82abed28..125a88c0f98f09 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -162,7 +162,7 @@ def test_func(): def _runtest_env_changed_exc(result: TestResult, runtests: RunTests, display_failure: bool = True) -> None: # Handle exceptions, detect environment changes. - ansi = get_colors() + ansi = get_colors(file=sys.stderr) red, reset, yellow = ansi.RED, ansi.RESET, ansi.YELLOW # Reset the environment_altered flag to detect if a test altered @@ -303,7 +303,7 @@ def run_single_test(test_name: TestName, runtests: RunTests) -> TestResult: If runtests.use_junit, xml_data is a list containing each generated testsuite element. """ - ansi = get_colors() + ansi = get_colors(file=sys.stderr) red, reset, yellow = ansi.BOLD_RED, ansi.RESET, ansi.YELLOW start_time = time.perf_counter() diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index ee9520a8838625..e05e91babc2499 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2839,7 +2839,7 @@ def no_color(): from .os_helper import EnvironmentVarGuard with ( - swap_attr(_colorize, "can_colorize", lambda: False), + swap_attr(_colorize, "can_colorize", lambda file=None: False), EnvironmentVarGuard() as env, ): for var in {"FORCE_COLOR", "NO_COLOR", "PYTHON_COLORS"}: diff --git a/Lib/unittest/runner.py b/Lib/unittest/runner.py index d60c295a1eddf7..5784ca48b54c9d 100644 --- a/Lib/unittest/runner.py +++ b/Lib/unittest/runner.py @@ -45,7 +45,8 @@ def __init__(self, stream, descriptions, verbosity, *, durations=None): self.showAll = verbosity > 1 self.dots = verbosity == 1 self.descriptions = descriptions - self._ansi = get_colors() + file = sys.stderr if stream == "" else None + self._ansi = get_colors(file=file) self._newline = True self.durations = durations @@ -286,7 +287,8 @@ def run(self, test): expected_fails, unexpected_successes, skipped = results infos = [] - ansi = get_colors() + file = sys.stderr if self.stream == "" else None + ansi = get_colors(file=file) bold_red = ansi.BOLD_RED green = ansi.GREEN red = ansi.RED From 7debe697b40b26d6b7d4f410b6a287779c78c7dd Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 13 Jan 2025 19:00:15 +0200 Subject: [PATCH 10/17] Apply suggestions from code review Co-authored-by: Serhiy Storchaka Co-authored-by: Victor Stinner --- Lib/_colorize.py | 3 --- Lib/unittest/result.py | 4 ++-- .../Library/2025-01-07-21-48-32.gh-issue-128498.n6jtlW.rst | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 0dd8db4aa6e1ef..8517e14d99a8d6 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -27,9 +27,6 @@ class ANSIColors: def get_colors(colorize: bool = False, *, file=None) -> ANSIColors: - if file is None: - file = sys.stdout - if colorize or can_colorize(file=file): return ANSIColors() else: diff --git a/Lib/unittest/result.py b/Lib/unittest/result.py index e2f0353fead032..23b550991bc9c9 100644 --- a/Lib/unittest/result.py +++ b/Lib/unittest/result.py @@ -191,8 +191,8 @@ def _exc_info_to_string(self, err, test): capture_locals=self.tb_locals, compact=True) from _colorize import can_colorize - file = self.stream if hasattr(self, "stream") else None - msgLines = list(tb_e.format(colorize=can_colorize(file=file))) + colorize = hasattr(self, "stream") and can_colorize(self.stream) + msgLines = list(tb_e.format(colorize=colorize) if self.buffer: output = sys.stdout.getvalue() diff --git a/Misc/NEWS.d/next/Library/2025-01-07-21-48-32.gh-issue-128498.n6jtlW.rst b/Misc/NEWS.d/next/Library/2025-01-07-21-48-32.gh-issue-128498.n6jtlW.rst index 812f3df2a4ef39..9a241e37c20a44 100644 --- a/Misc/NEWS.d/next/Library/2025-01-07-21-48-32.gh-issue-128498.n6jtlW.rst +++ b/Misc/NEWS.d/next/Library/2025-01-07-21-48-32.gh-issue-128498.n6jtlW.rst @@ -1,2 +1,2 @@ -Default to stdout isatty for colour detection instead of stderr. Patch by +Default to stdout isatty for color detection instead of stderr. Patch by Hugo van Kemenade. From 6faf53c79b7067a532ba31e0397e0f7fc710d878 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 13 Jan 2025 19:02:07 +0200 Subject: [PATCH 11/17] Add missing parenthesis --- Lib/unittest/result.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/unittest/result.py b/Lib/unittest/result.py index 23b550991bc9c9..0fa5ec9eb483ce 100644 --- a/Lib/unittest/result.py +++ b/Lib/unittest/result.py @@ -192,7 +192,7 @@ def _exc_info_to_string(self, err, test): from _colorize import can_colorize colorize = hasattr(self, "stream") and can_colorize(self.stream) - msgLines = list(tb_e.format(colorize=colorize) + msgLines = list(tb_e.format(colorize=colorize)) if self.buffer: output = sys.stdout.getvalue() From e1547d04407f45e67f94fb8a8c488de108fb85dd Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 13 Jan 2025 19:05:23 +0200 Subject: [PATCH 12/17] Name the argument --- Lib/unittest/result.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/unittest/result.py b/Lib/unittest/result.py index 0fa5ec9eb483ce..b8ea396db6772e 100644 --- a/Lib/unittest/result.py +++ b/Lib/unittest/result.py @@ -191,7 +191,7 @@ def _exc_info_to_string(self, err, test): capture_locals=self.tb_locals, compact=True) from _colorize import can_colorize - colorize = hasattr(self, "stream") and can_colorize(self.stream) + colorize = hasattr(self, "stream") and can_colorize(file=self.stream) msgLines = list(tb_e.format(colorize=colorize)) if self.buffer: From 7612bff993ca3087351fdfdb8c9a78238b6fa7a8 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 13 Jan 2025 19:06:53 +0200 Subject: [PATCH 13/17] Also print skips to stderr --- Lib/test/libregrtest/single.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index 125a88c0f98f09..e9c9adabacd202 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -184,12 +184,20 @@ def _runtest_env_changed_exc(result: TestResult, runtests: RunTests, _load_run_test(result, runtests) except support.ResourceDenied as exc: if not quiet and not pgo: - print(f"{yellow}{test_name} skipped -- {exc}{reset}", flush=True) + print( + f"{yellow}{test_name} skipped -- {exc}{reset}", + file=sys.stderr, + flush=True, + ) result.state = State.RESOURCE_DENIED return except unittest.SkipTest as exc: if not quiet and not pgo: - print(f"{yellow}{test_name} skipped -- {exc}{reset}", flush=True) + print( + f"{yellow}{test_name} skipped -- {exc}{reset}", + file=sys.stderr, + flush=True, + ) result.state = State.SKIPPED return except support.TestFailedWithDetails as exc: From 44828f0f9a883d55b97d857a853f7aa11c940b4e Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 14 Jan 2025 09:19:21 +0200 Subject: [PATCH 14/17] Check file not sys.stdout Co-authored-by: Victor Stinner --- Lib/_colorize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 8517e14d99a8d6..bab2e599b2c810 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -67,4 +67,4 @@ def can_colorize(*, file=None) -> bool: try: return os.isatty(file.fileno()) except io.UnsupportedOperation: - return sys.stdout.isatty() + return file.isatty() From 36eb18e1bcfdf4b6470ffb1013b883c235803d0b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 14 Jan 2025 09:18:19 +0200 Subject: [PATCH 15/17] file=stream --- Lib/unittest/runner.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Lib/unittest/runner.py b/Lib/unittest/runner.py index 5784ca48b54c9d..eb0234a2617680 100644 --- a/Lib/unittest/runner.py +++ b/Lib/unittest/runner.py @@ -45,8 +45,7 @@ def __init__(self, stream, descriptions, verbosity, *, durations=None): self.showAll = verbosity > 1 self.dots = verbosity == 1 self.descriptions = descriptions - file = sys.stderr if stream == "" else None - self._ansi = get_colors(file=file) + self._ansi = get_colors(file=stream) self._newline = True self.durations = durations @@ -287,8 +286,7 @@ def run(self, test): expected_fails, unexpected_successes, skipped = results infos = [] - file = sys.stderr if self.stream == "" else None - ansi = get_colors(file=file) + ansi = get_colors(file=self.stream) bold_red = ansi.BOLD_RED green = ansi.GREEN red = ansi.RED From f5857b5229930465589eefdfbbd8a5baf0e34c8f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 14 Jan 2025 09:18:49 +0200 Subject: [PATCH 16/17] Print to stdout --- Lib/test/libregrtest/single.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index e9c9adabacd202..274c493b7cf3e9 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -184,11 +184,7 @@ def _runtest_env_changed_exc(result: TestResult, runtests: RunTests, _load_run_test(result, runtests) except support.ResourceDenied as exc: if not quiet and not pgo: - print( - f"{yellow}{test_name} skipped -- {exc}{reset}", - file=sys.stderr, - flush=True, - ) + print(f"{yellow}{test_name} skipped -- {exc}{reset}", flush=True) result.state = State.RESOURCE_DENIED return except unittest.SkipTest as exc: From 3047d2af5f6e98392f655f1e2588ee8038e5a00b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:26:20 +0200 Subject: [PATCH 17/17] Log skipped to stdout, log failures to stderr --- Lib/test/libregrtest/single.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/Lib/test/libregrtest/single.py b/Lib/test/libregrtest/single.py index 274c493b7cf3e9..54df688bbc470e 100644 --- a/Lib/test/libregrtest/single.py +++ b/Lib/test/libregrtest/single.py @@ -162,8 +162,8 @@ def test_func(): def _runtest_env_changed_exc(result: TestResult, runtests: RunTests, display_failure: bool = True) -> None: # Handle exceptions, detect environment changes. - ansi = get_colors(file=sys.stderr) - red, reset, yellow = ansi.RED, ansi.RESET, ansi.YELLOW + stdout = get_colors(file=sys.stdout) + stderr = get_colors(file=sys.stderr) # Reset the environment_altered flag to detect if a test altered # the environment @@ -184,22 +184,24 @@ def _runtest_env_changed_exc(result: TestResult, runtests: RunTests, _load_run_test(result, runtests) except support.ResourceDenied as exc: if not quiet and not pgo: - print(f"{yellow}{test_name} skipped -- {exc}{reset}", flush=True) + print( + f"{stdout.YELLOW}{test_name} skipped -- {exc}{stdout.RESET}", + flush=True, + ) result.state = State.RESOURCE_DENIED return except unittest.SkipTest as exc: if not quiet and not pgo: print( - f"{yellow}{test_name} skipped -- {exc}{reset}", - file=sys.stderr, + f"{stdout.YELLOW}{test_name} skipped -- {exc}{stdout.RESET}", flush=True, ) result.state = State.SKIPPED return except support.TestFailedWithDetails as exc: - msg = f"{red}test {test_name} failed{reset}" + msg = f"{stderr.RED}test {test_name} failed{stderr.RESET}" if display_failure: - msg = f"{red}{msg} -- {exc}{reset}" + msg = f"{stderr.RED}{msg} -- {exc}{stderr.RESET}" print(msg, file=sys.stderr, flush=True) result.state = State.FAILED result.errors = exc.errors @@ -207,9 +209,9 @@ def _runtest_env_changed_exc(result: TestResult, runtests: RunTests, result.stats = exc.stats return except support.TestFailed as exc: - msg = f"{red}test {test_name} failed{reset}" + msg = f"{stderr.RED}test {test_name} failed{stderr.RESET}" if display_failure: - msg = f"{red}{msg} -- {exc}{reset}" + msg = f"{stderr.RED}{msg} -- {exc}{stderr.RESET}" print(msg, file=sys.stderr, flush=True) result.state = State.FAILED result.stats = exc.stats @@ -224,8 +226,11 @@ def _runtest_env_changed_exc(result: TestResult, runtests: RunTests, except: if not pgo: msg = traceback.format_exc() - print(f"{red}test {test_name} crashed -- {msg}{reset}", - file=sys.stderr, flush=True) + print( + f"{stderr.RED}test {test_name} crashed -- {msg}{stderr.RESET}", + file=sys.stderr, + flush=True, + ) result.state = State.UNCAUGHT_EXC return pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy