Repository: fastapi/fastapi

Issue:
Traceback stack does not show exact place of error
### Discussed in https://github.com/fastapi/fastapi/discussions/8428

<div type='discussions-op-text'>

<sup>Originally posted by **NewSouthMjos** December  5, 2022</sup>
### First Check

- [X] I added a very descriptive title to this issue.
- [X] I used the GitHub search to find a similar issue and didn't find it.
- [X] I searched the FastAPI documentation, with the integrated search.
- [X] I already searched in Google "How to X in FastAPI" and didn't find any information.
- [X] I already read and followed all the tutorial in the docs and didn't find an answer.
- [X] I already checked if it is not related to FastAPI but to [Pydantic](https://github.com/samuelcolvin/pydantic).
- [X] I already checked if it is not related to FastAPI but to [Swagger UI](https://github.com/swagger-api/swagger-ui).
- [X] I already checked if it is not related to FastAPI but to [ReDoc](https://github.com/Redocly/redoc).

### Commit to Help

- [x] I commit to help with one of those options 👆

### Example Code

```python
import uvicorn
from fastapi import FastAPI, Depends


app = FastAPI()


def get_something_sync():
    yield True


async def get_something_async():
    yield True


@app.get('/1')
def router_func(dependency=Depends(get_something_sync)):
    raise ValueError
    return


@app.get('/2')
def router_func(dependency=Depends(get_something_async)):
    raise ValueError
    return


if __name__ == "__main__":
    uvicorn.run("main:app", host="0.0.0.0", port=5600, workers=1)
```


### Description

If dependency function uses yield, injecting dependency with sync def function (get_something_sync), the error traceback will broke - it doesn't locate the place where it was rise (should be "/app/main.py", line 24)
calling endpoint /1:
```
fastapi_dependency  | INFO:     10.77.78.83:60070 - "GET /1 HTTP/1.1" 500 Internal Server Error
fastapi_dependency  | ERROR:    Exception in ASGI application
fastapi_dependency  | Traceback (most recent call last):
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 419, in run_asgi
fastapi_dependency  |     result = await app(  # type: ignore[func-returns-value]
fastapi_dependency  |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
fastapi_dependency  |     return await self.app(scope, receive, send)
fastapi_dependency  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/fastapi/applications.py", line 270, in __call__
fastapi_dependency  |     await super().__call__(scope, receive, send)
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/applications.py", line 124, in __call__
fastapi_dependency  |     await self.middleware_stack(scope, receive, send)
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 184, in __call__
fastapi_dependency  |     raise exc
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 162, in __call__
fastapi_dependency  |     await self.app(scope, receive, _send)
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
fastapi_dependency  |     raise exc
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
fastapi_dependency  |     await self.app(scope, receive, sender)
fastapi_dependency  |   File "/usr/local/lib/python3.11/contextlib.py", line 222, in __aexit__
fastapi_dependency  |     await self.gen.athrow(typ, value, traceback)
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/fastapi/concurrency.py", line 36, in contextmanager_in_threadpool
fastapi_dependency  |     raise e
fastapi_dependency  | ValueError
```

When injecting dependency with async def function (get_something_async), the error traceback seems to be right
endpoint /2:
```
fastapi_dependency  | INFO:     10.77.78.83:60067 - "GET /2 HTTP/1.1" 500 Internal Server Error
fastapi_dependency  | ERROR:    Exception in ASGI application
fastapi_dependency  | Traceback (most recent call last):
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py", line 419, in run_asgi
fastapi_dependency  |     result = await app(  # type: ignore[func-returns-value]
fastapi_dependency  |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
fastapi_dependency  |     return await self.app(scope, receive, send)
fastapi_dependency  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/fastapi/applications.py", line 270, in __call__
fastapi_dependency  |     await super().__call__(scope, receive, send)
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/applications.py", line 124, in __call__
fastapi_dependency  |     await self.middleware_stack(scope, receive, send)
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 184, in __call__
fastapi_dependency  |     raise exc
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 162, in __call__
fastapi_dependency  |     await self.app(scope, receive, _send)
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 79, in __call__
fastapi_dependency  |     raise exc
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
fastapi_dependency  |     await self.app(scope, receive, sender)
fastapi_dependency  |   File "/usr/local/lib/python3.11/contextlib.py", line 222, in __aexit__
fastapi_dependency  |     await self.gen.athrow(typ, value, traceback)
fastapi_dependency  |   File "/app/main.py", line 13, in get_something_async
fastapi_dependency  |     yield True
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__
fastapi_dependency  |     raise e
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
fastapi_dependency  |     await self.app(scope, receive, send)
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 706, in __call__
fastapi_dependency  |     await route.handle(scope, receive, send)
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 276, in handle
fastapi_dependency  |     await self.app(scope, receive, send)
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 66, in app
fastapi_dependency  |     response = await func(request)
fastapi_dependency  |                ^^^^^^^^^^^^^^^^^^^
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 235, in app
fastapi_dependency  |     raw_response = await run_endpoint_function(
fastapi_dependency  |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 163, in run_endpoint_function
fastapi_dependency  |     return await run_in_threadpool(dependant.call, **values)
fastapi_dependency  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/starlette/concurrency.py", line 41, in run_in_threadpool
fastapi_dependency  |     return await anyio.to_thread.run_sync(func, *args)
fastapi_dependency  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/anyio/to_thread.py", line 31, in run_sync
fastapi_dependency  |     return await get_asynclib().run_sync_in_worker_thread(
fastapi_dependency  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 937, in run_sync_in_worker_thread
fastapi_dependency  |     return await future
fastapi_dependency  |            ^^^^^^^^^^^^
fastapi_dependency  |   File "/usr/local/lib/python3.11/site-packages/anyio/_backends/_asyncio.py", line 867, in run
fastapi_dependency  |     result = context.run(func, *args)
fastapi_dependency  |              ^^^^^^^^^^^^^^^^^^^^^^^^
fastapi_dependency  |   File "/app/main.py", line 24, in router_func
fastapi_dependency  |     raise ValueError
fastapi_dependency  | ValueError
```

On python 3.10 there were no such a problem. 
Created repo for reproduce the problem: [https://github.com/NewSouthMjos/fastapi_dependency_test](https://github.com/NewSouthMjos/fastapi_dependency_test)
So, should the sync way to inject dependecy show exact plase of where is was raised?

### Operating System

Linux

### Operating System Details

Running in a docker

### FastAPI Version

0.88.0

### Python Version

3.11

### Additional Context

uvicorn[standard]==0.20.0</div>

Discussion:
@Kludex: I've simplified a bit the PR that solves this: https://github.com/fastapi/fastapi/pull/5823@Kludex: I think the issue is not what it's described here, but I'll not spam another one to not create confusion.

The issue is actually this one: https://github.com/fastapi/fastapi/discussions/12999

The one mentioned in this issue is very similar, but it was solved.

Solution patch:
diff --git a/fastapi/concurrency.py b/fastapi/concurrency.py
index 894bd3ed11873..3202c70789ad3 100644
--- a/fastapi/concurrency.py
+++ b/fastapi/concurrency.py
@@ -1,7 +1,7 @@
 from contextlib import asynccontextmanager as asynccontextmanager
 from typing import AsyncGenerator, ContextManager, TypeVar
 
-import anyio
+import anyio.to_thread
 from anyio import CapacityLimiter
 from starlette.concurrency import iterate_in_threadpool as iterate_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:
         ok = bool(
             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:


Test patch:
diff --git a/tests/test_exception_handlers.py b/tests/test_exception_handlers.py
index 67a4becec7adc..6a3cbd830d0bc 100644
--- a/tests/test_exception_handlers.py
+++ b/tests/test_exception_handlers.py
@@ -1,5 +1,5 @@
 import pytest
-from fastapi import FastAPI, HTTPException
+from fastapi import Depends, FastAPI, HTTPException
 from fastapi.exceptions import RequestValidationError
 from fastapi.testclient import TestClient
 from starlette.responses import JSONResponse
@@ -28,6 +28,18 @@ def server_error_exception_handler(request, exception):
 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")
 def route_with_http_exception():
     raise HTTPException(status_code=400)
@@ -65,3 +77,12 @@ def test_override_server_error_exception_response():
     response = client.get("/server-error")
     assert response.status_code == 500
     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