plba
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from threading import Thread
|
||||
|
||||
from fastapi import FastAPI
|
||||
from uvicorn import Config, Server
|
||||
|
||||
|
||||
class UvicornThreadRunner:
|
||||
def __init__(self, host: str, port: int, timeout: int) -> None:
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._timeout = timeout
|
||||
self._server: Server | None = None
|
||||
self._thread: Thread | None = None
|
||||
self._error: BaseException | None = None
|
||||
|
||||
async def start(self, app: FastAPI) -> None:
|
||||
if self._thread is not None and self._thread.is_alive():
|
||||
return
|
||||
self._error = None
|
||||
config = Config(app=app, host=self._host, port=self._port, log_level="warning")
|
||||
self._server = Server(config)
|
||||
self._thread = Thread(target=self._serve, name="plba-http-control", daemon=True)
|
||||
self._thread.start()
|
||||
await self._wait_until_started()
|
||||
|
||||
async def stop(self) -> None:
|
||||
if self._server is None or self._thread is None:
|
||||
return
|
||||
self._server.should_exit = True
|
||||
await asyncio.to_thread(self._thread.join, self._timeout)
|
||||
self._server = None
|
||||
self._thread = None
|
||||
|
||||
@property
|
||||
def port(self) -> int:
|
||||
if self._server is None or not getattr(self._server, "servers", None):
|
||||
return self._port
|
||||
socket = self._server.servers[0].sockets[0]
|
||||
return int(socket.getsockname()[1])
|
||||
|
||||
async def _wait_until_started(self) -> None:
|
||||
if self._server is None:
|
||||
raise RuntimeError("Server is not initialized")
|
||||
deadline = asyncio.get_running_loop().time() + max(float(self._timeout), 1.0)
|
||||
while not self._server.started:
|
||||
if self._error is not None:
|
||||
raise RuntimeError("HTTP control server failed to start") from self._error
|
||||
if asyncio.get_running_loop().time() >= deadline:
|
||||
raise TimeoutError("HTTP control server startup timed out")
|
||||
await asyncio.sleep(0.05)
|
||||
|
||||
def _serve(self) -> None:
|
||||
if self._server is None:
|
||||
return
|
||||
try:
|
||||
asyncio.run(self._server.serve())
|
||||
except BaseException as exc: # noqa: BLE001
|
||||
self._error = exc
|
||||
Reference in New Issue
Block a user