CommonAutoRearsh/engine/mutation_engine.py

54 lines
1.7 KiB
Python

from __future__ import annotations
from difflib import unified_diff
from pathlib import Path
from engine.models import BaselineSnapshot, TaskSpec
class MutationValidationError(ValueError):
pass
def _count_changed_lines(before: str, after: str, path: Path) -> int:
diff = unified_diff(
before.splitlines(keepends=True),
after.splitlines(keepends=True),
fromfile=f"{path.as_posix()} (before)",
tofile=f"{path.as_posix()} (after)",
)
changed_lines = 0
for line in diff:
if line.startswith(("---", "+++", "@@")):
continue
if line.startswith(("+", "-")):
changed_lines += 1
return changed_lines
def validate_candidate_changes(task: TaskSpec, snapshot: BaselineSnapshot) -> None:
changed_files = 0
changed_lines = 0
allowed_file_types = set(task.mutation.allowed_file_types)
for path, baseline_text in snapshot.file_contents.items():
current_text = path.read_text(encoding="utf-8") if path.exists() else ""
if current_text == baseline_text:
continue
changed_files += 1
if path.suffix not in allowed_file_types:
raise MutationValidationError(f"disallowed file type: {path.suffix}")
changed_lines += _count_changed_lines(baseline_text, current_text, path)
if changed_files > task.artifacts.max_files_per_iteration:
raise MutationValidationError(
f"too many changed files: {changed_files} > {task.artifacts.max_files_per_iteration}"
)
if changed_lines > task.mutation.max_changed_lines:
raise MutationValidationError(
f"too many changed lines: {changed_lines} > {task.mutation.max_changed_lines}"
)