From: Victor Stinner Date: Wed, 19 Nov 2025 14:28:31 +0000 (+0100) Subject: [3.13] gh-141570: can_colorize: Expect fileno() to raise OSError, as documented ... X-Git-Tag: v3.13.10~62 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a0cbdd84afd7526a2cb31bfc606170cca492e32e;p=thirdparty%2FPython%2Fcpython.git [3.13] gh-141570: can_colorize: Expect fileno() to raise OSError, as documented (#141716) (#141748) gh-141570: can_colorize: Expect fileno() to raise OSError, as documented (#141716) 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 --- diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 0688ea00aa5b..8263d2df6ecd 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -1,5 +1,4 @@ from __future__ import annotations -import io import os import sys @@ -116,5 +115,5 @@ def can_colorize(*, file: IO[str] | IO[bytes] | None = None) -> bool: 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 b2f0bb1386fe..5c3743677534 100644 --- a/Lib/test/test__colorize.py +++ b/Lib/test/test__colorize.py @@ -129,6 +129,17 @@ class TestColorizeFunction(unittest.TestCase): 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 000000000000..8f4641ce4cf8 --- /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.