Skip to content

Commit 8ef7735

Browse files
yihong0618picnixz
andauthored
gh-128636: Fix crash in PyREPL when os.environ is overwritten with an invalid value for macOS (GH-138089)
Signed-off-by: yihong0618 <[email protected]> Co-authored-by: Bénédikt Tran <[email protected]>
1 parent 46f823b commit 8ef7735

File tree

5 files changed

+33
-9
lines changed

5 files changed

+33
-9
lines changed

Lib/_colorize.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -288,21 +288,29 @@ def decolor(text: str) -> str:
288288

289289

290290
def can_colorize(*, file: IO[str] | IO[bytes] | None = None) -> bool:
291+
292+
def _safe_getenv(k: str, fallback: str | None = None) -> str | None:
293+
"""Exception-safe environment retrieval. See gh-128636."""
294+
try:
295+
return os.environ.get(k, fallback)
296+
except Exception:
297+
return fallback
298+
291299
if file is None:
292300
file = sys.stdout
293301

294302
if not sys.flags.ignore_environment:
295-
if os.environ.get("PYTHON_COLORS") == "0":
303+
if _safe_getenv("PYTHON_COLORS") == "0":
296304
return False
297-
if os.environ.get("PYTHON_COLORS") == "1":
305+
if _safe_getenv("PYTHON_COLORS") == "1":
298306
return True
299-
if os.environ.get("NO_COLOR"):
307+
if _safe_getenv("NO_COLOR"):
300308
return False
301309
if not COLORIZE:
302310
return False
303-
if os.environ.get("FORCE_COLOR"):
311+
if _safe_getenv("FORCE_COLOR"):
304312
return True
305-
if os.environ.get("TERM") == "dumb":
313+
if _safe_getenv("TERM") == "dumb":
306314
return False
307315

308316
if not hasattr(file, "fileno"):
@@ -345,7 +353,8 @@ def get_theme(
345353
environment (including environment variable state and console configuration
346354
on Windows) can also change in the course of the application life cycle.
347355
"""
348-
if force_color or (not force_no_color and can_colorize(file=tty_file)):
356+
if force_color or (not force_no_color and
357+
can_colorize(file=tty_file)):
349358
return _theme
350359
return theme_no_color
351360

Lib/_pyrepl/unix_console.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ def __init__(
159159
self.pollob.register(self.input_fd, select.POLLIN)
160160
self.terminfo = terminfo.TermInfo(term or None)
161161
self.term = term
162+
self.is_apple_terminal = (
163+
platform.system() == "Darwin"
164+
and os.getenv("TERM_PROGRAM") == "Apple_Terminal"
165+
)
162166

163167
@overload
164168
def _my_getstr(cap: str, optional: Literal[False] = False) -> bytes: ...
@@ -339,7 +343,7 @@ def prepare(self):
339343
tcsetattr(self.input_fd, termios.TCSADRAIN, raw)
340344

341345
# In macOS terminal we need to deactivate line wrap via ANSI escape code
342-
if platform.system() == "Darwin" and os.getenv("TERM_PROGRAM") == "Apple_Terminal":
346+
if self.is_apple_terminal:
343347
os.write(self.output_fd, b"\033[?7l")
344348

345349
self.screen = []
@@ -370,7 +374,7 @@ def restore(self):
370374
self.flushoutput()
371375
tcsetattr(self.input_fd, termios.TCSADRAIN, self.__svtermstate)
372376

373-
if platform.system() == "Darwin" and os.getenv("TERM_PROGRAM") == "Apple_Terminal":
377+
if self.is_apple_terminal:
374378
os.write(self.output_fd, b"\033[?7h")
375379

376380
if hasattr(self, "old_sigwinch"):

Lib/test/support/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2898,7 +2898,7 @@ def force_color(color: bool):
28982898
from .os_helper import EnvironmentVarGuard
28992899

29002900
with (
2901-
swap_attr(_colorize, "can_colorize", lambda file=None: color),
2901+
swap_attr(_colorize, "can_colorize", lambda *, file=None: color),
29022902
EnvironmentVarGuard() as env,
29032903
):
29042904
env.unset("FORCE_COLOR", "NO_COLOR", "PYTHON_COLORS")

Lib/test/test_pyrepl/test_unix_console.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,3 +303,12 @@ def test_getheightwidth_with_invalid_environ(self, _os_write):
303303
self.assertIsInstance(console.getheightwidth(), tuple)
304304
os.environ = []
305305
self.assertIsInstance(console.getheightwidth(), tuple)
306+
307+
@unittest.skipUnless(sys.platform == "darwin", "requires macOS")
308+
def test_restore_with_invalid_environ_on_macos(self, _os_write):
309+
# gh-128636 for macOS
310+
console = UnixConsole(term="xterm")
311+
with os_helper.EnvironmentVarGuard():
312+
os.environ = []
313+
console.prepare() # needed to call restore()
314+
console.restore() # this should succeed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix crash in PyREPL when os.environ is overwritten with an invalid value for
2+
mac

0 commit comments

Comments
 (0)