53 lines
1.7 KiB
Python
53 lines
1.7 KiB
Python
|
|
"""Phase-1 fitness oracle: score a ``.dom`` via Urb's ``urb-fitness.pl``.
|
||
|
|
|
||
|
|
This is the only throwaway component. It shells out to the Perl evaluator so we
|
||
|
|
can validate the Python search core against the trusted fitness before porting
|
||
|
|
fitness to Python (Phase 2). ``urb-fitness.pl`` reads ``patterns.config`` from
|
||
|
|
its working directory, so the ``.dom`` must live beside the programme config.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import os
|
||
|
|
import subprocess
|
||
|
|
from dataclasses import dataclass
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
DEFAULT_URB_ROOT = Path("/home/bruno/src/urb")
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass
|
||
|
|
class Score:
|
||
|
|
fitness: float
|
||
|
|
fails: str # raw .fails content (YAML and/or plain lines)
|
||
|
|
|
||
|
|
@property
|
||
|
|
def n_fails(self) -> int:
|
||
|
|
return sum(1 for line in self.fails.splitlines() if line.strip() and line.strip() != "---")
|
||
|
|
|
||
|
|
|
||
|
|
def score(dom_path: str | Path, urb_root: str | Path = DEFAULT_URB_ROOT) -> Score:
|
||
|
|
dom_path = Path(dom_path).resolve()
|
||
|
|
urb_root = Path(urb_root).resolve()
|
||
|
|
score_file = Path(f"{dom_path}.score")
|
||
|
|
fails_file = Path(f"{dom_path}.fails")
|
||
|
|
for f in (score_file, fails_file):
|
||
|
|
f.unlink(missing_ok=True)
|
||
|
|
|
||
|
|
env = {**os.environ, "DEBUG": "1"}
|
||
|
|
proc = subprocess.run(
|
||
|
|
["perl", f"-I{urb_root}/lib", str(urb_root / "bin" / "urb-fitness.pl"), dom_path.name],
|
||
|
|
cwd=dom_path.parent,
|
||
|
|
env=env,
|
||
|
|
capture_output=True,
|
||
|
|
text=True,
|
||
|
|
)
|
||
|
|
if not score_file.exists():
|
||
|
|
raise RuntimeError(
|
||
|
|
f"urb-fitness.pl produced no score for {dom_path}\n"
|
||
|
|
f"stdout:\n{proc.stdout}\nstderr:\n{proc.stderr}"
|
||
|
|
)
|
||
|
|
fitness = float(score_file.read_text().strip())
|
||
|
|
fails = fails_file.read_text() if fails_file.exists() else ""
|
||
|
|
return Score(fitness=fitness, fails=fails)
|