From 49a5c5754a6c2c04004f18464de52663a23a7c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 18 Nov 2025 14:39:36 +0100 Subject: [PATCH 1/7] can_colorize: Expect fileno() to raise OSError, as documented 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 --- Lib/_colorize.py | 3 +-- Lib/test/test__colorize.py | 11 +++++++++++ .../2025-11-18-14-39-31.gh-issue-141570.q3n984.rst | 3 +++ 3 files changed, 15 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 57b712bc068d4e..7d573274328826 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -1,4 +1,3 @@ -import io import os import sys @@ -330,7 +329,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 25012466840f18..0ceaafe3722897 100644 --- a/Lib/test/test__colorize.py +++ b/Lib/test/test__colorize.py @@ -166,6 +166,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. + # See https://github.com/python/cpython/issues/141570 + 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..1d2375c3ae80c6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst @@ -0,0 +1,3 @@ +In :func:`_colorize.can_colorize`, support file objects rising +:exc:`OSError` from the :meth:`~io.IOBase.fileno` method, as docuemtned. An +example of such an object is a ``mod_wsgi`` logger. From ae077f0880d25aff8adc2f2cd030edee21d2a2c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 18 Nov 2025 14:50:02 +0100 Subject: [PATCH 2/7] fixup! can_colorize: Expect fileno() to raise OSError, as documented --- .../next/Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 1d2375c3ae80c6..cbc346ce064e5a 100644 --- 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 @@ -1,3 +1,3 @@ -In :func:`_colorize.can_colorize`, support file objects rising +In ``_colorize.can_colorize``, support file objects rising :exc:`OSError` from the :meth:`~io.IOBase.fileno` method, as docuemtned. An example of such an object is a ``mod_wsgi`` logger. From 1e91ce46a63debdb80e4dabb75c14304079d8529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 19 Nov 2025 13:31:30 +0100 Subject: [PATCH 3/7] Update Misc/NEWS.d/next/Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst Co-authored-by: Cody Maloney --- .../Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 index cbc346ce064e5a..cacf8994745398 100644 --- 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 @@ -1,3 +1,2 @@ -In ``_colorize.can_colorize``, support file objects rising -:exc:`OSError` from the :meth:`~io.IOBase.fileno` method, as docuemtned. An -example of such an object is a ``mod_wsgi`` logger. +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. From 731f2e337b9a6bc7696f190f2f9a316d7b11a0de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 19 Nov 2025 13:31:55 +0100 Subject: [PATCH 4/7] Update Lib/test/test__colorize.py --- Lib/test/test__colorize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test__colorize.py b/Lib/test/test__colorize.py index 0ceaafe3722897..67e0595943d356 100644 --- a/Lib/test/test__colorize.py +++ b/Lib/test/test__colorize.py @@ -168,7 +168,7 @@ def test_colorized_detection_checks_for_file(self): # The documentation for file.fileno says: # > An OSError is raised if the IO object does not use a file descriptor. - # See https://github.com/python/cpython/issues/141570 + # 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 From 96620c9b0fe9a7210a421d0923fb16ed36c034d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 19 Nov 2025 13:48:12 +0100 Subject: [PATCH 5/7] fixup! Update Misc/NEWS.d/next/Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst --- .../next/Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index cacf8994745398..8b91eae8a3bd3f 100644 --- 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 @@ -1,2 +1,2 @@ -Support :term:`file-like object` raising :exc:`OSError` from :meth:`~io.IOBase.fileno` in color +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. From a2c12595b1f65d601b8d6d651be53437e04a33f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 19 Nov 2025 13:50:26 +0100 Subject: [PATCH 6/7] fixup! Update Misc/NEWS.d/next/Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst --- .../next/Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 8b91eae8a3bd3f..951effda3ab025 100644 --- 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 @@ -1,2 +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. +detection (``_colorize.can_colorize``). This can occur when ``sys.stdout`` is redirected. From 7f2318413e90ff9f4075ae508b5c883b96855ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 19 Nov 2025 14:05:23 +0100 Subject: [PATCH 7/7] Update Misc/NEWS.d/next/Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst Co-authored-by: Victor Stinner --- .../next/Library/2025-11-18-14-39-31.gh-issue-141570.q3n984.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 951effda3ab025..8f4641ce4cf8c2 100644 --- 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 @@ -1,2 +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. +detection (``_colorize.can_colorize()``). This can occur when ``sys.stdout`` is redirected.