import sys import subprocess import json import tempfile import os EXECUTION_TEMPLATE = """ import sys import json import traceback # --- User Code Start --- {user_code} # --- User Code End --- def run_tests(): test_cases = {test_cases_json} results = [] # Locate entry point function func = None if 'solve' in globals() and callable(globals()['solve']): func = globals()['solve'] else: for name, obj in list(globals().items()): if callable(obj) and not name.startswith('_') and name not in ('run_tests', 'sys', 'json', 'traceback', 'copy'): func = obj break if not func: print(json.dumps([ {{"status": "error", "error": "No callable function found in your code. Please define a function (e.g., 'def solve(...)')."}} ])) return for idx, tc in enumerate(test_cases): inputs = tc.get("input", []) expected = tc.get("expected") if not isinstance(inputs, list): inputs = [inputs] try: import copy args = copy.deepcopy(inputs) res = func(*args) # Comparison is_match = (res == expected) results.append({{ "index": idx, "status": "passed" if is_match else "failed", "input": inputs, "expected": expected, "got": res }}) except Exception as e: results.append({{ "index": idx, "status": "error", "input": inputs, "expected": expected, "error": str(e), "traceback": traceback.format_exc().splitlines()[-2:] }}) print(json.dumps(results)) if __name__ == '__main__': run_tests() """ def run_python_sandbox(user_code: str, test_cases: list, timeout: float = 4.0) -> dict: """ Executes user code against test cases in an isolated sandbox. Attempts Docker execution first, then falls back to restricted subprocess. """ test_cases_json = json.dumps(test_cases) full_script = EXECUTION_TEMPLATE.format( user_code=user_code, test_cases_json=test_cases_json ) # Try Docker execution try: # Run python:3.11-slim container with stdin, no network, 128MB memory, 0.5 CPU # On Windows, we pipe the script directly to python via stdin to avoid path mounts cmd = [ "docker", "run", "--rm", "-i", "--network", "none", "--memory", "128m", "--cpus", "0.5", "python:3.11-slim", "python" ] proc = subprocess.Popen( cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) stdout, stderr = proc.communicate(input=full_script, timeout=timeout) if proc.returncode == 137: return { "success": False, "error": "Memory limit exceeded (OOM) or container killed.", "sandbox_type": "docker" } if proc.returncode != 0: return { "success": False, "error": stderr or f"Execution failed with code {proc.returncode}", "sandbox_type": "docker" } try: results = json.loads(stdout.strip()) return { "success": True, "results": results, "sandbox_type": "docker" } except json.JSONDecodeError: return { "success": False, "error": f"Invalid output from execution: {stdout}\nErrors: {stderr}", "sandbox_type": "docker" } except (subprocess.TimeoutExpired, TimeoutError): # Clean up process try: proc.kill() except Exception: pass return { "success": False, "error": f"Execution timed out. Code exceeded max runtime of {timeout}s.", "sandbox_type": "docker" } except Exception as docker_err: # Fall back to native local subprocess execution with limited environment return run_local_fallback(full_script, timeout, str(docker_err)) def run_local_fallback(script_content: str, timeout: float, debug_msg: str) -> dict: """ Fallback execution using current python environment. """ try: proc = subprocess.Popen( [sys.executable], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) stdout, stderr = proc.communicate(input=script_content, timeout=timeout) if proc.returncode != 0: return { "success": False, "error": stderr or f"Local execution failed with code {proc.returncode}", "sandbox_type": "local_fallback", "sandbox_warning": f"Docker isolation unavailable: {debug_msg}" } try: results = json.loads(stdout.strip()) return { "success": True, "results": results, "sandbox_type": "local_fallback", "sandbox_warning": f"Docker isolation unavailable: {debug_msg}" } except json.JSONDecodeError: return { "success": False, "error": f"Invalid output from local runner: {stdout}", "sandbox_type": "local_fallback" } except subprocess.TimeoutExpired: try: proc.kill() except Exception: pass return { "success": False, "error": f"Local execution timed out. Code exceeded {timeout}s.", "sandbox_type": "local_fallback" } except Exception as e: return { "success": False, "error": f"Failed to execute local fallback runner: {str(e)}", "sandbox_type": "none" }