Spaces:
Running
Running
| """Gradio app runner β launches Gradio apps as subprocess servers. | |
| Manages the lifecycle of Gradio app processes: start, status check, and stop. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| import os | |
| import re | |
| import subprocess | |
| import sys | |
| import tempfile | |
| from pathlib import Path | |
| from typing import Any | |
| logger = logging.getLogger(__name__) | |
| # βββ Registry for running Gradio subprocesses βββββββββββββββββββββββββββ | |
| _running_gradio_procs: dict[str, subprocess.Popen] = {} | |
| def run_gradio_app(code: str, port: int = 7861) -> dict[str, Any]: | |
| """Launch a Gradio app as a subprocess and return its URL. | |
| The Gradio app is run on the specified port. We modify the code | |
| to ensure it launches on the correct port and is accessible. | |
| """ | |
| # Kill any previously running Gradio app | |
| _stop_all_procs() | |
| # Patch the code: ensure launch uses correct server_name and server_port | |
| patched_code = code | |
| # Replace .launch() with correct params | |
| patched_code = re.sub( | |
| r"(\w+)\.launch\([^)]*\)", | |
| f'\\1.launch(server_name="0.0.0.0", server_port={port}, share=False)', | |
| patched_code, | |
| ) | |
| # If no .launch() found, add one | |
| if ".launch(" not in patched_code: | |
| patched_code += ( | |
| f'\n\nif __name__ == "__main__":\n' | |
| f' iface.launch(server_name="0.0.0.0", server_port={port}, share=False)\n' | |
| ) | |
| with tempfile.TemporaryDirectory(prefix="gradio_app_") as tmp: | |
| app_path = Path(tmp) / "gradio_app.py" | |
| app_path.write_text(patched_code, encoding="utf-8") | |
| env = { | |
| **os.environ, | |
| "PYTHONUNBUFFERED": "1", | |
| "GRADIO_SERVER_NAME": "0.0.0.0", | |
| "GRADIO_SERVER_PORT": str(port), | |
| } | |
| try: | |
| proc = subprocess.Popen( | |
| [sys.executable, str(app_path)], | |
| cwd=tmp, | |
| env=env, | |
| stdout=subprocess.PIPE, | |
| stderr=subprocess.PIPE, | |
| text=True, | |
| ) | |
| proc_id = f"gradio_{port}" | |
| _running_gradio_procs[proc_id] = proc | |
| # Wait a bit for the server to start | |
| import time as _time | |
| _time.sleep(3) | |
| # Check if process is still running | |
| poll = proc.poll() | |
| if poll is not None: | |
| stdout = proc.stdout.read() if proc.stdout else "" | |
| stderr = proc.stderr.read() if proc.stderr else "" | |
| return { | |
| "success": False, | |
| "url": "", | |
| "message": f"Gradio app exited with code {poll}", | |
| "stdout": stdout[-2000:] if stdout else "", | |
| "stderr": stderr[-2000:] if stderr else "", | |
| } | |
| gradio_url = f"http://localhost:{port}" | |
| return { | |
| "success": True, | |
| "url": gradio_url, | |
| "message": f"Gradio app running at {gradio_url}", | |
| "port": port, | |
| } | |
| except Exception as exc: | |
| logger.exception("Failed to launch Gradio app") | |
| return { | |
| "success": False, | |
| "url": "", | |
| "message": f"Failed to launch: {exc}", | |
| } | |
| def stop_gradio_app() -> dict[str, Any]: | |
| """Stop any running Gradio app subprocess.""" | |
| stopped = _stop_all_procs() | |
| return {"success": True, "message": f"Stopped {stopped} Gradio app(s)"} | |
| def _stop_all_procs() -> int: | |
| """Stop all running Gradio processes. Returns count of stopped procs.""" | |
| stopped = 0 | |
| for pid, proc in list(_running_gradio_procs.items()): | |
| try: | |
| proc.terminate() | |
| proc.wait(timeout=3) | |
| stopped += 1 | |
| except Exception: | |
| try: | |
| proc.kill() | |
| stopped += 1 | |
| except Exception: | |
| pass | |
| _running_gradio_procs.clear() | |
| return stopped | |