compiler

Log | Files | Refs | README

commit b742a49f714646e0d53cd45cc7e9b32029bd582c
parent 686c1675f1e8de4c228f99c67305eb17c6503fd6
Author: AMIT DUTTA <amitdutta4255@gmail.com>
Date:   Sun, 11 Jan 2026 19:07:50 +0530

Refactor backend to use websockets and auto-install packages
Diffstat:
Mbackend.py | 249+++++++++++++++++++++++--------------------------------------------------------
1 file changed, 73 insertions(+), 176 deletions(-)

diff --git a/backend.py b/backend.py @@ -1,225 +1,122 @@ import asyncio -from aiohttp import web +import websockets import json import subprocess import os import sys +import platform import threading import re import importlib.util -import platform # <--- FIXED: Added missing import # ================================================================================== -# CLOUD COMPILER SERVER (AIOHTTP) - RENDER COMPATIBLE +# THREADED COMPILER SERVER (Auto-Install + UTF-8) # ================================================================================== -def install_package(package_name, ws=None, loop=None): - """Attempt to install a python package via pip.""" +def install_package(package_name): + """Attempt to install a 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: - 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 - ) + print(f"Failed to install {package_name}: {e}") -def check_and_install_packages(code, ws=None, loop=None): - """Scan Python code for imports and install them if missing.""" +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' 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, 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") + install_package(pkg) - # --- WEBSOCKET HANDLING --- - ws = web.WebSocketResponse() - await ws.prepare(request) - - print(f"Client connected: {request.remote}") +async def run_code(websocket): + print(f"Client connected: {websocket.remote_address}") 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( - ws.send_json({'type': 'stdout', 'data': char}), + websocket.send(json.dumps({'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 msg in ws: - if msg.type == web.WSMsgType.TEXT: - data = json.loads(msg.data) - - 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 + async for message in websocket: + data = json.loads(message) - elif msg.type == web.WSMsgType.ERROR: - print(f'ws connection closed with exception {ws.exception()}') - + 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) + + # 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}") finally: if process: process.kill() - print("Client disconnected") - - return ws async def main(): - 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() + print("Server started on port 8765...") + async with websockets.serve(run_code, "0.0.0.0", 8765): + await asyncio.Future() 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