Skip to content

Commit 0e91261

Browse files
committed
update modern-di
1 parent 2f0dcfe commit 0e91261

File tree

5 files changed

+73
-69
lines changed

5 files changed

+73
-69
lines changed

app/application.py

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import contextlib
2-
import typing
3-
41
import fastapi
52
import modern_di_fastapi
63
from advanced_alchemy.exceptions import DuplicateKeyError
@@ -15,40 +12,31 @@ def include_routers(app: fastapi.FastAPI) -> None:
1512
app.include_router(ROUTER, prefix="/api")
1613

1714

18-
class AppBuilder:
19-
def __init__(self) -> None:
20-
self.app: fastapi.FastAPI = fastapi.FastAPI(
21-
lifespan=self.lifespan_manager,
22-
)
23-
self.bootstrapper = FastAPIBootstrapper(
24-
bootstrap_config=FastAPIConfig(
25-
application=self.app,
26-
service_name=settings.service_name,
27-
service_version=settings.service_version,
28-
service_environment=settings.service_environment,
29-
service_debug=settings.service_debug,
30-
opentelemetry_endpoint=settings.opentelemetry_endpoint,
31-
sentry_dsn=settings.sentry_dsn,
32-
cors_allowed_origins=settings.cors_allowed_origins,
33-
cors_allowed_methods=settings.cors_allowed_methods,
34-
cors_allowed_headers=settings.cors_allowed_headers,
35-
cors_exposed_headers=settings.cors_exposed_headers,
36-
logging_buffer_capacity=settings.logging_buffer_capacity,
37-
swagger_offline_docs=settings.swagger_offline_docs,
38-
),
39-
)
40-
self.bootstrapper.bootstrap()
41-
self.di_container = modern_di_fastapi.setup_di(self.app)
42-
include_routers(self.app)
43-
self.app.add_exception_handler(
44-
DuplicateKeyError,
45-
exceptions.duplicate_key_error_handler, # type: ignore[arg-type]
46-
)
47-
48-
@contextlib.asynccontextmanager
49-
async def lifespan_manager(self, _: fastapi.FastAPI) -> typing.AsyncIterator[dict[str, typing.Any]]:
50-
async with self.di_container:
51-
yield {}
52-
53-
54-
application = AppBuilder().app
15+
def build_app() -> fastapi.FastAPI:
16+
bootstrapper = FastAPIBootstrapper(
17+
bootstrap_config=FastAPIConfig(
18+
service_name=settings.service_name,
19+
service_version=settings.service_version,
20+
service_environment=settings.service_environment,
21+
service_debug=settings.service_debug,
22+
opentelemetry_endpoint=settings.opentelemetry_endpoint,
23+
sentry_dsn=settings.sentry_dsn,
24+
cors_allowed_origins=settings.cors_allowed_origins,
25+
cors_allowed_methods=settings.cors_allowed_methods,
26+
cors_allowed_headers=settings.cors_allowed_headers,
27+
cors_exposed_headers=settings.cors_exposed_headers,
28+
logging_buffer_capacity=settings.logging_buffer_capacity,
29+
swagger_offline_docs=settings.swagger_offline_docs,
30+
),
31+
)
32+
app: fastapi.FastAPI = bootstrapper.bootstrap()
33+
modern_di_fastapi.setup_di(app)
34+
include_routers(app)
35+
app.add_exception_handler(
36+
DuplicateKeyError,
37+
exceptions.duplicate_key_error_handler, # type: ignore[arg-type]
38+
)
39+
return app
40+
41+
42+
application = build_app()

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ dev = [
3030
"pytest",
3131
"pytest-cov",
3232
"pytest-asyncio",
33+
"asgi_lifespan",
3334
"ruff",
3435
"mypy",
3536
"asyncpg-stubs",

tests/conftest.py

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

3+
import fastapi
34
import modern_di
45
import modern_di_fastapi
56
import pytest
7+
from asgi_lifespan import LifespanManager
68
from httpx import ASGITransport, AsyncClient
79
from sqlalchemy.ext.asyncio import AsyncSession
810

911
from app import ioc
10-
from app.application import application
12+
from app.application import build_app
1113

1214

1315
@pytest.fixture
14-
async def client() -> typing.AsyncIterator[AsyncClient]:
16+
async def app() -> typing.AsyncIterator[fastapi.FastAPI]:
17+
app_ = build_app()
18+
async with LifespanManager(app_):
19+
yield app_
20+
21+
22+
@pytest.fixture
23+
async def client(app: fastapi.FastAPI) -> typing.AsyncIterator[AsyncClient]:
1524
async with AsyncClient(
16-
transport=ASGITransport(app=application),
25+
transport=ASGITransport(app=app),
1726
base_url="http://test",
1827
) as client:
1928
yield client
2029

2130

2231
@pytest.fixture
23-
async def di_container() -> typing.AsyncIterator[modern_di.Container]:
24-
di_container_: typing.Final = modern_di_fastapi.fetch_di_container(application)
25-
async with di_container_:
26-
yield di_container_
32+
def di_container(app: fastapi.FastAPI) -> modern_di.Container:
33+
return modern_di_fastapi.fetch_di_container(app)
2734

2835

2936
@pytest.fixture(autouse=True)

tests/test_main.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,17 @@
11
import runpy
22
from unittest import mock
33

4-
import fastapi
54
import modern_di
65
import pytest
76

87
from app import ioc
9-
from app.application import AppBuilder
108

119

1210
def test_main(monkeypatch: pytest.MonkeyPatch) -> None:
1311
monkeypatch.setattr("granian.Granian", mock.Mock())
1412
runpy.run_module("app.__main__", run_name="__main__")
1513

1614

17-
async def test_app_lifespan() -> None:
18-
async with AppBuilder().lifespan_manager(fastapi.FastAPI()):
19-
pass
20-
21-
2215
async def test_session() -> None:
2316
async with (
2417
modern_di.Container(scope=modern_di.Scope.APP) as container,

uv.lock

Lines changed: 30 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)