compiler

Log | Files | Refs | README

commit b68322302eaf4c2c4ed4f3bee7d8ef6cd36e8b43
parent b742a49f714646e0d53cd45cc7e9b32029bd582c
Author: AMIT DUTTA <amitdutta4255@gmail.com>
Date:   Sun, 11 Jan 2026 19:12:24 +0530

Migrate to AIOHTTP for cloud compiler server

Refactor server to use AIOHTTP for handling requests and WebSocket connections. Added support for running Python, C, and C++ code with package installation and error handling.
Diffstat:
Mbackend.py | 249++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
1 file changed, 176 insertions(+), 73 deletions(-)

diff --git a/backend.py b/backend.py @@ -1,122 +1,225 @@ import asyncio -import websockets +from aiohttp import web import json import subprocess import os import sys -import platform import threading import re import importlib.util +import platform # <--- FIXED: Added missing import # ================================================================================== -# THREADED COMPILER SERVER (Auto-Install + UTF-8) +# CLOUD COMPILER SERVER (AIOHTTP) - RENDER COMPATIBLE # ================================================================================== -def install_package(package_name): - """Attempt to install a package via pip.""" +def install_package(package_name, ws=None, loop=None): + """Attempt to install a python package via pip.""" try: - # Check if already installed to save time if importlib.util.find_spec(package_name) is not None: return + + msg = f"Installing missing package: {package_name}..." + print(msg) + if ws and loop: + asyncio.run_coroutine_threadsafe( + ws.send_json({'type': 'status', 'msg': msg}), + loop + ) - print(f"Installing missing package: {package_name}...") subprocess.check_call([sys.executable, "-m", "pip", "install", package_name]) except Exception as e: - print(f"Failed to install {package_name}: {e}") + error_msg = f"Failed to install {package_name}: {e}" + print(error_msg) + if ws and loop: + asyncio.run_coroutine_threadsafe( + ws.send_json({'type': 'stdout', 'data': f"\n[Error] {error_msg}\n"}), + loop + ) -def check_and_install_packages(code): - """Scan code for imports and install them if missing.""" - # Regex to find 'import x' or 'from x import y' +def check_and_install_packages(code, ws=None, loop=None): + """Scan Python code for imports and install them if missing.""" imports = re.findall(r'^\s*import\s+(\w+)', code, re.MULTILINE) from_imports = re.findall(r'^\s*from\s+(\w+)', code, re.MULTILINE) - unique_packages = set(imports + from_imports) - - # Filter out standard library modules (approximate list or just let pip handle it) - # Ideally, we just try to install. Pip is smart enough to skip if satisfied. for pkg in unique_packages: - # Skip common standard libs to save time (add more if needed) if pkg in ['os', 'sys', 'time', 'random', 'math', 'json', 'asyncio', 'threading', 'platform', 'subprocess', 're']: continue - install_package(pkg) + install_package(pkg, ws, loop) + +async def handle_client(request): + # --- HEALTH CHECK HANDLING --- + # Render sends HEAD/GET to root. If not a websocket upgrade, return OK. + if request.headers.get("Upgrade", "").lower() != "websocket": + return web.Response(text="OK") -async def run_code(websocket): - print(f"Client connected: {websocket.remote_address}") + # --- WEBSOCKET HANDLING --- + ws = web.WebSocketResponse() + await ws.prepare(request) + + print(f"Client connected: {request.remote}") process = None + # Helper to read output stream in a separate thread def read_stream(stream, loop): try: while True: + # Read 1 byte/char at a time char = stream.read(1) if not char: break + # aiohttp's send_json is a coroutine asyncio.run_coroutine_threadsafe( - websocket.send(json.dumps({'type': 'stdout', 'data': char})), + ws.send_json({'type': 'stdout', 'data': char}), loop ) + + # --- SIGNAL FINISH --- + # When stream ends (process exits), tell frontend + asyncio.run_coroutine_threadsafe( + ws.send_json({'type': 'status', 'msg': 'Program finished'}), + loop + ) + except Exception: pass try: - async for message in websocket: - data = json.loads(message) - - if data.get('type') == 'run': - code = data.get('code') - - # 1. AUTO-INSTALL PACKAGES - await websocket.send(json.dumps({'type': 'status', 'msg': 'Checking dependencies...'})) - # Run in executor to avoid blocking the event loop - await asyncio.get_running_loop().run_in_executor(None, check_and_install_packages, code) - - # 2. WRITE FILE - filename = "temp_script.py" - with open(filename, "w", encoding="utf-8") as f: - f.write(code) + async for msg in ws: + if msg.type == web.WSMsgType.TEXT: + data = json.loads(msg.data) - # 3. RUN CODE - env = os.environ.copy() - env["PYTHONIOENCODING"] = "utf-8" - - process = subprocess.Popen( - [sys.executable, "-u", filename], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - bufsize=0, - encoding='utf-8', - env=env - ) - - await websocket.send(json.dumps({'type': 'status', 'msg': 'Running...'})) - - loop = asyncio.get_running_loop() - thread = threading.Thread(target=read_stream, args=(process.stdout, loop)) - thread.daemon = True - thread.start() - - elif data.get('type') == 'input': - if process and process.poll() is None: - user_input = data.get('data') - try: - process.stdin.write(user_input) - process.stdin.flush() - except Exception: - pass - - except websockets.exceptions.ConnectionClosed: - print("Client disconnected") - except Exception as e: - print(f"Server Error: {e}") + if data.get('type') == 'run': + code = data.get('code') + language = data.get('language', 'python') + + # --- PYTHON HANDLING --- + if language == 'python': + await ws.send_json({'type': 'status', 'msg': 'Checking dependencies...'}) + loop = asyncio.get_running_loop() + await loop.run_in_executor(None, check_and_install_packages, code, ws, loop) + + filename = "temp_script.py" + with open(filename, "w", encoding="utf-8") as f: + f.write(code) + + env = os.environ.copy() + env["PYTHONIOENCODING"] = "utf-8" + + process = subprocess.Popen( + [sys.executable, "-u", filename], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=0, + encoding='utf-8', + env=env + ) + await ws.send_json({'type': 'status', 'msg': 'Running Python...'}) + + # --- C HANDLING --- + elif language == 'c': + filename = "temp_code.c" + executable = "./a.out" if platform.system() != "Windows" else "a.exe" + + with open(filename, "w", encoding="utf-8") as f: + f.write(code) + + await ws.send_json({'type': 'status', 'msg': 'Compiling C...'}) + + compile_process = subprocess.run( + ["gcc", filename, "-o", executable], + capture_output=True, + text=True + ) + + if compile_process.returncode != 0: + await ws.send_json({'type': 'stdout', 'data': f"Compilation Error:\n{compile_process.stderr}"}) + continue + + await ws.send_json({'type': 'status', 'msg': 'Running C Binary...'}) + + process = subprocess.Popen( + [executable], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=0, + encoding='utf-8' + ) + + # --- C++ HANDLING --- + elif language == 'cpp': + filename = "temp_code.cpp" + executable = "./a.out" if platform.system() != "Windows" else "a.exe" + + with open(filename, "w", encoding="utf-8") as f: + f.write(code) + + await ws.send_json({'type': 'status', 'msg': 'Compiling C++...'}) + + compile_process = subprocess.run( + ["g++", filename, "-o", executable], + capture_output=True, + text=True + ) + + if compile_process.returncode != 0: + await ws.send_json({'type': 'stdout', 'data': f"Compilation Error:\n{compile_process.stderr}"}) + continue + + await ws.send_json({'type': 'status', 'msg': 'Running C++ Binary...'}) + + process = subprocess.Popen( + [executable], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=0, + encoding='utf-8' + ) + + if process: + loop = asyncio.get_running_loop() + thread = threading.Thread(target=read_stream, args=(process.stdout, loop)) + thread.daemon = True + thread.start() + + elif data.get('type') == 'input': + if process and process.poll() is None: + user_input = data.get('data') + try: + process.stdin.write(user_input) + process.stdin.flush() + except Exception: + pass + + elif msg.type == web.WSMsgType.ERROR: + print(f'ws connection closed with exception {ws.exception()}') + finally: if process: process.kill() + print("Client disconnected") + + return ws async def main(): - print("Server started on port 8765...") - async with websockets.serve(run_code, "0.0.0.0", 8765): - await asyncio.Future() + port = int(os.environ.get("PORT", 8765)) + app = web.Application() + # Route root path to the unified handler (handles both HTTP checks and WS upgrades) + app.add_routes([web.get('/', handle_client)]) + + runner = web.AppRunner(app) + await runner.setup() + site = web.TCPSite(runner, '0.0.0.0', port) + print(f"AIOHTTP Server started on port {port}...") + await site.start() + + # Keep the server running + await asyncio.Event().wait() if __name__ == "__main__": asyncio.run(main())
© notamitgamer • Site Built: 2026-07-21 13:58:23 UTC • git-mirror commit: 1037f62 [view raw info]
Originally created with stagit • modified by notamitgamer
Forked from github.com/notamitgamer/git-mirror