"""Общий ключ `--set имя=значение` для любых полей `Params`. У `evaluate.py` и `make_benchmark.py` уже по два десятка отдельных флагов, по одному на каждую настройку конвейера, и каждый новый ключ в `Params` требовал ещё двух правок в каждом инструменте. Через `--set` любое поле меняется без правки инструментов: `--set h_lo_core=0.16 --set lying_exempt=1`. Тип берётся из описания поля, поэтому опечатка в имени или в значении падает сразу, с понятным сообщением, а не тихо превращается в строку. """ from __future__ import annotations import dataclasses _TRUE = {"1", "true", "yes", "on", "да"} _FALSE = {"0", "false", "no", "off", "нет"} def add_argument(ap) -> None: ap.add_argument("--set", action="append", default=[], metavar="ИМЯ=ЗНАЧЕНИЕ", help="поменять любое поле Params; можно несколько раз") def apply(over: dict, sets: list[str], params_cls) -> dict: """Дописать в `over` значения из `--set`, приведя их к типу поля.""" types = {f.name: str(f.type) for f in dataclasses.fields(params_cls)} for item in sets: if "=" not in item: raise SystemExit(f"--set ждёт имя=значение, а получил: {item!r}") name, raw = (s.strip() for s in item.split("=", 1)) if name not in types: near = sorted(n for n in types if name.split("_")[0] in n)[:6] raise SystemExit(f"в Params нет поля {name!r}" + (f"; похожие: {', '.join(near)}" if near else "")) kind = types[name] if kind == "bool": low = raw.lower() if low not in _TRUE | _FALSE: raise SystemExit(f"{name}: ждал да/нет, а получил {raw!r}") over[name] = low in _TRUE elif kind == "int": over[name] = int(raw) elif kind == "float": over[name] = float(raw) else: over[name] = raw return over