Coverage for src / graphable / cli / commands / serve.py: 100%
47 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-16 21:32 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-16 21:32 +0000
1from asyncio import get_event_loop
2from html import escape
3from logging import getLogger
4from pathlib import Path
6from starlette.applications import Starlette
7from starlette.responses import HTMLResponse
8from starlette.routing import Route, WebSocketRoute
9from starlette.websockets import WebSocket
10from uvicorn import Config
11from uvicorn import Server as UvicornServer
12from watchfiles import awatch
14from ...views.html import create_topology_html
15from .core import load_graph
17logger = getLogger(__name__)
20class Server:
21 def __init__(self, path: Path, tag: str | None = None):
22 self.path = path
23 self.tag = tag
24 self.connections: set[WebSocket] = set()
25 self.app = Starlette(
26 routes=[
27 Route("/", self.index),
28 WebSocketRoute("/ws", self.websocket_endpoint),
29 ]
30 )
32 async def index(self, request):
33 try:
34 g = load_graph(self.path, tag=self.tag)
35 html = create_topology_html(g)
36 return HTMLResponse(html)
37 except Exception as e:
38 return HTMLResponse(
39 f"<h1>Error loading graph</h1><pre>{escape(str(e))}</pre>",
40 status_code=500,
41 )
43 async def websocket_endpoint(self, websocket: WebSocket):
44 await websocket.accept()
45 self.connections.add(websocket)
46 try:
47 while True:
48 await websocket.receive_text()
49 except Exception:
50 self.connections.remove(websocket)
52 async def watch_file(self):
53 async for changes in awatch(self.path):
54 logger.info(f"File {self.path} changed, reloading...")
55 for ws in self.connections:
56 await ws.send_text("reload")
59def serve_command(path: Path, port: int = 8000, tag: str | None = None):
60 server = Server(path, tag=tag)
62 config = Config(server.app, host="127.0.0.1", port=port, log_level="info")
63 uv_server = UvicornServer(config)
65 loop = get_event_loop()
66 loop.create_task(server.watch_file())
67 loop.run_until_complete(uv_server.serve())