From 3146683b284d10ff7019d8e947ffdff4b619a9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 19 Nov 2025 15:02:52 +0100 Subject: [PATCH] gh-141570: can_colorize: Expect fileno() to raise OSError, as documented (GH-141716) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Fedora, we've been given a slightly incomplete reproducer for a problematic Python 3.14 color-related change in argparse that leads to an exception when Python is used from mod_wsgi: https://bugzilla.redhat.com/2414940 mod_wsgi replaces sys.stdout with a custom object that raises OSError on .fileno(): https://github.com/GrahamDumpleton/mod_wsgi/blob/8460dbfcd5c7108892b3cde9fab7cbc1caa27886/src/server/wsgi_logger.c#L434-L440 This should be supported, as the documentation of fileno explicitly says: > An OSError is raised if the IO object does not use a file descriptor. https://docs.python.org/3.14/library/io.html#io.IOBase.fileno The previously expected exception inherits from OSError, so it is still expected. Fixes https://github.com/python/cpython/issues/141570 (cherry picked from commit 96f496a949b05054d0d043c3085f00cec2f83bf5) Co-authored-by: Miro HronĨok Co-authored-by: Cody Maloney Co-authored-by: Victor Stinner --- Lib/_colorize.py | 3 +-- Lib/test/test__colorize.py | 11 +++++++++++ .../2025-11-18-14-39-31.gh-issue-141570.q3n984.rst | 2 ++ 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 766b2d8b80b1a4..d6673f6692f761 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -1,4 +1,3 @@ -import io import os import sys @@ -312,7 +311,7 @@ def _safe_getenv(k: str, fallback: str | None = None) -> str | None: try: return os.isatty(file.fileno()) - except io.UnsupportedOperation: + except OSError: return hasattr(file, "isatty") and file.isatty() diff --git a/Lib/test/test__colorize.py b/Lib/test/test__colorize.py index 3ac89987f91e44..026277267e04ce 100644 --- a/Lib/test/test__colorize.py +++ b/Lib/test/test__colorize.py @@ -165,6 +165,17 @@ def test_colorized_detection_checks_for_file(self): file.isatty.return_value = False self.assertEqual(_colorize.can_colorize(file=file), False) + # The documentation for file.fileno says: + # > An OSError is raised if the IO object does not use a file descriptor. + # gh-141570: Check OSError is caught and handled + with unittest.mock.patch("os.isatty", side_effect=ZeroDivisionError): + file = unittest.mock.MagicMock() + file.fileno.side_effect = OSError + file.isatty.return_value = True + self.assertEqual(_colorize.can_colorize(file=file), True) + file.isatty.return_value = False + self.assertEqual(_colorize.can_colorize(file=file), False) + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst b/Misc/NEWS.d/next/Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst new file mode 100644 index 00000000000000..8f4641ce4cf8c2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst @@ -0,0 +1,2 @@ +Support :term:`file-like object` raising :exc:`OSError` from :meth:`~io.IOBase.fileno` in color +detection (``_colorize.can_colorize()``). This can occur when ``sys.stdout`` is redirected.