Browse Source

🐛 Preserve traceback when exception is raised in sync dependency with `yield` (#5823)

Co-authored-by: Marcelo Trylesinski <[email protected]>
pull/13031/head
Abdullah Hashim 4 months ago
committed by GitHub
parent
commit
4f8157588e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      fastapi/concurrency.py
  2. 23
      tests/test_exception_handlers.py

4
fastapi/concurrency.py

@ -1,7 +1,7 @@
from contextlib import asynccontextmanager as asynccontextmanager from contextlib import asynccontextmanager as asynccontextmanager
from typing import AsyncGenerator, ContextManager, TypeVar from typing import AsyncGenerator, ContextManager, TypeVar
import anyio import anyio.to_thread
from anyio import CapacityLimiter from anyio import CapacityLimiter
from starlette.concurrency import iterate_in_threadpool as iterate_in_threadpool # noqa from starlette.concurrency import iterate_in_threadpool as iterate_in_threadpool # noqa
from starlette.concurrency import run_in_threadpool as run_in_threadpool # noqa from starlette.concurrency import run_in_threadpool as run_in_threadpool # noqa
@ -28,7 +28,7 @@ async def contextmanager_in_threadpool(
except Exception as e: except Exception as e:
ok = bool( ok = bool(
await anyio.to_thread.run_sync( await anyio.to_thread.run_sync(
cm.__exit__, type(e), e, None, limiter=exit_limiter cm.__exit__, type(e), e, e.__traceback__, limiter=exit_limiter
) )
) )
if not ok: if not ok:

23
tests/test_exception_handlers.py

@ -1,5 +1,5 @@
import pytest import pytest
from fastapi import FastAPI, HTTPException from fastapi import Depends, FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError from fastapi.exceptions import RequestValidationError
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from starlette.responses import JSONResponse from starlette.responses import JSONResponse
@ -28,6 +28,18 @@ app = FastAPI(
client = TestClient(app) client = TestClient(app)
def raise_value_error():
raise ValueError()
def dependency_with_yield():
yield raise_value_error()
@app.get("/dependency-with-yield", dependencies=[Depends(dependency_with_yield)])
def with_yield(): ...
@app.get("/http-exception") @app.get("/http-exception")
def route_with_http_exception(): def route_with_http_exception():
raise HTTPException(status_code=400) raise HTTPException(status_code=400)
@ -65,3 +77,12 @@ def test_override_server_error_exception_response():
response = client.get("/server-error") response = client.get("/server-error")
assert response.status_code == 500 assert response.status_code == 500
assert response.json() == {"exception": "server-error"} assert response.json() == {"exception": "server-error"}
def test_traceback_for_dependency_with_yield():
client = TestClient(app, raise_server_exceptions=True)
with pytest.raises(ValueError) as exc_info:
client.get("/dependency-with-yield")
last_frame = exc_info.traceback[-1]
assert str(last_frame.path) == __file__
assert last_frame.lineno == raise_value_error.__code__.co_firstlineno

Loading…
Cancel
Save