]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-128636: Fix crash in PyREPL when os.environ is overwritten with an invalid...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 16 Sep 2025 09:10:53 +0000 (11:10 +0200)
committerGitHub <noreply@github.com>
Tue, 16 Sep 2025 09:10:53 +0000 (12:10 +0300)
Co-authored-by: yihong <zouzou0208@gmail.com>
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Lib/_colorize.py
Lib/_pyrepl/unix_console.py
Lib/test/support/__init__.py
Lib/test/test_pyrepl/test_unix_console.py
Misc/NEWS.d/next/Library/2025-09-10-10-02-59.gh-issue-128636.ldRKGZ.rst [new file with mode: 0644]

index 7c09bb4564c2e01fb29d697c2429d019ab1dad33..4f510a7141b3d2999d3f1d4a107dc772eaa6f29c 100644 (file)
@@ -273,21 +273,29 @@ def decolor(text: str) -> str:
 
 
 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"):
@@ -330,7 +338,8 @@ def get_theme(
     environment (including environment variable state and console configuration
     on Windows) can also change in the course of the application life cycle.
     """
-    if force_color or (not force_no_color and can_colorize(file=tty_file)):
+    if force_color or (not force_no_color and
+                       can_colorize(file=tty_file)):
         return _theme
     return theme_no_color
 
index a7e49923191c073232374a173cbe46aa23e00c22..9953051bf7c4efbbdbcbb01eac2e5601704b074d 100644 (file)
@@ -159,6 +159,10 @@ class UnixConsole(Console):
         self.pollob.register(self.input_fd, select.POLLIN)
         self.terminfo = terminfo.TermInfo(term or None)
         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: ...
@@ -339,7 +343,7 @@ class UnixConsole(Console):
         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 = []
@@ -370,7 +374,7 @@ class UnixConsole(Console):
         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"):
index 65c1da29936bd31c1e14f461a2392d3e950907cc..a719e49ef377ffe6002b775da561fcb2ae99b7fc 100644 (file)
@@ -2899,7 +2899,7 @@ def force_color(color: bool):
     from .os_helper import EnvironmentVarGuard
 
     with (
-        swap_attr(_colorize, "can_colorize", lambda file=None: color),
+        swap_attr(_colorize, "can_colorize", lambda *, file=None: color),
         EnvironmentVarGuard() as env,
     ):
         env.unset("FORCE_COLOR", "NO_COLOR", "PYTHON_COLORS")
index ab1236768cfb3ef66e821b38a5447f9e107611be..6185c7e3c794e374a6827a8fb3c8346a14719ce0 100644 (file)
@@ -303,3 +303,12 @@ class TestConsole(TestCase):
             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 (file)
index 0000000..54eae0a
--- /dev/null
@@ -0,0 +1,2 @@
+Fix crash in PyREPL when os.environ is overwritten with an invalid value for
+mac