Spaces:
Sleeping
Sleeping
Download src/spreadsheetbench_env/executor.py from emrekuruu/spreadsheetbench-openenv: direct link, hf CLI and curl.
- Browser
- Download file 9.88 kB
-
https://huggingface.co/spaces/emrekuruu/spreadsheetbench-openenv/resolve/main/src/spreadsheetbench_env/executor.py
- Command line
-
hf download hf://spaces/emrekuruu/spreadsheetbench-openenv/src/spreadsheetbench_env/executor.py
-
curl -L -o executor.py https://huggingface.co/spaces/emrekuruu/spreadsheetbench-openenv/resolve/main/src/spreadsheetbench_env/executor.py
9.88 kB
| """Bounded subprocess execution for one SpreadsheetBench session.""" | |
| from __future__ import annotations | |
| from dataclasses import dataclass, replace | |
| import os | |
| from pathlib import Path | |
| import re | |
| import shutil | |
| import signal | |
| import subprocess | |
| import sys | |
| import textwrap | |
| import threading | |
| _PATH_ASSIGNMENT = re.compile( | |
| r"^\s*(INPUT_PATH|OUTPUT_PATH)\s*=\s*.+$", | |
| re.MULTILINE, | |
| ) | |
| _SAFE_CASE_NAME = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]*$") | |
| _MAX_CAPTURE_CHARS = 50_000 | |
| _UID_LEASE_LOCK = threading.Lock() | |
| _LEASED_UIDS: set[int] = set() | |
| def _lease_uid(base: int) -> int: | |
| with _UID_LEASE_LOCK: | |
| candidate = base | |
| while candidate in _LEASED_UIDS: | |
| candidate += 1 | |
| _LEASED_UIDS.add(candidate) | |
| return candidate | |
| def _release_uid(uid: int) -> None: | |
| with _UID_LEASE_LOCK: | |
| _LEASED_UIDS.discard(uid) | |
| class ExecutorLimits: | |
| """Wall-clock and POSIX resource limits for generated code.""" | |
| timeout_s: float = 120.0 | |
| cpu_seconds: int = 120 | |
| memory_bytes: int = 2 * 1024 * 1024 * 1024 | |
| file_bytes: int = 256 * 1024 * 1024 | |
| process_count: int = 64 | |
| open_files: int = 128 | |
| run_uid: int | None = None | |
| run_uid_base: int | None = None | |
| run_gid: int | None = None | |
| class ExecutionResult: | |
| """One generated-script execution result.""" | |
| ok: bool | |
| output_path: Path | None | |
| stdout: str | |
| stderr: str | |
| error: str | |
| timed_out: bool | |
| returncode: int | None | |
| def _bounded_text(value: str) -> str: | |
| if len(value) <= _MAX_CAPTURE_CHARS: | |
| return value | |
| half = _MAX_CAPTURE_CHARS // 2 | |
| return value[:half] + "\n...[subprocess output truncated]...\n" + value[-half:] | |
| def _runner_source(code: str, input_path: Path, output_path: Path) -> str: | |
| cleaned = _PATH_ASSIGNMENT.sub("", code) | |
| indented = textwrap.indent(cleaned, " ") or " pass" | |
| return ( | |
| "import traceback\n" | |
| f"INPUT_PATH = {str(input_path)!r}\n" | |
| f"OUTPUT_PATH = {str(output_path)!r}\n" | |
| "try:\n" | |
| f"{indented}\n" | |
| "except Exception:\n" | |
| " traceback.print_exc()\n" | |
| " raise SystemExit(2)\n" | |
| ) | |
| def _safe_environment(case_dir: Path, scratch_dir: Path) -> dict[str, str]: | |
| environment = { | |
| "PATH": os.environ.get("PATH") or os.defpath, | |
| "HOME": str(case_dir), | |
| "TMPDIR": str(scratch_dir), | |
| "PYTHONUNBUFFERED": "1", | |
| "OPENBLAS_NUM_THREADS": "1", | |
| "OMP_NUM_THREADS": "1", | |
| "MKL_NUM_THREADS": "1", | |
| "NUMEXPR_NUM_THREADS": "1", | |
| } | |
| for key in ("LANG", "LC_ALL", "LC_CTYPE", "PYTHONIOENCODING", "PYTHONUTF8"): | |
| value = os.environ.get(key) | |
| if value: | |
| environment[key] = value | |
| return environment | |
| def _launcher_source(limits: ExecutorLimits, runner_path: Path) -> str: | |
| """Apply limits after exec, avoiding unsafe preexec_fn work in the server.""" | |
| return ( | |
| "import os\n" | |
| "import resource\n" | |
| "import sys\n" | |
| "resource.setrlimit(resource.RLIMIT_CORE, (0, 0))\n" | |
| f"resource.setrlimit(resource.RLIMIT_CPU, ({limits.cpu_seconds}, {limits.cpu_seconds}))\n" | |
| f"memory_bytes = {limits.memory_bytes}\n" | |
| "if sys.platform != 'darwin':\n" | |
| " resource.setrlimit(resource.RLIMIT_AS, (memory_bytes, memory_bytes))\n" | |
| f"resource.setrlimit(resource.RLIMIT_FSIZE, ({limits.file_bytes}, {limits.file_bytes}))\n" | |
| f"resource.setrlimit(resource.RLIMIT_NOFILE, ({limits.open_files}, {limits.open_files}))\n" | |
| f"process_count = {limits.process_count}\n" | |
| "if sys.platform != 'darwin' and hasattr(resource, 'RLIMIT_NPROC'):\n" | |
| " resource.setrlimit(resource.RLIMIT_NPROC, (process_count, process_count))\n" | |
| f"run_gid = {limits.run_gid!r}\n" | |
| "if run_gid is not None:\n" | |
| " os.setgroups([])\n" | |
| " os.setgid(run_gid)\n" | |
| f"run_uid = {limits.run_uid!r}\n" | |
| "if run_uid is not None:\n" | |
| " os.setuid(run_uid)\n" | |
| f"runner_path = {str(runner_path)!r}\n" | |
| "os.execv(sys.executable, [sys.executable, runner_path])\n" | |
| ) | |
| class ScriptExecutor: | |
| """Own the private working tree for one connection-owned episode.""" | |
| def __init__(self, root: str | Path, *, limits: ExecutorLimits | None = None) -> None: | |
| self._root = Path(root).resolve() | |
| configured = limits or ExecutorLimits() | |
| if configured.run_uid is not None and configured.run_uid_base is not None: | |
| raise ValueError("configure either run_uid or run_uid_base, not both") | |
| self._leased_uid = ( | |
| None | |
| if configured.run_uid_base is None | |
| else _lease_uid(configured.run_uid_base) | |
| ) | |
| self._limits = replace(configured, run_uid=self._leased_uid or configured.run_uid) | |
| self._closed = False | |
| try: | |
| self._root.mkdir(parents=True, exist_ok=False, mode=0o700) | |
| except Exception: | |
| if self._leased_uid is not None: | |
| _release_uid(self._leased_uid) | |
| raise | |
| def root(self) -> Path: | |
| return self._root | |
| def leased_uid(self) -> int | None: | |
| return self._leased_uid or self._limits.run_uid | |
| def run( | |
| self, | |
| code: str, | |
| input_path: str | Path, | |
| *, | |
| case_name: str, | |
| ) -> ExecutionResult: | |
| if self._closed: | |
| raise RuntimeError("script executor is closed") | |
| if ( | |
| not _SAFE_CASE_NAME.fullmatch(case_name) | |
| or case_name in {".", ".."} | |
| or ".." in case_name | |
| ): | |
| raise ValueError(f"unsafe case name: {case_name!r}") | |
| source = Path(input_path).resolve(strict=True) | |
| case_dir = self._root / case_name | |
| case_dir.mkdir(mode=0o700) | |
| scratch_dir = case_dir / "tmp" | |
| scratch_dir.mkdir(mode=0o700) | |
| private_input = case_dir / "input.xlsx" | |
| output_path = case_dir / "output.xlsx" | |
| runner_path = case_dir / "runner.py" | |
| launcher_path = case_dir / "launcher.py" | |
| stdout_path = case_dir / "stdout.log" | |
| stderr_path = case_dir / "stderr.log" | |
| shutil.copy2(source, private_input) | |
| runner_path.write_text( | |
| _runner_source(code, private_input, output_path), | |
| encoding="utf-8", | |
| ) | |
| launcher_path.write_text( | |
| _launcher_source(self._limits, runner_path), | |
| encoding="utf-8", | |
| ) | |
| if self._limits.run_uid is not None or self._limits.run_gid is not None: | |
| uid = -1 if self._limits.run_uid is None else self._limits.run_uid | |
| gid = -1 if self._limits.run_gid is None else self._limits.run_gid | |
| os.chmod(self._root, 0o711) | |
| for path in (case_dir, scratch_dir, private_input, runner_path, launcher_path): | |
| os.chown(path, uid, gid) | |
| with stdout_path.open("w", encoding="utf-8") as stdout_file, stderr_path.open( | |
| "w", encoding="utf-8" | |
| ) as stderr_file: | |
| process = subprocess.Popen( | |
| [sys.executable, str(launcher_path)], | |
| cwd=case_dir, | |
| env=_safe_environment(case_dir, scratch_dir), | |
| stdout=stdout_file, | |
| stderr=stderr_file, | |
| text=True, | |
| start_new_session=True, | |
| ) | |
| timed_out = False | |
| try: | |
| process.wait(timeout=self._limits.timeout_s) | |
| except subprocess.TimeoutExpired: | |
| timed_out = True | |
| try: | |
| os.killpg(process.pid, signal.SIGKILL) | |
| except ProcessLookupError: | |
| pass | |
| process.wait() | |
| stdout = stdout_path.read_text(encoding="utf-8", errors="replace") | |
| stderr = stderr_path.read_text(encoding="utf-8", errors="replace") | |
| stdout = _bounded_text(stdout) | |
| stderr = _bounded_text(stderr) | |
| if timed_out: | |
| return ExecutionResult( | |
| ok=False, | |
| output_path=None, | |
| stdout=stdout, | |
| stderr=stderr, | |
| error=f"timeout after {self._limits.timeout_s:g}s", | |
| timed_out=True, | |
| returncode=process.returncode, | |
| ) | |
| if process.returncode != 0: | |
| error = (stdout + "\n" + stderr).strip() or ( | |
| f"generated script exited with status {process.returncode}" | |
| ) | |
| return ExecutionResult( | |
| ok=False, | |
| output_path=None, | |
| stdout=stdout, | |
| stderr=stderr, | |
| error=error, | |
| timed_out=False, | |
| returncode=process.returncode, | |
| ) | |
| if not output_path.is_file(): | |
| return ExecutionResult( | |
| ok=False, | |
| output_path=None, | |
| stdout=stdout, | |
| stderr=stderr, | |
| error="output file was not created", | |
| timed_out=False, | |
| returncode=process.returncode, | |
| ) | |
| return ExecutionResult( | |
| ok=True, | |
| output_path=output_path, | |
| stdout=stdout, | |
| stderr=stderr, | |
| error="", | |
| timed_out=False, | |
| returncode=process.returncode, | |
| ) | |
| def remove_case(self, case_name: str) -> None: | |
| if _SAFE_CASE_NAME.fullmatch(case_name) and ".." not in case_name: | |
| shutil.rmtree(self._root / case_name, ignore_errors=True) | |
| def close(self) -> None: | |
| if self._closed: | |
| return | |
| shutil.rmtree(self._root, ignore_errors=True) | |
| if self._leased_uid is not None: | |
| _release_uid(self._leased_uid) | |
| self._closed = True | |