compile-hub/compilers/base.py

116 lines
No EOL
4 KiB
Python

import asyncio, tempfile, os, shutil
import subprocess, uuid
from typing import Dict, Any, Optional
class CompilerBase:
def __init__(self, language: str, timeout: int = 5):
self.language = language
self.timeout = timeout
self.max_output_size = 1000000 # 1MB
async def compile_and_run(self, code: str, input_data: str = "") -> Dict[str, Any]:
temp_dir = None
try:
temp_dir = tempfile.mkdtemp(prefix="compile_")
result = await self._execute(code, input_data, temp_dir)
return result
except Exception as e:
return {
"success": False,
"error": f"Compilation error: {str(e)}"
}
finally:
if temp_dir and os.path.exists(temp_dir):
try:
shutil.rmtree(temp_dir, ignore_errors=True)
except:
pass
async def _execute(self, code: str, input_data: str, temp_dir: str) -> Dict[str, Any]:
raise NotImplementedError("Subclass must implement _execute method")
async def _run_process(self, cmd: list, input_data: str = "",
cwd: Optional[str] = None) -> Dict[str, Any]:
try:
process = await asyncio.create_subprocess_exec(
*cmd,
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
cwd=cwd
)
stdout, stderr = await asyncio.wait_for(
process.communicate(input=input_data.encode() if input_data else None),
timeout=self.timeout
)
stdout_str = stdout.decode('utf-8', errors='replace')[:self.max_output_size]
stderr_str = stderr.decode('utf-8', errors='replace')[:self.max_output_size]
if process.returncode != 0:
return {
"success": False,
"error": stderr_str or "Execution failed",
"output": stdout_str
}
return {
"success": True,
"output": stdout_str,
"error": stderr_str if stderr_str else None
}
except asyncio.TimeoutError:
if 'process' in locals():
process.kill()
return {
"success": False,
"error": f"Execution timeout ({self.timeout}s exceeded)"
}
except Exception as e:
return {
"success": False,
"error": f"Execution error: {str(e)}"
}
def _sanitize_code(self, code: str) -> str:
# Базовая очистка кода
dangerous_patterns = [
"system(", "exec(", "eval(", "__import__",
"subprocess", "os.system", "popen"
]
for pattern in dangerous_patterns:
if pattern in code:
return ""
return code
def _check_input_requirements(self, code: str) -> Dict[str, Any]:
# Проверка требований к вводу
input_keywords = {
"cpp": ["cin", "scanf", "getline"],
"python": ["input(", "raw_input"],
"javascript": ["readline", "prompt"]
}
keywords = input_keywords.get(self.language, [])
for keyword in keywords:
if keyword in code:
return {
"requiresInput": True,
"inputDescription": {
"variables": [
{
"name": "input",
"type": "string",
"description": "Program input data"
}
]
}
}
return {"requiresInput": False}