ASGI Lifespan
asgi-lifespan is a Python library that enables programmatic control over the startup and shutdown lifecycle events of ASGI applications. It's primarily used for testing or mocking ASGI apps without requiring a full ASGI server, facilitating resource initialization and cleanup during development and CI. The current version is 2.1.0, with releases typically driven by new features, bug fixes, or Python version support.
Warnings
- breaking Version 2.0.0 of `asgi-lifespan` dropped support for Python 3.6. Users on Python 3.6 must remain on `asgi-lifespan<2.0.0` or upgrade their Python interpreter.
- breaking The `Lifespan` class and `LifespanMiddleware` were removed in earlier major versions (specifically 1.0.0) in favor of the `LifespanManager` asynchronous context manager. Code using the old API will break.
- gotcha If you encounter the error `LifespanNotSupported` or `ASGI 'lifespan' protocol appears unsupported`, it typically means the wrapped ASGI application did not correctly implement the ASGI lifespan protocol, or an unhandled exception occurred during its startup or shutdown phase. `asgi-lifespan` detects this if the app calls `send()` before `receive()` for the first time, or raises an exception during startup.
- gotcha When using `LifespanManager` in conjunction with an ASGI test client (e.g., `httpx.AsyncClient`), it's crucial to pass `manager.app` (not just `app`) to the client's `ASGITransport`. This ensures that any state created by the lifespan events is correctly propagated and available to subsequent request/response handling within the client's scope.
Install
-
pip install asgi-lifespan
Imports
- LifespanManager
from asgi_lifespan import LifespanManager
Quickstart
import asyncio
from contextlib import asynccontextmanager
from asgi_lifespan import LifespanManager
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
# An example ASGI application with a lifespan context manager
@asynccontextmanager
async def lifespan(app: Starlette):
print("ASGI app startup: Initializing resources...")
yield
print("ASGI app shutdown: Cleaning up resources...")
app = Starlette(lifespan=lifespan)
@app.route("/")
async def homepage(request):
return PlainTextResponse("Hello, world!")
async def run_lifespan_example():
print("Starting LifespanManager...")
async with LifespanManager(app) as manager:
print("LifespanManager active, app is ready.")
# In a real test, you would now use an ASGI client like httpx.AsyncClient(app=manager.app)
# to send requests and interact with the 'started' application.
# For this example, we'll just show the lifespan events.
print("LifespanManager exited, app is shut down.")
if __name__ == "__main__":
asyncio.run(run_lifespan_example())