From 1bcf854aad6e54f46a2982d32d27a7707846dbdc Mon Sep 17 00:00:00 2001 From: yihong Date: Tue, 16 Sep 2025 00:26:23 +0800 Subject: [PATCH 1/2] gh-128636: Fix crash in PyREPL when os.environ is overwritten with an invalid value for macOS (GH-138089) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: yihong0618 Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> (cherry picked from commit 8ef7735c536e0ffe4a60224e59b7587288f53e9e) --- Lib/_colorize.py | 18 +++++++++++++----- Lib/_pyrepl/unix_console.py | 8 ++++++-- Lib/test/support/__init__.py | 2 +- Lib/test/test_pyrepl/test_unix_console.py | 9 +++++++++ ...5-09-10-10-02-59.gh-issue-128636.ldRKGZ.rst | 2 ++ 5 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-09-10-10-02-59.gh-issue-128636.ldRKGZ.rst diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 9eb6f0933b8150..0688ea00aa5bff 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -77,21 +77,29 @@ def get_colors( def can_colorize(*, file: IO[str] | IO[bytes] | None = None) -> bool: + + def _safe_getenv(k: str, fallback: str | None = None) -> str | None: + """Exception-safe environment retrieval. See gh-128636.""" + try: + return os.environ.get(k, fallback) + except Exception: + return fallback + if file is None: file = sys.stdout if not sys.flags.ignore_environment: - if os.environ.get("PYTHON_COLORS") == "0": + if _safe_getenv("PYTHON_COLORS") == "0": return False - if os.environ.get("PYTHON_COLORS") == "1": + if _safe_getenv("PYTHON_COLORS") == "1": return True - if os.environ.get("NO_COLOR"): + if _safe_getenv("NO_COLOR"): return False if not COLORIZE: return False - if os.environ.get("FORCE_COLOR"): + if _safe_getenv("FORCE_COLOR"): return True - if os.environ.get("TERM") == "dumb": + if _safe_getenv("TERM") == "dumb": return False if not hasattr(file, "fileno"): diff --git a/Lib/_pyrepl/unix_console.py b/Lib/_pyrepl/unix_console.py index 5b0c2fef92c0f0..6462ba4369d8b0 100644 --- a/Lib/_pyrepl/unix_console.py +++ b/Lib/_pyrepl/unix_console.py @@ -154,6 +154,10 @@ def __init__( self.input_buffer_pos = 0 curses.setupterm(term or None, self.output_fd) self.term = term + self.is_apple_terminal = ( + platform.system() == "Darwin" + and os.getenv("TERM_PROGRAM") == "Apple_Terminal" + ) @overload def _my_getstr(cap: str, optional: Literal[False] = False) -> bytes: ... @@ -348,7 +352,7 @@ def prepare(self): tcsetattr(self.input_fd, termios.TCSADRAIN, raw) # In macOS terminal we need to deactivate line wrap via ANSI escape code - if platform.system() == "Darwin" and os.getenv("TERM_PROGRAM") == "Apple_Terminal": + if self.is_apple_terminal: os.write(self.output_fd, b"\033[?7l") self.screen = [] @@ -379,7 +383,7 @@ def restore(self): self.flushoutput() tcsetattr(self.input_fd, termios.TCSADRAIN, self.__svtermstate) - if platform.system() == "Darwin" and os.getenv("TERM_PROGRAM") == "Apple_Terminal": + if self.is_apple_terminal: os.write(self.output_fd, b"\033[?7h") if hasattr(self, "old_sigwinch"): diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 279a9ca23ef4df..ce1fdbaf719fc2 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2754,7 +2754,7 @@ def no_color(): from .os_helper import EnvironmentVarGuard with ( - swap_attr(_colorize, "can_colorize", lambda file=None: False), + swap_attr(_colorize, "can_colorize", lambda *, file=None: False), EnvironmentVarGuard() as env, ): env.unset("FORCE_COLOR", "NO_COLOR", "PYTHON_COLORS") diff --git a/Lib/test/test_pyrepl/test_unix_console.py b/Lib/test/test_pyrepl/test_unix_console.py index 057cdd112852dc..b143c40c58e093 100644 --- a/Lib/test/test_pyrepl/test_unix_console.py +++ b/Lib/test/test_pyrepl/test_unix_console.py @@ -327,3 +327,12 @@ def test_getheightwidth_with_invalid_environ(self, _os_write): self.assertIsInstance(console.getheightwidth(), tuple) os.environ = [] self.assertIsInstance(console.getheightwidth(), tuple) + + @unittest.skipUnless(sys.platform == "darwin", "requires macOS") + def test_restore_with_invalid_environ_on_macos(self, _os_write): + # gh-128636 for macOS + console = UnixConsole(term="xterm") + with os_helper.EnvironmentVarGuard(): + os.environ = [] + console.prepare() # needed to call restore() + console.restore() # this should succeed diff --git a/Misc/NEWS.d/next/Library/2025-09-10-10-02-59.gh-issue-128636.ldRKGZ.rst b/Misc/NEWS.d/next/Library/2025-09-10-10-02-59.gh-issue-128636.ldRKGZ.rst new file mode 100644 index 00000000000000..54eae0a4601617 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-10-10-02-59.gh-issue-128636.ldRKGZ.rst @@ -0,0 +1,2 @@ +Fix crash in PyREPL when os.environ is overwritten with an invalid value for +mac From b294cdfcd2f8fc515d0b73e1637d4e606800f76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Mon, 15 Sep 2025 18:47:43 +0200 Subject: [PATCH 2/2] [3.13] gh-128636: Fix crash in PyREPL when os.environ is overwritten with an invalid value for macOS (GH-138089) (cherry picked from commit 8ef7735c536e0ffe4a60224e59b7587288f53e9e) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: yihong Signed-off-by: yihong0618 Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>