https://github.com/python/cpython/commit/3669efb890bfa9f0db6e5c4d92c23c059cfe275b
commit: 3669efb890bfa9f0db6e5c4d92c23c059cfe275b
branch: 3.13
author: Łukasz Langa <[email protected]>
committer: ambv <[email protected]>
date: 2025-09-16T00:53:51+02:00
summary:

[3.13] gh-128636: Fix crash in PyREPL when os.environ is overwritten with an 
invalid value for macOS (GH-138089) (GH-138942)

Signed-off-by: yihong0618 <[email protected]>
Co-authored-by: Bénédikt Tran <[email protected]>

(cherry picked from commit 8ef7735c536e0ffe4a60224e59b7587288f53e9e)

files:
A Misc/NEWS.d/next/Library/2025-09-10-10-02-59.gh-issue-128636.ldRKGZ.rst
M Lib/_colorize.py
M Lib/_pyrepl/unix_console.py
M Lib/test/support/__init__.py
M Lib/test/test_pyrepl/test_unix_console.py

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

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to