mirror of https://github.com/tiangolo/fastapi.git
🐛 Preserve traceback when exception is raised in sync dependency with `yield` (#5823)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
This commit is contained in:
parent
8255edfecf
commit
4f8157588e
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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…
Reference in New Issue