codespace / main.py
peterpeter8585's picture
Create main.py
c9e8218 verified
raw
history blame
2.81 kB
import asyncio
import os
import sys
import tempfile
from typing import List
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI(title="Unlimited Runner API", version="0.1.0")
# μ™„ν™”λœ μ œν•œ
MAX_STDOUT_BYTES = 2_000_000 # 2MB
EXEC_TIMEOUT = 120 # 120s
PIP_TIMEOUT = 600 # 10m
class RunPythonBody(BaseModel):
code: str
class PipBody(BaseModel):
packages: List[str]
@app.get("/health")
async def health():
return {"ok": True}
@app.post("/run-python")
async def run_python(body: RunPythonBody):
# μ½”λ“œ 파일둜 μ €μž₯ ν›„ μ‹€ν–‰
with tempfile.TemporaryDirectory() as td:
code_path = os.path.join(td, "snippet.py")
with open(code_path, "w", encoding="utf-8") as f:
f.write(body.code)
try:
proc = await asyncio.create_subprocess_exec(
sys.executable, code_path,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
try:
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=EXEC_TIMEOUT)
except asyncio.TimeoutError:
proc.kill()
raise HTTPException(status_code=408, detail="Execution timed out")
return {
"returncode": proc.returncode,
"stdout": stdout[:MAX_STDOUT_BYTES].decode("utf-8", errors="replace"),
"stderr": stderr[:MAX_STDOUT_BYTES].decode("utf-8", errors="replace"),
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/pip")
async def pip_install(body: PipBody):
pkgs = [p.strip() for p in body.packages if p.strip()]
if not pkgs:
raise HTTPException(status_code=400, detail="No packages provided")
try:
proc = await asyncio.create_subprocess_exec(
sys.executable, "-m", "pip", "install", "--no-input", *pkgs,
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
)
try:
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=PIP_TIMEOUT)
except asyncio.TimeoutError:
proc.kill()
raise HTTPException(status_code=408, detail="pip install timed out")
return {
"returncode": proc.returncode,
"stdout": stdout[:MAX_STDOUT_BYTES].decode("utf-8", errors="replace"),
"stderr": stderr[:MAX_STDOUT_BYTES].decode("utf-8", errors="replace"),
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
import uvicorn
port = int(os.environ.get("PORT", "7860"))
uvicorn.run("app.main:app", host="0.0.0.0", port=port, workers=1)