From 9f6085801d43b55486bce60c79453290c870e2df Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 21 Jul 2025 09:58:45 +0200 Subject: [PATCH 1/3] Use bytes.index instead of comparing each byte --- Lib/_pyrepl/terminfo.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Lib/_pyrepl/terminfo.py b/Lib/_pyrepl/terminfo.py index 063a285bb9900c..cd645b5b905fc5 100644 --- a/Lib/_pyrepl/terminfo.py +++ b/Lib/_pyrepl/terminfo.py @@ -437,10 +437,8 @@ def _parse_terminfo_file(self, terminal_name: str) -> None: strings.append(CANCELLED_STRING) elif off < len(string_table): # Find null terminator - end = off - while end < len(string_table) and string_table[end] != 0: - end += 1 - if end <= len(string_table): + end = string_table.find(0, off) + if end >= 0: strings.append(string_table[off:end]) else: strings.append(ABSENT_STRING) From 71ae3c00d7f64abf2d7948b45f20e16dafb26b46 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 21 Jul 2025 10:36:29 +0200 Subject: [PATCH 2/3] Skip parsing/storing stuff we don't need --- Lib/_pyrepl/terminfo.py | 99 +++++++++++++---------------------------- 1 file changed, 30 insertions(+), 69 deletions(-) diff --git a/Lib/_pyrepl/terminfo.py b/Lib/_pyrepl/terminfo.py index cd645b5b905fc5..b3ee67027471f0 100644 --- a/Lib/_pyrepl/terminfo.py +++ b/Lib/_pyrepl/terminfo.py @@ -322,10 +322,6 @@ class TermInfo: terminal_name: str | bytes | None fallback: bool = True - _names: list[str] = field(default_factory=list) - _booleans: list[int] = field(default_factory=list) - _numbers: list[int] = field(default_factory=list) - _strings: list[bytes | None] = field(default_factory=list) _capabilities: dict[str, bytes] = field(default_factory=dict) def __post_init__(self) -> None: @@ -362,9 +358,12 @@ def __post_init__(self) -> None: def _parse_terminfo_file(self, terminal_name: str) -> None: """Parse a terminfo file. + Populate the _capabilities dict for easy retrieval + Based on ncurses implementation in: - ncurses/tinfo/read_entry.c:_nc_read_termtype() - ncurses/tinfo/read_entry.c:_nc_read_file_entry() + - ncurses/tinfo/lib_ti.c:tigetstr() """ data = _read_terminfo_file(terminal_name) too_short = f"TermInfo file for {terminal_name!r} too short" @@ -377,53 +376,36 @@ def _parse_terminfo_file(self, terminal_name: str) -> None: ) if magic == MAGIC16: - number_format = " len(data): - raise ValueError(too_short) - names = data[offset : offset + name_size - 1].decode( - "ascii", errors="ignore" - ) + # Skip data than PyREPL doesn't need: + # - names (`|`-separated ASCII strings) + # - boolean capabilities (bytes with value 0 or 1) + # - numbers (little-endian integers, `number_size` bytes each) offset += name_size - - # Read boolean capabilities - if offset + bool_count > len(data): - raise ValueError(too_short) - booleans = list(data[offset : offset + bool_count]) offset += bool_count - - # Align to even byte boundary for numbers if offset % 2: + # Align to even byte boundary for numbers offset += 1 - - # Read numeric capabilities - numbers = [] - for i in range(num_count): - if offset + number_size > len(data): - raise ValueError(too_short) - num = struct.unpack( - number_format, data[offset : offset + number_size] - )[0] - numbers.append(num) - offset += number_size + offset += num_count * number_size + if offset > len(data): + raise ValueError(too_short) # Read string offsets - string_offsets = [] - for i in range(str_count): - if offset + 2 > len(data): - raise ValueError(too_short) - off = struct.unpack(" len(data): + raise ValueError(too_short) + string_offset_data = data[offset:end_offset] + string_offsets = [ + off for [off] in struct.iter_unpack(" len(data): @@ -431,51 +413,30 @@ def _parse_terminfo_file(self, terminal_name: str) -> None: string_table = data[offset : offset + str_size] # Extract strings from string table - strings: list[bytes | None] = [] - for off in string_offsets: + capabilities = {} + for cap, off in zip(_STRING_CAPABILITY_NAMES, string_offsets): if off < 0: - strings.append(CANCELLED_STRING) + # CANCELLED_STRING; we do not store those + continue elif off < len(string_table): # Find null terminator end = string_table.find(0, off) if end >= 0: - strings.append(string_table[off:end]) - else: - strings.append(ABSENT_STRING) - else: - strings.append(ABSENT_STRING) + capabilities[cap] = string_table[off:end] + # in other cases this is ABSENT_STRING; we don't store those. - self._names = names.split("|") - self._booleans = booleans - self._numbers = numbers - self._strings = strings + # Note: we don't support extended capabilities since PyREPL doesn't + # need them. + + self._capabilities = capabilities def get(self, cap: str) -> bytes | None: """Get terminal capability string by name. - - Based on ncurses implementation in: - - ncurses/tinfo/lib_ti.c:tigetstr() - - The ncurses version searches through compiled terminfo data structures. - This version first checks parsed terminfo data, then falls back to - hardcoded capabilities. """ if not isinstance(cap, str): raise TypeError(f"`cap` must be a string, not {type(cap)}") - if self._capabilities: - # Fallbacks populated, use them - return self._capabilities.get(cap) - - # Look up in standard capabilities first - if cap in _STRING_CAPABILITY_NAMES: - index = _STRING_CAPABILITY_NAMES[cap] - if index < len(self._strings): - return self._strings[index] - - # Note: we don't support extended capabilities since PyREPL doesn't - # need them. - return None + return self._capabilities.get(cap) def tparm(cap_bytes: bytes, *params: int) -> bytes: From 086b761fe5f081805e368c6d5c76dd21ddedc1f1 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 21 Jul 2025 11:57:04 +0200 Subject: [PATCH 3/3] Remove _STRING_CAPABILITY_NAMES --- Lib/_pyrepl/terminfo.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/_pyrepl/terminfo.py b/Lib/_pyrepl/terminfo.py index b3ee67027471f0..d02ef69cce0bd8 100644 --- a/Lib/_pyrepl/terminfo.py +++ b/Lib/_pyrepl/terminfo.py @@ -71,7 +71,6 @@ "OTGV", "OTGC","meml", "memu", "box1" ) # fmt: on -_STRING_CAPABILITY_NAMES = {name: i for i, name in enumerate(_STRING_NAMES)} def _get_terminfo_dirs() -> list[Path]: @@ -414,7 +413,7 @@ def _parse_terminfo_file(self, terminal_name: str) -> None: # Extract strings from string table capabilities = {} - for cap, off in zip(_STRING_CAPABILITY_NAMES, string_offsets): + for cap, off in zip(_STRING_NAMES, string_offsets): if off < 0: # CANCELLED_STRING; we do not store those continue pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy