LastNoob's picture
Replace global runtime resources with explicit ownership (#1042)
4294178 unverified
Raw
History Blame Contribute Delete
2.36 kB
"""ASGI lifespan adapter for the application runtime owner."""
from typing import Any
from loguru import logger
from starlette.types import ASGIApp, Receive, Scope, Send
from .application import ApplicationRuntime, startup_failure_message
class RuntimeASGIApp:
"""Delegate HTTP to FastAPI and lifespan to `ApplicationRuntime`."""
def __init__(self, app: ASGIApp, runtime: ApplicationRuntime) -> None:
self.app = app
self.runtime = runtime
def __getattr__(self, name: str) -> Any:
return getattr(self.app, name)
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] != "lifespan":
await self.app(scope, receive, send)
return
await self._lifespan(receive, send)
async def _lifespan(self, receive: Receive, send: Send) -> None:
started = False
while True:
message = await receive()
if message["type"] == "lifespan.startup":
try:
await self.runtime.start()
except Exception as exc:
await send(
{
"type": "lifespan.startup.failed",
"message": startup_failure_message(
self.runtime.settings,
exc,
),
}
)
return
started = True
await send({"type": "lifespan.startup.complete"})
continue
if message["type"] == "lifespan.shutdown":
if started:
try:
closed = await self.runtime.close()
except Exception as exc:
logger.error(
"Shutdown failed: exc_type={}",
type(exc).__name__,
)
await send({"type": "lifespan.shutdown.failed", "message": ""})
return
if not closed:
await send({"type": "lifespan.shutdown.failed", "message": ""})
return
await send({"type": "lifespan.shutdown.complete"})
return