|
|
from fastapi import FastAPI, Request, Form |
|
|
from fastapi.responses import HTMLResponse |
|
|
from fastapi.staticfiles import StaticFiles |
|
|
from fastapi.templating import Jinja2Templates |
|
|
import sys |
|
|
import io |
|
|
import json |
|
|
|
|
|
app = FastAPI() |
|
|
|
|
|
|
|
|
app.mount("/static", StaticFiles(directory="static"), name="static") |
|
|
templates = Jinja2Templates(directory="templates") |
|
|
|
|
|
|
|
|
def run_code_with_trace(code: str): |
|
|
trace_data = [] |
|
|
stdout_capture = io.StringIO() |
|
|
|
|
|
|
|
|
def safe_serialize(obj): |
|
|
try: |
|
|
json.dumps(obj) |
|
|
return obj |
|
|
except (TypeError, OverflowError): |
|
|
return str(obj) |
|
|
|
|
|
|
|
|
def tracer(frame, event, arg): |
|
|
if event == "line": |
|
|
locals_serialized = {k: safe_serialize(v) for k, v in frame.f_locals.items()} |
|
|
trace_data.append({ |
|
|
"line": frame.f_lineno, |
|
|
"locals": locals_serialized |
|
|
}) |
|
|
return tracer |
|
|
|
|
|
|
|
|
sys.settrace(tracer) |
|
|
sys.stdout = stdout_capture |
|
|
try: |
|
|
safe_globals = {"__builtins__": {"print": print, "range": range, "len": len}} |
|
|
exec(code, safe_globals) |
|
|
except Exception as e: |
|
|
trace_data.append({"error": str(e)}) |
|
|
finally: |
|
|
sys.settrace(None) |
|
|
sys.stdout = sys.__stdout__ |
|
|
|
|
|
output = stdout_capture.getvalue() |
|
|
stdout_capture.close() |
|
|
return trace_data, output |
|
|
|
|
|
|
|
|
@app.get("/", response_class=HTMLResponse) |
|
|
async def index(request: Request): |
|
|
return templates.TemplateResponse("index.html", {"request": request}) |
|
|
|
|
|
|
|
|
@app.post("/run", response_class=HTMLResponse) |
|
|
async def run_code(request: Request, code: str = Form(...)): |
|
|
trace, output = run_code_with_trace(code) |
|
|
return templates.TemplateResponse( |
|
|
"index.html", |
|
|
{ |
|
|
"request": request, |
|
|
"trace_json": json.dumps(trace), |
|
|
"output": output, |
|
|
"code": code |
|
|
} |
|
|
) |
|
|
|