From 53ebb9b46aab11ea4194d285ee70a4a524faa00a Mon Sep 17 00:00:00 2001 From: essentiaMarco <131397104+essentiaMarco@users.noreply.github.com> Date: Sun, 15 Mar 2026 16:59:20 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20resolve=20merge=20with=20master=20?= =?UTF-8?q?=E2=80=94=20lifespan=20app=20vs=20router?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Default lifespan: use app.router for _DefaultLifespan (router has _startup/_shutdown; FastAPI app does not). - Wrapper: when app is FastAPI, set fastapi_app from app and pass app.router to _run_lifespan_dependencies so lifespan deps are collected. - Test: use only Request.receive() (Starlette Request has no .send). Made-with: Cursor --- fastapi/applications.py | 11 +++++++++-- tests/test_dependency_lifespan_scope.py | 3 +-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/fastapi/applications.py b/fastapi/applications.py index 8c3197219d..c7328087f4 100644 --- a/fastapi/applications.py +++ b/fastapi/applications.py @@ -49,6 +49,11 @@ def _wrap_lifespan_with_dependency_cache(original: Any) -> Any: @asynccontextmanager async def cm() -> Any: fastapi_app = getattr(app, "_fastapi_app", None) + if fastapi_app is None and hasattr(app, "router"): + router = getattr(app, "router", None) + if router is not None and getattr(router, "_fastapi_app", None) is app: + fastapi_app = app + router_for_deps = getattr(app, "router", app) stack: AsyncExitStack | None = None orig_cm = original(app) try: @@ -56,7 +61,9 @@ def _wrap_lifespan_with_dependency_cache(original: Any) -> Any: stack = AsyncExitStack() await stack.__aenter__() cache: dict[Any, Any] = {} - await routing._run_lifespan_dependencies(app, cache, stack) + await routing._run_lifespan_dependencies( + router_for_deps, cache, stack + ) setattr( fastapi_app.state, FASTAPI_LIFESPAN_DEPENDENCY_CACHE, @@ -1020,7 +1027,7 @@ class FastAPI(Starlette): _inner_lifespan = ( lifespan if lifespan is not None - else (lambda app: routing._DefaultLifespan(app)) + else (lambda app: routing._DefaultLifespan(app.router)) ) _lifespan = _wrap_lifespan_with_dependency_cache(_inner_lifespan) self.router: routing.APIRouter = routing.APIRouter( diff --git a/tests/test_dependency_lifespan_scope.py b/tests/test_dependency_lifespan_scope.py index dac9fbbc0a..1d3c06e426 100644 --- a/tests/test_dependency_lifespan_scope.py +++ b/tests/test_dependency_lifespan_scope.py @@ -122,10 +122,9 @@ def test_collect_lifespan_dependants_route_level_scope() -> None: def test_lifespan_dependency_synthetic_request_receive_send() -> None: - """Lifespan dep that uses Request.receive/send covers noop_receive and noop_send during startup.""" + """Lifespan dep that uses Request.receive covers noop_receive during startup.""" async def lifespan_dep(request: Request) -> str: await request.receive() - await request.send({"type": "http.response.body"}) return "ok" app = FastAPI()