From 3fe5f448425121f7a1afd4e00376cfb66be7b343 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Tue, 15 Sep 2026 13:32:13 -0700 Subject: [PATCH 1/5] gh-157581: Handle exceptions in can_colorize Handle unexpected exceptions in can_colorize instead of surfacing them in the REPL --- Lib/_colorize.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 5f44a3aa05eb8f6..770002e447be8d0 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -603,6 +603,8 @@ def _safe_getenv(k: str, fallback: str | None = None) -> str | None: return os.isatty(file.fileno()) except OSError: return hasattr(file, "isatty") and file.isatty() + except Exception: + return False default_theme = Theme() From 3202e46ab8c1b31ad1afa52308482f74103154f4 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Tue, 15 Sep 2026 14:05:33 -0700 Subject: [PATCH 2/5] Add test --- Lib/test/test__colorize.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib/test/test__colorize.py b/Lib/test/test__colorize.py index c7bc6914ac1bc44..e9e4a07201df468 100644 --- a/Lib/test/test__colorize.py +++ b/Lib/test/test__colorize.py @@ -2,6 +2,7 @@ import dataclasses import io import sys +import tempfile import unittest import unittest.mock import _colorize @@ -187,6 +188,11 @@ def test_colorized_detection_checks_for_file(self): file.isatty.return_value = False self.assertEqual(_colorize.can_colorize(file=file), False) + # gh-157581: A closed file raises ValueError from fileno(). + file = tempfile.TemporaryFile(mode="w") + file.close() + self.assertEqual(_colorize.can_colorize(file=file), False) + if __name__ == "__main__": unittest.main() From 2aa17944eaa40b6ae2d523d6e3e8d0d27fd8c9d5 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 15 Sep 2026 21:12:03 +0000 Subject: [PATCH 3/5] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2026-09-15-21-12-02.gh-issue-157581.qJEepi.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2026-09-15-21-12-02.gh-issue-157581.qJEepi.rst diff --git a/Misc/NEWS.d/next/Library/2026-09-15-21-12-02.gh-issue-157581.qJEepi.rst b/Misc/NEWS.d/next/Library/2026-09-15-21-12-02.gh-issue-157581.qJEepi.rst new file mode 100644 index 000000000000000..e9f5a375824c85a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-15-21-12-02.gh-issue-157581.qJEepi.rst @@ -0,0 +1 @@ +Updated ``can_colorize`` logic in ``_colorize.py`` to catch all exceptions when checking if a file is a TTY, returning ``False`` if any exception occurs. This prevents crashes in edge cases, such as when a file is closed. From bb976a657bdd5b756268b8c92fb393233f47a19d Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Sun, 4 Oct 2026 18:16:00 -0700 Subject: [PATCH 4/5] Address PR feedback --- Lib/_colorize.py | 2 +- Lib/test/test__colorize.py | 2 +- .../next/Library/2026-09-15-21-12-02.gh-issue-157581.qJEepi.rst | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 770002e447be8d0..4427d5ee2fe0e5e 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -603,7 +603,7 @@ def _safe_getenv(k: str, fallback: str | None = None) -> str | None: return os.isatty(file.fileno()) except OSError: return hasattr(file, "isatty") and file.isatty() - except Exception: + except ValueError: return False diff --git a/Lib/test/test__colorize.py b/Lib/test/test__colorize.py index e9e4a07201df468..0c4f5b21a1f8551 100644 --- a/Lib/test/test__colorize.py +++ b/Lib/test/test__colorize.py @@ -191,7 +191,7 @@ def test_colorized_detection_checks_for_file(self): # gh-157581: A closed file raises ValueError from fileno(). file = tempfile.TemporaryFile(mode="w") file.close() - self.assertEqual(_colorize.can_colorize(file=file), False) + self.assertFalse(_colorize.can_colorize(file=file)) if __name__ == "__main__": diff --git a/Misc/NEWS.d/next/Library/2026-09-15-21-12-02.gh-issue-157581.qJEepi.rst b/Misc/NEWS.d/next/Library/2026-09-15-21-12-02.gh-issue-157581.qJEepi.rst index e9f5a375824c85a..3cb829b65e624ee 100644 --- a/Misc/NEWS.d/next/Library/2026-09-15-21-12-02.gh-issue-157581.qJEepi.rst +++ b/Misc/NEWS.d/next/Library/2026-09-15-21-12-02.gh-issue-157581.qJEepi.rst @@ -1 +1 @@ -Updated ``can_colorize`` logic in ``_colorize.py`` to catch all exceptions when checking if a file is a TTY, returning ``False`` if any exception occurs. This prevents crashes in edge cases, such as when a file is closed. +Updated :func:``!_colorize.can_colorize`` to not raise when given a closed file stream. \ No newline at end of file From 54e330531647c9419bfac3f50f18d5f25005d406 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Sun, 4 Oct 2026 18:41:47 -0700 Subject: [PATCH 5/5] Use mock instead of temp file --- Lib/test/test__colorize.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/test/test__colorize.py b/Lib/test/test__colorize.py index 0c4f5b21a1f8551..788c68562cabd47 100644 --- a/Lib/test/test__colorize.py +++ b/Lib/test/test__colorize.py @@ -2,7 +2,6 @@ import dataclasses import io import sys -import tempfile import unittest import unittest.mock import _colorize @@ -189,8 +188,8 @@ def test_colorized_detection_checks_for_file(self): self.assertEqual(_colorize.can_colorize(file=file), False) # gh-157581: A closed file raises ValueError from fileno(). - file = tempfile.TemporaryFile(mode="w") - file.close() + file = unittest.mock.Mock() + file.fileno.side_effect = ValueError self.assertFalse(_colorize.can_colorize(file=file))