#!/usr/bin/env python3 # Предполётная проверка кампании: каждый сценарий запускается на два шага. # # python preflight.py # проверить все # python preflight.py --group I # только группу # # Ловит опечатки в ключах, несовместимые сочетания и пределы железа ДО того, как кампания # уйдёт считать на девяносто часов. Не бесплатная привычка, а окупившаяся: именно так # выяснилось, что вся группа сверхмелких сеток падала на пределе GPU в 65535 рабочих групп # на измерение, а девять прогонов группы F передавали --body-x дважды. # # Физику проверка не трогает вовсе: два шага — это ровно «запустилось и не упало». import argparse import json import os import subprocess import sys HERE = os.path.dirname(os.path.abspath(__file__)) BIN = os.environ.get("KBC2D_BIN") or os.path.join( HERE, "..", "target", "release", "kbc2d" + (".exe" if os.name == "nt" else "")) # Всё, что пишет файлы, выкидываем: проверяем запуск, а не вывод. Ключ идёт со значением, # поэтому следующий за ним аргумент тоже пропускается. DROP = {"--gif", "--xt", "--case-csv", "--gif-field", "--gif-downsample", "--gif-scale", "--gif-every", "--gif-fps", "--series-every"} def main(): ap = argparse.ArgumentParser() ap.add_argument("--group", help="только эта группа, например I") ap.add_argument("--scenarios", default=os.path.join(HERE, "scenarios.json")) args = ap.parse_args() runs = json.load(open(args.scenarios, encoding="utf-8"))["runs"] if args.group: runs = [r for r in runs if r["group"].upper() == args.group.upper()] if not runs: print("под выборку не попал ни один прогон") return 0 if not os.path.exists(BIN): print(f"не найден бинарь решателя: {BIN}") return 1 bad = [] for i, r in enumerate(runs, 1): argv, skip = [], False for a in r["args"]: if skip: skip = False continue if a in DROP: skip = True continue argv.append(a) if "--steps" in argv: argv[argv.index("--steps") + 1] = "2" else: argv += ["--steps", "2"] p = subprocess.run([BIN] + argv + ["--verbose", "quiet", "--report-every", "0"], capture_output=True, cwd=HERE) ok = p.returncode == 0 print("." if ok else "X", end="", flush=True) if not ok: tail = (p.stdout + p.stderr).decode("utf-8", "replace").strip().splitlines() bad.append((r["id"], tail[-1] if tail else "(без вывода)")) if i % 40 == 0: print(f" {i}/{len(runs)}", flush=True) print(f"\n\nпроверено сценариев: {len(runs)}, не запустились: {len(bad)}") for id_, msg in bad: print(f" {id_:<28} {msg}") return 1 if bad else 0 if __name__ == "__main__": sys.exit(main())