Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Update internal AsyncExitStack to fix context for dependencies with yield #4575

Merged
merged 12 commits into from Feb 17, 2022
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 34 additions & 7 deletions fastapi/applications.py
Expand Up @@ -11,6 +11,7 @@
)
from fastapi.exceptions import RequestValidationError
from fastapi.logger import logger
from fastapi.middleware.asyncexitstack import AsyncExitStackMiddleware
from fastapi.openapi.docs import (
get_redoc_html,
get_swagger_ui_html,
Expand All @@ -21,8 +22,9 @@
from fastapi.types import DecoratedCallable
from starlette.applications import Starlette
from starlette.datastructures import State
from starlette.exceptions import HTTPException
from starlette.exceptions import ExceptionMiddleware, HTTPException
from starlette.middleware import Middleware
from starlette.middleware.errors import ServerErrorMiddleware
from starlette.requests import Request
from starlette.responses import HTMLResponse, JSONResponse, Response
from starlette.routing import BaseRoute
Expand Down Expand Up @@ -134,6 +136,36 @@ def __init__(
self.openapi_schema: Optional[Dict[str, Any]] = None
self.setup()

def build_middleware_stack(self) -> ASGIApp:
# Duplicate/override from Starlette to add AsyncExitStackMiddleware after
# inside of ExceptionMiddleware
debug = self.debug
error_handler = None
exception_handlers = {}

for key, value in self.exception_handlers.items():
if key in (500, Exception):
error_handler = value
else:
exception_handlers[key] = value

middleware = (
[Middleware(ServerErrorMiddleware, handler=error_handler, debug=debug)]
+ self.user_middleware
+ [
Middleware(
ExceptionMiddleware, handlers=exception_handlers, debug=debug
),
# Add FastAPI-specific AsyncExitStackMiddleware for dependencies
Middleware(AsyncExitStackMiddleware),
]
)

app = self.router
for cls, options in reversed(middleware):
tiangolo marked this conversation as resolved.
Show resolved Hide resolved
app = cls(app=app, **options)
return app

def openapi(self) -> Dict[str, Any]:
if not self.openapi_schema:
self.openapi_schema = get_openapi(
Expand Down Expand Up @@ -206,12 +238,7 @@ async def redoc_html(req: Request) -> HTMLResponse:
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if self.root_path:
scope["root_path"] = self.root_path
if AsyncExitStack:
async with AsyncExitStack() as stack:
scope["fastapi_astack"] = stack
await super().__call__(scope, receive, send)
else:
await super().__call__(scope, receive, send) # pragma: no cover
await super().__call__(scope, receive, send)

def add_api_route(
self,
Expand Down