]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-128595: Default to stdout isatty for colour detection instead of stderr (#128498)
authorHugo van Kemenade <1324225+hugovk@users.noreply.github.com>
Mon, 20 Jan 2025 10:52:42 +0000 (12:52 +0200)
committerGitHub <noreply@github.com>
Mon, 20 Jan 2025 10:52:42 +0000 (12:52 +0200)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
Lib/_colorize.py
Lib/doctest.py
Lib/test/libregrtest/single.py
Lib/test/support/__init__.py
Lib/traceback.py
Lib/unittest/result.py
Lib/unittest/runner.py
Misc/NEWS.d/next/Library/2025-01-07-21-48-32.gh-issue-128498.n6jtlW.rst [new file with mode: 0644]

index f609901887a26baa57f443a3b9be5ae55e88f935..bab2e599b2c810977e1fbffb4b29e69ebf2e9628 100644 (file)
@@ -26,14 +26,17 @@ for attr in dir(NoColors):
         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 colorize or can_colorize(file=file):
         return ANSIColors()
     else:
         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.stderr, "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.stderr.fileno())
+        return os.isatty(file.fileno())
     except io.UnsupportedOperation:
-        return sys.stderr.isatty()
+        return file.isatty()
index bb281fc483c41c684d11d427fda0a3d1cfe350b4..e02e73ed722f7ec29b2487b15da95aa49bb96576 100644 (file)
@@ -1558,7 +1558,7 @@ class DocTestRunner:
         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)
index 0e174f82abed2868a2707cbcb6e4f88233a18fff..54df688bbc470e753f9f0cd5cdc1967cab725de8 100644 (file)
@@ -162,8 +162,8 @@ def _load_run_test(result: TestResult, runtests: RunTests) -> None:
 def _runtest_env_changed_exc(result: TestResult, runtests: RunTests,
                              display_failure: bool = True) -> None:
     # Handle exceptions, detect environment changes.
-    ansi = get_colors()
-    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,18 +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}", flush=True)
+            print(
+                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
@@ -203,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
@@ -220,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
 
@@ -303,7 +312,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()
index ee9520a8838625051da2e5cd26e6d67a0db59ca8..e05e91babc2499aeaf3b9cf25fb129e7c941fce1 100644 (file)
@@ -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"}:
index 6367c00e4d4b86916c5a451f8f3dd2d092c159bf..3ed06af15a0a8929eaf01990ae72fcfe8a678460 100644 (file)
@@ -135,7 +135,7 @@ BUILTIN_EXCEPTION_LIMIT = object()
 
 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)
 
 
index 97262735aa831177fde1610030e9f648b1b212af..b8ea396db6772edbcae66f6e724763bb42ab0473 100644 (file)
@@ -191,7 +191,8 @@ class TestResult(object):
             capture_locals=self.tb_locals, compact=True)
         from _colorize import can_colorize
 
-        msgLines = list(tb_e.format(colorize=can_colorize()))
+        colorize = hasattr(self, "stream") and can_colorize(file=self.stream)
+        msgLines = list(tb_e.format(colorize=colorize))
 
         if self.buffer:
             output = sys.stdout.getvalue()
index d60c295a1eddf7a760590d104ed00326b4eb64bb..eb0234a261768027f23b507ca3b396630834d038 100644 (file)
@@ -45,7 +45,7 @@ class TextTestResult(result.TestResult):
         self.showAll = verbosity > 1
         self.dots = verbosity == 1
         self.descriptions = descriptions
-        self._ansi = get_colors()
+        self._ansi = get_colors(file=stream)
         self._newline = True
         self.durations = durations
 
@@ -286,7 +286,7 @@ class TextTestRunner(object):
             expected_fails, unexpected_successes, skipped = results
 
         infos = []
-        ansi = get_colors()
+        ansi = get_colors(file=self.stream)
         bold_red = ansi.BOLD_RED
         green = ansi.GREEN
         red = ansi.RED
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 (file)
index 0000000..9a241e3
--- /dev/null
@@ -0,0 +1,2 @@
+Default to stdout isatty for color detection instead of stderr. Patch by
+Hugo van Kemenade.