Spaces:
Build error
Build error
| """Helpers for deterministic pytest execution in temp sandboxes.""" | |
| from __future__ import annotations | |
| import json | |
| import subprocess | |
| import sys | |
| import tempfile | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| from typing import Iterable | |
| class PytestExecution: | |
| """Exact pytest execution summary.""" | |
| passed: int | |
| failed: int | |
| total: int | |
| timed_out: bool | |
| output: str | |
| def _test_module_source(tests: Iterable[str]) -> str: | |
| """Build a valid pytest module from expression-style or full test snippets.""" | |
| blocks: list[str] = ["from candidate import * # noqa: F401,F403"] | |
| for index, test in enumerate(tests, start=1): | |
| snippet = str(test).strip() | |
| if not snippet: | |
| continue | |
| if snippet.startswith("def test_"): | |
| blocks.append(snippet) | |
| continue | |
| blocks.append( | |
| "\n".join( | |
| [ | |
| f"def test_case_{index:03d}():", | |
| f" assert {snippet}", | |
| ] | |
| ) | |
| ) | |
| return "\n\n".join(blocks) or "def test_placeholder():\n assert True\n" | |
| def _runner_script() -> str: | |
| return """import json | |
| import pathlib | |
| import pytest | |
| class Collector: | |
| def __init__(self) -> None: | |
| self.passed = 0 | |
| self.failed = 0 | |
| def pytest_runtest_logreport(self, report): | |
| if report.when != "call": | |
| return | |
| if report.passed: | |
| self.passed += 1 | |
| elif report.failed: | |
| self.failed += 1 | |
| collector = Collector() | |
| exit_code = pytest.main(["-q", "test_candidate.py"], plugins=[collector]) | |
| payload = { | |
| "passed": collector.passed, | |
| "failed": collector.failed, | |
| "exit_code": int(exit_code), | |
| } | |
| pathlib.Path("pytest_results.json").write_text(json.dumps(payload), encoding="utf-8") | |
| """ | |
| def run_pytest_suite(candidate_code: str, tests: Iterable[str], timeout_s: float = 3.0) -> PytestExecution: | |
| """Run a pytest suite against candidate.py and return structured results.""" | |
| test_cases = list(tests) | |
| try: | |
| with tempfile.TemporaryDirectory(prefix="python-code-review-") as temp_dir: | |
| temp_path = Path(temp_dir) | |
| (temp_path / "candidate.py").write_text(candidate_code, encoding="utf-8") | |
| (temp_path / "test_candidate.py").write_text(_test_module_source(test_cases), encoding="utf-8") | |
| (temp_path / "runner.py").write_text(_runner_script(), encoding="utf-8") | |
| try: | |
| completed = subprocess.run( | |
| [sys.executable, "runner.py"], | |
| cwd=temp_path, | |
| capture_output=True, | |
| text=True, | |
| timeout=timeout_s, | |
| check=False, | |
| ) | |
| except subprocess.TimeoutExpired as exc: | |
| output = (exc.stdout or "") + (exc.stderr or "") | |
| return PytestExecution( | |
| passed=0, | |
| failed=max(len(test_cases), 1), | |
| total=max(len(test_cases), 1), | |
| timed_out=True, | |
| output=(output or "pytest timed out").strip(), | |
| ) | |
| result_path = temp_path / "pytest_results.json" | |
| if not result_path.exists(): | |
| output = (completed.stdout or "") + (completed.stderr or "") | |
| total = max(len(test_cases), 1) | |
| return PytestExecution( | |
| passed=0, | |
| failed=total, | |
| total=total, | |
| timed_out=False, | |
| output=output.strip(), | |
| ) | |
| try: | |
| payload = json.loads(result_path.read_text(encoding="utf-8")) | |
| except Exception as exc: | |
| output = ((completed.stdout or "") + (completed.stderr or "")).strip() | |
| return PytestExecution( | |
| passed=0, | |
| failed=max(len(test_cases), 1), | |
| total=max(len(test_cases), 1), | |
| timed_out=False, | |
| output=(output or str(exc)).strip(), | |
| ) | |
| passed = int(payload.get("passed", 0)) | |
| failed = int(payload.get("failed", 0)) | |
| total = max(passed + failed, len(test_cases)) | |
| output = ((completed.stdout or "") + (completed.stderr or "")).strip() | |
| return PytestExecution( | |
| passed=passed, | |
| failed=failed, | |
| total=total, | |
| timed_out=False, | |
| output=output, | |
| ) | |
| except Exception as exc: | |
| return PytestExecution( | |
| passed=0, | |
| failed=max(len(test_cases), 1), | |
| total=max(len(test_cases), 1), | |
| timed_out=False, | |
| output=str(exc), | |
| ) | |