diff --git a/modern_di_fastapi/main.py b/modern_di_fastapi/main.py index c044d7f..c1415c9 100644 --- a/modern_di_fastapi/main.py +++ b/modern_di_fastapi/main.py @@ -21,11 +21,11 @@ def fetch_di_container(app_: fastapi.FastAPI) -> Container: @contextlib.asynccontextmanager async def _lifespan_manager(app_: fastapi.FastAPI) -> typing.AsyncIterator[None]: - container = fetch_di_container(app_) - try: + # ``async with`` reopens the root container on each startup (``__aenter__``) + # and closes it on shutdown, so a second lifespan cycle against the same + # container works instead of raising ContainerClosedError. + async with fetch_di_container(app_): yield - finally: - await container.close_async() def setup_di(app: fastapi.FastAPI, container: Container) -> Container: diff --git a/pyproject.toml b/pyproject.toml index 1706ab9..d578924 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ classifiers = [ "Typing :: Typed", "Topic :: Software Development :: Libraries", ] -dependencies = ["fastapi>=0.100,<1", "modern-di>=2.16.1,<3"] +dependencies = ["fastapi>=0.100,<1", "modern-di>=2.19.0,<3"] version = "0" [project.urls] diff --git a/tests/test_lifespan.py b/tests/test_lifespan.py new file mode 100644 index 0000000..4080470 --- /dev/null +++ b/tests/test_lifespan.py @@ -0,0 +1,25 @@ +import typing + +import fastapi +from starlette import status +from starlette.testclient import TestClient + +from modern_di_fastapi import FromDI, fetch_di_container +from tests.dependencies import Dependencies, SimpleCreator + + +def test_lifespan_reopens_container_across_cycles(app: fastapi.FastAPI) -> None: + @app.get("/") + async def read_root(instance: typing.Annotated[SimpleCreator, FromDI(Dependencies.app_factory)]) -> None: + assert isinstance(instance, SimpleCreator) + + container = fetch_di_container(app) + + # First lifespan cycle: shutdown closes the root container. + with TestClient(app=app) as client: + assert client.get("/").status_code == status.HTTP_200_OK + assert container.closed + + # Second cycle must reopen the same container instead of raising ContainerClosedError. + with TestClient(app=app) as client: + assert client.get("/").status_code == status.HTTP_200_OK