algospaced-dsa / sandbox.py
iamfebin's picture
Configure AlgoSpaced for Hugging Face Spaces deployment
aaa634c
Raw
History Blame Contribute Delete
6.29 kB
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"
}