Skip to content

gh-135621: Remove dependency on curses from PyREPL #136758

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 21, 2025
Prev Previous commit
Next Next commit
Make terminal name validation more comprehensive
  • Loading branch information
ambv committed Jul 19, 2025
commit 9db38c79f910c766bba15195102ed290217ca54f
22 changes: 16 additions & 6 deletions Lib/_pyrepl/terminfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,18 +112,28 @@ def _get_terminfo_dirs() -> list[Path]:
return [Path(d) for d in dirs if Path(d).is_dir()]


def _read_terminfo_file(terminal_name: str) -> bytes:
"""Find and read terminfo file for given terminal name.

Terminfo files are stored in directories using the first character
of the terminal name as a subdirectory.
"""
def _validate_terminal_name_or_raise(terminal_name: str) -> None:
if not isinstance(terminal_name, str):
raise TypeError("`terminal_name` must be a string")

if not terminal_name:
raise ValueError("`terminal_name` cannot be empty")

if "\x00" in terminal_name:
raise ValueError("NUL character found in `terminal_name`")

t = Path(terminal_name)
if len(t.parts) > 1:
raise ValueError("`terminal_name` cannot contain path separators")


def _read_terminfo_file(terminal_name: str) -> bytes:
"""Find and read terminfo file for given terminal name.

Terminfo files are stored in directories using the first character
of the terminal name as a subdirectory.
"""
_validate_terminal_name_or_raise(terminal_name)
first_char = terminal_name[0].lower()
filename = terminal_name

Expand Down
13 changes: 13 additions & 0 deletions Lib/test/test_pyrepl/test_terminfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,3 +652,16 @@ def test_terminfo_fallback(self):
self.assertIsNotNone(
bel, "PyREPL should provide basic capabilities after fallback"
)

def test_invalid_terminal_names(self):
cases = [
(42, TypeError),
("", ValueError),
("w\x00t", ValueError),
(f"..{os.sep}name", ValueError),
]

for term, exc in cases:
with self.subTest(term=term):
with self.assertRaises(exc):
terminfo._validate_terminal_name_or_raise(term)