Spaces:
Running
Running
File size: 3,433 Bytes
91990f9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | import httpx
import logging
from config.settings import get_settings
logger = logging.getLogger(__name__)
settings = get_settings()
JDOODLE_LANGUAGE_MAP = {
"python": {"language": "python3", "versionIndex": "4"},
"javascript": {"language": "nodejs", "versionIndex": "4"},
"typescript": {"language": "typescript", "versionIndex": "1"},
"java": {"language": "java", "versionIndex": "4"},
"c++": {"language": "cpp17", "versionIndex": "1"},
"c": {"language": "c", "versionIndex": "5"},
"go": {"language": "go", "versionIndex": "4"},
"rust": {"language": "rust", "versionIndex": "4"},
"ruby": {"language": "ruby", "versionIndex": "4"},
"php": {"language": "php", "versionIndex": "4"},
}
async def run_code_jdoodle(language: str, code: str) -> str:
"""Run code via JDoodle API. Returns output string."""
lang_config = JDOODLE_LANGUAGE_MAP.get(language.lower())
if not lang_config:
raise ValueError(f"Language '{language}' not supported")
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(
"https://api.jdoodle.com/v1/execute",
json={
"clientId": settings.jdoodle_client_id,
"clientSecret": settings.jdoodle_client_secret,
"script": code,
"language": lang_config["language"],
"versionIndex": lang_config["versionIndex"],
}
)
if response.status_code != 200:
logger.error(f"JDoodle error {response.status_code}: {response.text}")
raise Exception(f"Code execution service error: {response.status_code}")
result = response.json()
# JDoodle returns error info in statusCode field
if result.get("statusCode") == 400:
raise Exception("JDoodle: Invalid request")
if result.get("statusCode") == 429:
raise Exception("Daily code execution limit reached. Try again tomorrow.")
return result.get("output", "No output")
async def run_code_piston(language: str, version: str, code: str) -> str:
"""Run code via Piston API (fallback). Returns output string."""
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(
f"{settings.piston_api_url}/execute",
json={
"language": language,
"version": version,
"files": [{"content": code}],
}
)
if response.status_code != 200:
raise Exception(f"Piston error: {response.status_code}")
result = response.json()
run = result.get("run", {})
return run.get("output") or run.get("stderr") or "No output"
async def run_code(language: str, code: str, version: str = "") -> str:
"""Run code with JDoodle primary, Piston fallback."""
try:
return await run_code_jdoodle(language, code)
except Exception as e:
logger.warning(f"JDoodle failed ({e}), falling back to Piston")
try:
return await run_code_piston(language, version, code)
except Exception as e2:
raise Exception(f"Both code runners failed. JDoodle: {e} | Piston: {e2}")
|