""" Быстрый шлюз проверки SDR Rover Link. Проверяет за одну-две секунды то, что ломается чаще всего: 1. Синтаксис всех файлов в protocol, tests и tools. 2. Импорт каждого модуля protocol. 3. Функциональные проверки тех лаб, которым не нужны данные эксперимента. Полный прогон лабы шлюз не заменяет. Он отвечает на один вопрос: не сломана ли кодовая база прямо сейчас. Запуск: python tools/quick_gate.py """ from __future__ import annotations import ast import importlib import inspect from pathlib import Path import sys import traceback PROJECT_ROOT = Path(__file__).resolve().parent.parent CHECKED_DIRECTORIES = ("protocol", "tests", "experiments", "tools") def check_syntax() -> list[str]: """Разобрать все файлы Python и вернуть список сообщений об ошибках.""" failures: list[str] = [] for directory in CHECKED_DIRECTORIES: for path in sorted((PROJECT_ROOT / directory).glob("*.py")): try: ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) except SyntaxError as error: relative = path.relative_to(PROJECT_ROOT) failures.append(f"{relative}:{error.lineno}: синтаксис: {error.msg}") return failures def check_protocol_imports() -> list[str]: """Импортировать каждый модуль protocol и вернуть список сообщений об ошибках.""" failures: list[str] = [] for path in sorted((PROJECT_ROOT / "protocol").glob("*.py")): if path.name == "__init__.py": continue module_name = f"protocol.{path.stem}" try: importlib.import_module(module_name) except Exception: failures.append(f"{module_name}: импорт: {traceback.format_exc(limit=3).strip()}") return failures def check_functional_tests() -> tuple[list[str], int, int]: """ Выполнить функциональные проверки лаб, не требующие данных эксперимента. Возвращает: failures: Список сообщений об ошибках. executed: Количество выполненных лаб. skipped: Количество пропущенных лаб. """ failures: list[str] = [] executed = 0 skipped = 0 for path in sorted((PROJECT_ROOT / "experiments").glob("lab*.py")): source = path.read_text(encoding="utf-8") if "def run_functional_tests" not in source: continue module_name = f"experiments.{path.stem}" try: module = importlib.import_module(module_name) except Exception: failures.append(f"{module_name}: импорт: {traceback.format_exc(limit=3).strip()}") continue run_functional_tests = module.run_functional_tests parameters = inspect.signature(run_functional_tests).parameters required = [name for name, parameter in parameters.items() if parameter.default is parameter.empty] if required: skipped += 1 continue try: run_functional_tests() except Exception: failures.append(f"{module_name}: проверки: {traceback.format_exc(limit=3).strip()}") continue executed += 1 return failures, executed, skipped def main() -> int: """Выполнить шлюз и вернуть код возврата процесса.""" sys.path.insert(0, str(PROJECT_ROOT)) syntax_failures = check_syntax() if syntax_failures: print("Шлюз не пройден: ошибки синтаксиса") for failure in syntax_failures: print(f" {failure}") return 1 import_failures = check_protocol_imports() test_failures, executed, skipped = check_functional_tests() failures = import_failures + test_failures if failures: print("Шлюз не пройден") for failure in failures: print(f" {failure}") return 1 print(f"Шлюз пройден: синтаксис в порядке, protocol импортируется, функциональных проверок выполнено {executed}, пропущено {skipped}") return 0 if __name__ == "__main__": raise SystemExit(main())