archinstall/test_tooling/check_binding_descriptions.py

67 lines
2.2 KiB
Python
Executable File

#!/usr/bin/env python3
"""Verify archinstall/tui/binding_descriptions.py against the installed textual.
Run manually after a textual upgrade or after introducing a new textual
widget in archinstall/tui/components.py:
python3 test_tooling/check_binding_descriptions.py
The script parses the tr_noop() entries from binding_descriptions.py (no
archinstall import needed) and compares them with the MRO-merged binding
descriptions of the textual classes used in components.py. Exits non-zero
on any difference and prints the exact missing/stale entries.
"""
import re
import sys
from pathlib import Path
from textual.app import App
from textual.screen import Screen
from textual.widgets import Button, DataTable, HelpPanel, Input, OptionList, SelectionList
# Must mirror the textual widget classes used in archinstall/tui/components.py.
# Extend when a new widget is introduced there.
TEXTUAL_CLASSES = (App, Screen, Button, DataTable, HelpPanel, Input, OptionList, SelectionList)
MODULE_PATH = Path(__file__).parent.parent / 'archinstall/tui/binding_descriptions.py'
def live_descriptions() -> set[str]:
descriptions: set[str] = set()
# Walk the full MRO: _translate_bindings() operates on the merged bindings
# map, so descriptions inherited from ancestors are translated too.
for cls in TEXTUAL_CLASSES:
for klass in cls.__mro__:
for binding in klass.__dict__.get('BINDINGS', []):
description = getattr(binding, 'description', None)
if not description and isinstance(binding, tuple) and len(binding) > 2:
description = binding[2]
if description:
descriptions.add(str(description))
return descriptions
def listed_descriptions() -> set[str]:
content = MODULE_PATH.read_text(encoding='utf-8')
return set(re.findall(r"tr_noop\('([^']+)'\)", content))
def main() -> int:
live = live_descriptions()
listed = listed_descriptions()
if live == listed:
print(f'OK: {len(listed)} descriptions in sync with installed textual')
return 0
for description in sorted(live - listed):
print(f'missing from binding_descriptions.py: {description}')
for description in sorted(listed - live):
print(f'stale in binding_descriptions.py: {description}')
return 1
if __name__ == '__main__':
sys.exit(main())