Skip to content

Commit 7ccd8f7

Browse files
authored
fix: raise a clear RuntimeError when FromDI resolves without setup_di (#51)
* fix: raise a clear RuntimeError when FromDI resolves without setup_di * docs: note the RuntimeError FromDI raises without setup_di
1 parent a5e42ae commit 7ccd8f7

3 files changed

Lines changed: 21 additions & 3 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ The framework `Request` / `WebSocket` are resolvable within DI via the pre-built
7474
| Symbol | Description |
7575
|---|---|
7676
| `setup_di(app, container)` | Stores the container on `app.state` and composes the container's open/close into the app lifespan (nesting inside any existing `lifespan=`) |
77-
| `FromDI(provider, *, use_cache=True)` | FastAPI `Depends` that resolves a provider (or type) from the per-connection child container |
78-
| `fetch_di_container(app)` | Returns the app-scoped container from `app.state` |
77+
| `FromDI(provider, *, use_cache=True)` | FastAPI `Depends` that resolves a provider (or type) from the per-connection child container. Raises `RuntimeError` naming `setup_di` when a request reaches it without `setup_di` called |
78+
| `fetch_di_container(app)` | Returns the app-scoped container from `app.state`. Raises `RuntimeError` naming `setup_di` when called without `setup_di` called |
7979
| `build_di_container(connection)` | FastAPI `Depends` callable that yields the per-connection child container — `REQUEST` scope for an HTTP request, `SESSION` scope for a WebSocket |
8080
| `fastapi_request_provider` | `ContextProvider` for the current `fastapi.Request` |
8181
| `fastapi_websocket_provider` | `ContextProvider` for the current `fastapi.WebSocket` |

‎modern_di_fastapi/main.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@
2222

2323

2424
def fetch_di_container(app_: fastapi.FastAPI) -> Container:
25-
return typing.cast(Container, app_.state.di_container)
25+
try:
26+
return typing.cast(Container, app_.state.di_container)
27+
except AttributeError:
28+
msg = (
29+
"No modern-di container found on the app. "
30+
"Call setup_di(app, container) before using FromDI or fetch_di_container."
31+
)
32+
raise RuntimeError(msg) from None
2633

2734

2835
def _compose_lifespan(original: Lifespan[fastapi.FastAPI]) -> Lifespan[fastapi.FastAPI]:

‎tests/test_routes.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import typing
22

33
import fastapi
4+
import pytest
45
from modern_di import Container
56
from starlette import status
67
from starlette.testclient import TestClient
@@ -48,3 +49,13 @@ async def read_root(
4849
response = client.get("/")
4950
assert response.status_code == status.HTTP_200_OK
5051
assert response.json() is None
52+
53+
54+
def test_from_di_without_setup_di_raises_clear_error() -> None:
55+
app = fastapi.FastAPI()
56+
57+
@app.get("/")
58+
async def read_root(instance: typing.Annotated[SimpleCreator, FromDI(SimpleCreator)]) -> None: ...
59+
60+
with TestClient(app=app) as client, pytest.raises(RuntimeError, match=r"setup_di\(app, container\)"):
61+
client.get("/")

0 commit comments

Comments
 (0)