Yield dependency doesn't get http exceptions being raised.
See original GitHub issueDescribe 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
Issue Analytics
- State:
- Created 4 years ago
- Reactions:11
- Comments:17 (10 by maintainers)
Top GitHub Comments
@tiangolo these docs are incredible 👏👏
I think this behavior is explained in the new docs.
It sounds to me like your question is closer to “how can I detect whether an error was raised during the execution of the yield statement, even if it was handled by an exception handler”. Unfortunately, as of now, I think the answer is just that you can’t. So maybe this issue is better classified as a feature request than a bug.
For what it’s worth, I do think this is a reasonable feature request, though I’m not sure 1) what a good API for it would be, or 2) whether there are any common problems that could be caused by “double-handling” the exception like that.
That said, I would be in favor of having exceptions raised in contextmanager dependencies even if they were also handled by request-handling code, as it would make it easier to perform proper cleanup (e.g., rolling back a transaction when using a contextmanager-based sqlalchemy session dependency).