diff --git a/uvicorn/config.py b/uvicorn/config.py index 65dfe651e..248292d33 100644 --- a/uvicorn/config.py +++ b/uvicorn/config.py @@ -223,6 +223,7 @@ def __init__( headers: list[tuple[str, str]] | None = None, factory: bool = False, h11_max_incomplete_event_size: int | None = None, + shutdown_delay: float = 0, ): self.app = app self.host = host @@ -268,6 +269,7 @@ def __init__( self.encoded_headers: list[tuple[bytes, bytes]] = [] self.factory = factory self.h11_max_incomplete_event_size = h11_max_incomplete_event_size + self.shutdown_delay = shutdown_delay self.loaded = False self.configure_logging() diff --git a/uvicorn/main.py b/uvicorn/main.py index 96a10d538..5ab7d02b7 100644 --- a/uvicorn/main.py +++ b/uvicorn/main.py @@ -408,6 +408,7 @@ def main( app_dir: str, h11_max_incomplete_event_size: int | None, factory: bool, + shutdown_delay: float = 0, ) -> None: run( app, @@ -457,6 +458,7 @@ def main( factory=factory, app_dir=app_dir, h11_max_incomplete_event_size=h11_max_incomplete_event_size, + shutdown_delay=shutdown_delay, ) @@ -509,6 +511,7 @@ def run( app_dir: str | None = None, factory: bool = False, h11_max_incomplete_event_size: int | None = None, + shutdown_delay: float = 0, ) -> None: if app_dir is not None: sys.path.insert(0, app_dir) @@ -560,6 +563,7 @@ def run( use_colors=use_colors, factory=factory, h11_max_incomplete_event_size=h11_max_incomplete_event_size, + shutdown_delay=shutdown_delay, ) server = Server(config=config) diff --git a/uvicorn/server.py b/uvicorn/server.py index f14026f16..018471981 100644 --- a/uvicorn/server.py +++ b/uvicorn/server.py @@ -48,6 +48,10 @@ def __init__(self) -> None: self.default_headers: list[tuple[bytes, bytes]] = [] +class ShutdownTrigger: + is_shutdown_triggered: bool = False + + class Server: def __init__(self, config: Config) -> None: self.config = config @@ -259,8 +263,12 @@ async def on_tick(self, counter: int) -> bool: return False async def shutdown(self, sockets: list[socket.socket] | None = None) -> None: - logger.info("Shutting down") + if self.config.shutdown_delay: + logger.info(f"Shutting down in {self.config.shutdown_delay} seconds") + ShutdownTrigger.is_shutdown_triggered = True + await asyncio.sleep(self.config.shutdown_delay) + logger.info("Shutting down") # Stop accepting new connections. for server in self.servers: server.close()