From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Tue, 21 Jan 2025 16:14:24 +0000 (+0200) Subject: [3.13] gh-128595: Default to stdout isatty for colour detection instead of stderr... X-Git-Tag: v3.13.2~66 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=383af395af828f40d9543ee0a8fdc5cc011d43db;p=thirdparty%2FPython%2Fcpython.git [3.13] gh-128595: Default to stdout isatty for colour detection instead of stderr (GH-128498) (#129057) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Serhiy Storchaka Co-authored-by: Victor Stinner Fix `test__colorize` unexpected keyword argument 'file' on buildbots (#129070) --- diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 709081e25ec5..daecf5b16c19 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -24,14 +24,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 @@ -47,7 +50,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": @@ -60,6 +63,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() diff --git a/Lib/doctest.py b/Lib/doctest.py index c531e3ca6a3d..dd4d62a210a9 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -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) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 1e711ef32bc7..e6a8ef1ddcc1 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2700,7 +2700,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/test/test__colorize.py b/Lib/test/test__colorize.py index 1871775fa205..77e74fa3e23c 100644 --- a/Lib/test/test__colorize.py +++ b/Lib/test/test__colorize.py @@ -9,7 +9,7 @@ ORIGINAL_CAN_COLORIZE = _colorize.can_colorize def setUpModule(): - _colorize.can_colorize = lambda: False + _colorize.can_colorize = lambda *args, **kwargs: False def tearDownModule(): @@ -21,6 +21,7 @@ class TestColorizeFunction(unittest.TestCase): def test_colorized_detection_checks_for_environment_variables(self): flags = unittest.mock.MagicMock(ignore_environment=False) with (unittest.mock.patch("os.isatty") as isatty_mock, + unittest.mock.patch("sys.stdout") as stdout_mock, unittest.mock.patch("sys.stderr") as stderr_mock, unittest.mock.patch("sys.flags", flags), unittest.mock.patch("_colorize.can_colorize", ORIGINAL_CAN_COLORIZE), @@ -29,6 +30,8 @@ class TestColorizeFunction(unittest.TestCase): contextlib.nullcontext()) as vt_mock): isatty_mock.return_value = True + stdout_mock.fileno.return_value = 1 + stdout_mock.isatty.return_value = True stderr_mock.fileno.return_value = 2 stderr_mock.isatty.return_value = True with unittest.mock.patch("os.environ", {'TERM': 'dumb'}): @@ -61,6 +64,7 @@ class TestColorizeFunction(unittest.TestCase): self.assertEqual(_colorize.can_colorize(), True) isatty_mock.return_value = False + stdout_mock.isatty.return_value = False stderr_mock.isatty.return_value = False self.assertEqual(_colorize.can_colorize(), False) diff --git a/Lib/traceback.py b/Lib/traceback.py index f73149271b9b..947c3e82b8c0 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -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) 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 000000000000..9a241e37c20a --- /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 color detection instead of stderr. Patch by +Hugo van Kemenade.