Spaces:
Sleeping
Sleeping
Create src/macg/tools/python_runner.py
Browse files
src/macg/tools/python_runner.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
import os
|
| 3 |
+
import subprocess
|
| 4 |
+
import tempfile
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
|
| 7 |
+
@dataclass
|
| 8 |
+
class RunResult:
|
| 9 |
+
ok: bool
|
| 10 |
+
stdout: str
|
| 11 |
+
stderr: str
|
| 12 |
+
returncode: int
|
| 13 |
+
|
| 14 |
+
def run_pytest(module_name: str, code: str, tests: str, timeout_s: int = 15) -> RunResult:
|
| 15 |
+
"""
|
| 16 |
+
Minimal safety:
|
| 17 |
+
- runs in temp dir
|
| 18 |
+
- timeout
|
| 19 |
+
NOTE: still executes arbitrary code. For “serious” safety, use containers/sandboxing.
|
| 20 |
+
"""
|
| 21 |
+
with tempfile.TemporaryDirectory() as d:
|
| 22 |
+
mod_path = os.path.join(d, f"{module_name}.py")
|
| 23 |
+
test_path = os.path.join(d, "test_solution.py")
|
| 24 |
+
with open(mod_path, "w", encoding="utf-8") as f:
|
| 25 |
+
f.write(code)
|
| 26 |
+
with open(test_path, "w", encoding="utf-8") as f:
|
| 27 |
+
f.write(tests)
|
| 28 |
+
|
| 29 |
+
cmd = ["python", "-m", "pytest", "-q"]
|
| 30 |
+
try:
|
| 31 |
+
p = subprocess.run(
|
| 32 |
+
cmd,
|
| 33 |
+
cwd=d,
|
| 34 |
+
capture_output=True,
|
| 35 |
+
text=True,
|
| 36 |
+
timeout=timeout_s,
|
| 37 |
+
env={**os.environ, "PYTHONPATH": d},
|
| 38 |
+
)
|
| 39 |
+
ok = (p.returncode == 0)
|
| 40 |
+
return RunResult(ok=ok, stdout=p.stdout, stderr=p.stderr, returncode=p.returncode)
|
| 41 |
+
except subprocess.TimeoutExpired as e:
|
| 42 |
+
return RunResult(ok=False, stdout=e.stdout or "", stderr="Timeout running pytest.", returncode=124)
|