- I-1 (R-23): readers/_limits.py — сумма ZipInfo.file_size ≤ 10 × PARSE_MAX_BYTES (параметр read_book от OsvParser), потоковый скан <mergeCell ref> до openpyxl и площадь merged после чтения любого формата (MERGED_AREA_LIMIT = 200 000; реальный максимум 10 090) → UNSUPPORTED_FORMAT; дедлайн в циклах detect (поиск строки групп, дополнительные колонки); README «Лимиты» исправлен, «Известные ограничения» (defusedxml). - I-2: OsvParser логирует стек непредвиденной ошибки (loguru ERROR), UNREADABLE — WARNING без стека. - M-1: ACCOUNT_RE с re.ASCII | re.DOTALL (многострочное название после запятой, только цифры 0–9). - M-3 (R-24): правило детектора ИНН не дополняет 9/11 цифр нулём; дополнение — только при извлечении (numbers.inn_digits) из колонки, выбранной по заголовку или по строгому правилу. - T9.1: тесты R-21 (свёрнутый родитель на противоположной стороне; (0,0) при детях (X, X)). - T1.1: хелперы фикстур пропускают скрытые файлы/каталоги и lock-файлы Excel (~$…). - T12.3: golden-update не удаляет снимки части без исходного каталога (osv/synthetic или реальные файлы). Golden-снимки не изменились (82/82). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
70 lines
3.5 KiB
Python
70 lines
3.5 KiB
Python
"""Перезапись golden-снимков разбора ОСВ и diff изменившихся (design D-10).
|
||
|
||
Из каталога `api/`: `ENV_FILE=.env.test .venv/bin/python scripts/golden_update.py [--check]` (то же — `make
|
||
golden-update`, из корня репозитория). Файлы — `fixtures/osv/**` включая `synthetic/`; снимки — в
|
||
`fixtures/golden/expected_osv_parse/` (зеркало `osv/`). Без флага снимки перезаписываются, для изменившихся печатается
|
||
unified diff; `--check` только сравнивает (код возврата 1, если снимки расходятся с разбором). Снимки части, для
|
||
которой нет исходного каталога (`osv/synthetic` отсутствует или пуст, нет реальных файлов), не удаляются.
|
||
|
||
Снимок — регрессия, а не оракул: перед коммитом в репозиторий fixtures просмотрите diff (`git -C ../fixtures diff`)
|
||
и убедитесь, что каждое изменение — следствие намеренной правки парсера.
|
||
"""
|
||
|
||
import argparse
|
||
import sys
|
||
from collections.abc import Sequence
|
||
from pathlib import Path
|
||
|
||
API_ROOT = Path(__file__).resolve().parents[1]
|
||
sys.path[:0] = [str(API_ROOT), str(API_ROOT / "tests")]
|
||
|
||
from fixtures_helpers import ( # noqa: E402
|
||
fixtures_skip_reason,
|
||
osv_fixture_files,
|
||
resolve_fixtures_dir,
|
||
synthetic_files,
|
||
)
|
||
from golden_helpers import sync_snapshots # noqa: E402
|
||
|
||
from src.parser import get_parser # noqa: E402
|
||
|
||
EXIT_OK = 0
|
||
EXIT_DIFFERS = 1
|
||
EXIT_NO_FIXTURES = 3
|
||
|
||
|
||
def main(argv: Sequence[str] | None = None) -> int:
|
||
parser = argparse.ArgumentParser(description="Golden-снимки разбора ОСВ: перезапись + diff, либо --check")
|
||
parser.add_argument("--check", action="store_true", help="только сравнить снимки с разбором, ничего не писать")
|
||
options = parser.parse_args(argv)
|
||
|
||
reason = fixtures_skip_reason()
|
||
if reason is not None:
|
||
print(reason, file=sys.stderr)
|
||
return EXIT_NO_FIXTURES
|
||
root = resolve_fixtures_dir()
|
||
files = [*osv_fixture_files(root), *synthetic_files(root)]
|
||
report = sync_snapshots(root, files, get_parser(), write=not options.check)
|
||
|
||
for diff in report.diffs:
|
||
sys.stdout.write(diff if diff.endswith("\n") else diff + "\n")
|
||
mode = "режим --check, ничего не записано" if options.check else "снимки записаны"
|
||
print(
|
||
f"файлов: {len(files)}; без изменений: {report.unchanged}; новых: {len(report.created)}; "
|
||
f"изменено: {len(report.changed)}; лишних снимков: {len(report.orphans)}; сохранено без исходного каталога: "
|
||
f"{len(report.protected)} ({mode})",
|
||
file=sys.stderr,
|
||
)
|
||
for orphan in report.orphans:
|
||
print(f"лишний снимок{'' if options.check else ' удалён'}: {orphan}", file=sys.stderr)
|
||
for kept in report.protected:
|
||
print(f"снимок сохранён (нет исходного каталога): {kept}", file=sys.stderr)
|
||
return EXIT_DIFFERS if options.check and report.differs else EXIT_OK
|
||
|
||
|
||
if __name__ == "__main__":
|
||
reconfigure = getattr(sys.stdout, "reconfigure", None)
|
||
if reconfigure is not None:
|
||
reconfigure(encoding="utf-8")
|
||
raise SystemExit(main())
|