Issue 1:
First check

I added a very descriptive title to this issue.

I used the GitHub search to find a similar issue and didn't find it.

I searched the FastAPI documentation, with the integrated search.

I already searched in Google "How to X in FastAPI" and didn't find any information.

I already read and followed all the tutorial in the docs and didn't find an answer.

I already checked if it is not related to FastAPI but to Pydantic.

I already checked if it is not related to FastAPI but to Swagger UI.

I already checked if it is not related to FastAPI but to ReDoc.

After submitting this, I commit to one of:

Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.

I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.

Implement a Pull Request for a confirmed bug.

Example

Here's a self-contained, minimal, reproducible, example with my use case:

from fastapi import FastAPI, Request, Depends
from fastapi.responses import JSONResponse
from starlette import status

class CustomException(Exception):
pass

async def some_dep():
print("inside dep")
try:
yield 123
except Exception as e:
print("exception occurred") # it disappears with custom handler
finally:
print("finally in dep")

def create_app() -> FastAPI:
app = FastAPI()

@app.on_event("startup")
async def startup():
print("startup action")

@app.on_event("shutdown")
async def shutdown():
print("shutdown action")

# when commenting line below "exception occurred" line appears on post request
install_exception_handlers(app)

@app.post("/qwe")
async def qwe(a: int = Depends(some_dep)):
print(a)
raise CustomException

return app

def install_exception_handlers(app: FastAPI):
@app.exception_handler(CustomException)
async def postgres_error_handler(_: Request, __: CustomException) -> JSONResponse:
return JSONResponse(
dict(response="this is json response"),
status_code=status.HTTP_400_BAD_REQUEST,
)

Description

Run FastAPI application

Call curl -X POST http://127.0.0.1:8000/qwe in bash

Got output:

inside dep
123
INFO: 127.0.0.1:57400 - "POST /qwe HTTP/1.1" 400 Bad Request
finally in dep

Comment line install_exception_handlers(app)

Reload app

Call curl -X POST http://127.0.0.1:8000/qwe in bash

Got output (output has exception occurred line that should also be with custom exception handler):

inside dep
123
INFO: 127.0.0.1:57414 - "POST /qwe HTTP/1.1" 500 Internal Server Error
exception occurred
finally in dep

Environment

OS: Linux artslob-laptop 5.4.0-48-generic #52~18.04.1-Ubuntu SMP Thu Sep 10 12:50:22 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux:

Dependencies:

$pip list
Package Version
------------------ -------
attrs 20.2.0
click 7.1.2
fastapi 0.61.1
h11 0.11.0
importlib-metadata 2.0.0
iniconfig 1.0.1
packaging 20.4
pip 20.2.3
pluggy 0.13.1
py 1.9.0
pydantic 1.6.1
pyparsing 2.4.7
pytest 6.1.1
pytest-asyncio 0.14.0
setuptools 50.3.0
six 1.15.0
starlette 0.13.6
toml 0.10.1
typing-extensions 3.7.4.3
uvicorn 0.12.1
zipp 3.3.0

Python version: 3.7.4

Additional context

Repo with code: https://github.com/artslob/check-custom-handler-fastapi.

So what we see here is that custom exception handler does not provide context about exception to yielding code in FastAPI dependecy. Its pretty ctitical, because if dependency is database transaction then it can result in false negative session commit (when actually commit is not desired).

Issue 2:
Describe the bug

HTTP exceptions are not raised propagated inside a dependency which encapsulates the yield in a try except statement.

Only custom exceptions are raised propagated if I understand correctly.

To Reproduce

Create a main.py with:

from fastapi import Depends, FastAPI, HTTPException

app = FastAPI()

def new_db_session():
try:
yield

except Exception:
print("rollback")

else:
print("commit")

finally:
print("close")

@app.get("/exception")
def get_exception(db=Depends(new_db_session)):
raise Exception()

@app.get("/http_exception")
def get_exception(db=Depends(new_db_session)):
raise HTTPException(status_code=400, detail="invalid request")

run app using uvicorn main:app

curl http://127.0.0.1:8000/exception; app prints:

INFO: 127.0.0.1:50069 - "GET /exception HTTP/1.1" 500 Internal Server Error
rollback
close

curl http://127.0.0.1:8000/http_exception; app prints:

INFO: 127.0.0.1:50072 - "GET /http_exception HTTP/1.1" 400 Bad Request
commit
close

Expected behavior

HTTP Exception should be raised propagated inside the dependency try except statement.

Environment

OS: Darwin Kernel Version 19.0.0: Thu Oct 17 16:17:15 PDT 2019; root:xnu-6153.41.3~29/RELEASE_X86_64 x86_64 i386 MacBookPro13,1 Darwin

FastAPI Version: 0.46.0

Python version: Python 3.7.3

Additional context

PR: Dependencies with yield (used as context managers)

From @dmontagu in #570:

@cjw296 As implemented, it would require some care to ensure teardown was done in a guaranteed order. I think it would currently amount to constructing a consistent "teardown" dependency. But I think in most cases, you would be able to just build a single dependency that sets everything up in order, and tears it down in order, and then to build other dependencies on top of that. For example, handling your use case:

def db_contextmanager():
conn = connect_to_db()
t = conn.transaction()
try:
yield conn, t
t.commit()
except:
t.rollback()
conn.close()

def get_connection(conn_t = Depends(db_contextmanager)):
conn, t = conn_t
return conn

def get_transaction(conn_t = Depends(db_contextmanager)):
conn, t = conn_t
return t

def endpoint(conn = Depends(get_connection)):
# Do something with the connection
pass