ba57b7fd21
Carves the first slice out of the monolithic rc-jav.py (now 2017
lines, was 2230). Two new modules:
rcjav/model.py FileEntry dataclass — the one shared shape that
every other submodule will need.
rcjav/ids.py Single source of truth for everything that
influences a FileEntry.jav_id: PRIMARY_ID_RE,
FALLBACK_ID_RE, COMPOUND_ID_RE, BUILTIN_PART_RES,
configure_part_patterns, detect_part,
detect_part_from_stem, part_key, extract_id,
normalize_id, describe_id_match, expand_range,
plus the supporting "private" regexes
(_BRACKET_ID_RE, _RESOLUTION_TAG_RE, etc.) that
other code in rc-jav.py still reads.
rcjav/__init__.py re-exports the public surface so future external
consumers can `from rcjav import extract_id` without caring which
submodule it lives in.
rc-jav.py drops the inline ID block and pulls everything from
rcjav.ids via a single import statement. PART_RES is intentionally
NOT imported — it's mutated by configure_part_patterns at runtime, so
a captured top-level reference would go stale. A small helper
`_current_part_res()` reads it dynamically via `_rcjav_ids.PART_RES`.
fixtures/run.py fix: synthesized importlib module name changed from
"rcjav" (which now collides with the real package directory) to
"rcjav_script". Also prepends ROOT to sys.path so rc-jav.py's
`from rcjav.model import …` resolves when run as
`python fixtures/run.py`.
Verified:
- python rc-jav.py --help → usage banner prints
- python fixtures/run.py → 17/17 cases pass
- python -m unittest tests.test_rules → 5/5 OK
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
78 lines
2.2 KiB
Python
78 lines
2.2 KiB
Python
"""Run the shared JAV-ID fixture corpus against rc-jav.py.
|
|
|
|
Exits non-zero if any fixture case fails. No third-party dependencies.
|
|
|
|
Usage:
|
|
python fixtures/run.py
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
FIXTURES = Path(__file__).resolve().parent
|
|
|
|
# rc-jav.py now imports from the `rcjav/` package at ROOT, so the parent dir
|
|
# must be on sys.path before we exec it.
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
# Synthesized module name avoids collision with the real `rcjav` package
|
|
# directory (rc-jav.py now imports from it).
|
|
SPEC = importlib.util.spec_from_file_location("rcjav_script", ROOT / "rc-jav.py")
|
|
RCJAV = importlib.util.module_from_spec(SPEC)
|
|
sys.modules[SPEC.name] = RCJAV
|
|
SPEC.loader.exec_module(RCJAV)
|
|
|
|
|
|
def _load(name: str) -> dict:
|
|
with (FIXTURES / name).open("r", encoding="utf-8") as f:
|
|
return json.load(f)
|
|
|
|
|
|
def _run(label: str, cases: list[dict], fn) -> tuple[int, int]:
|
|
passed = 0
|
|
failed = 0
|
|
for case in cases:
|
|
got = fn(case["input"])
|
|
if got == case["expected"]:
|
|
passed += 1
|
|
else:
|
|
failed += 1
|
|
print(f" FAIL [{label}] {case['name']!r}")
|
|
print(f" input = {case['input']!r}")
|
|
print(f" expected = {case['expected']!r}")
|
|
print(f" got = {got!r}")
|
|
return passed, failed
|
|
|
|
|
|
def main() -> int:
|
|
total_passed = 0
|
|
total_failed = 0
|
|
|
|
for filename, fn_name, fn in [
|
|
("filename-extraction.json", "extract_id", RCJAV.extract_id),
|
|
("shared-normalization.json", "normalize_id", RCJAV.normalize_id),
|
|
]:
|
|
doc = _load(filename)
|
|
cases = doc.get("cases", [])
|
|
print(f"\n{filename} -> rcjav.{fn_name} ({len(cases)} cases)")
|
|
p, f = _run(filename, cases, fn)
|
|
total_passed += p
|
|
total_failed += f
|
|
print(f" {p} passed | {f} failed")
|
|
|
|
print()
|
|
if total_failed:
|
|
print(f"FAILED: {total_failed} of {total_passed + total_failed} cases")
|
|
return 1
|
|
print(f"OK: all {total_passed} cases passed")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|