47 lines
2.3 KiB
Python
47 lines
2.3 KiB
Python
"""Общий ключ `--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
|