Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,18 @@


_UNINSTALLED_PACKAGES = frozenset(
{"aiohttp", "fastapi", "litestar", "modern_di_litestar", "modern_di_pytest", "pydantic_ai", "redis", "sqlalchemy"}
{
"aiohttp",
"fastapi",
"faststream",
"litestar",
"modern_di_litestar",
"modern_di_pytest",
"pydantic_ai",
"redis",
"sqlalchemy",
"uvicorn",
}
)

_EXCLUDED_PAGES = (
Expand Down
6 changes: 5 additions & 1 deletion docs/integrations/aiogram.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ to a WebSocket. See [the scope hierarchy](../providers/scopes.md#what-each-scope
`dispatcher.shutdown`, and closing runs APP-scoped finalizers. `start_polling()` emits both
events, and so does an aiohttp app wired with aiogram's `setup_application()`.

Since modern-di-aiogram 4.2.0, `setup_di(dispatcher, container, manage_lifespan=False)` registers
neither hook and leaves the root container's open and close to you, for a container shared with
another app; see [Lifecycle](../providers/lifecycle.md#closing-and-reopening).

## Framework context objects

Two types resolve by annotation: `aiogram.types.Update` and `aiogram.types.TelegramObject`,
Expand Down Expand Up @@ -356,7 +360,7 @@ await test_report()

| Symbol | Description |
|---|---|
| `setup_di(dispatcher, container, *, auto_inject=False)` | Stores the container on the dispatcher, registers `aiogram_update_provider`/`aiogram_event_provider`, wires `dispatcher.startup`/`dispatcher.shutdown` to open/close the container, and installs the per-update middleware. With `auto_inject=True`, also wraps every handler already registered on the dispatcher at startup. |
| `setup_di(dispatcher, container, *, auto_inject=False, manage_lifespan=True)` | Stores the container on the dispatcher, registers `aiogram_update_provider`/`aiogram_event_provider`, wires `dispatcher.startup`/`dispatcher.shutdown` to open/close the container (not with `manage_lifespan=False`), and installs the per-update middleware. With `auto_inject=True`, also wraps every handler already registered on the dispatcher at startup. |
| `FromDI(dependency)` | Marker (used with `@inject`) that resolves a provider or type from the per-update child container. |
| `inject` | Decorator for a sync or `async def` aiogram handler; resolves its `FromDI`-annotated parameters. Not needed when `setup_di(..., auto_inject=True)` is used. Raises `RuntimeError` naming `setup_di` when an update reaches it without the middleware installed. |
| `fetch_di_container(dispatcher)` | Returns the root `Container` stored on the dispatcher. |
Expand Down
6 changes: 5 additions & 1 deletion docs/integrations/aiohttp.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ The root container is open from the moment you construct it. `setup_di` reopens
closes it with `close_async()` on cleanup. An app attached with `add_subapp` receives both signals
from its parent, so a `setup_di`-wired sub-app is closed too.

Since modern-di-aiohttp 4.2.0, `setup_di(app, container, manage_lifespan=False)` registers neither
the startup nor the cleanup hook and leaves the root container's open and close to you, for a
container shared with another app; see [Lifecycle](../providers/lifecycle.md#closing-and-reopening).

A sub-app route resolves from the sub-app's container when the sub-app called `setup_di`, and from
the parent's otherwise. When both call it, each middleware opens and closes its own child container
for the request. Before modern-di-aiohttp 4.1.0, calling `setup_di` on both made every sub-app route
Expand Down Expand Up @@ -260,7 +264,7 @@ with warnings.catch_warnings():

| Symbol | Description |
|---|---|
| `setup_di(app, container)` | Stores the container on the app, registers the request and websocket context providers, reopens the container on startup and closes it on cleanup, and installs the middleware that builds a per-connection child container; returns the container. |
| `setup_di(app, container, *, manage_lifespan=True)` | Stores the container on the app, registers the request and websocket context providers, reopens the container on startup and closes it on cleanup (neither with `manage_lifespan=False`), and installs the middleware that builds a per-connection child container; returns the container. |
| `FromDI(dependency)` | Marker (used with `@inject`) that resolves a provider or type from the per-connection child container. |
| `inject` | Decorator for a sync or `async def` `handler(request: web.Request, ...)`, or an `async def` method of a `web.View` subclass; resolves its `FromDI`-annotated parameters. Raises `TypeError` when the handler receives neither a `web.Request` nor a `web.View`, and `RuntimeError` naming `setup_di` when the request did not pass through the middleware. |
| `fetch_di_container(app)` | Returns the root `Container` stored on the app. |
Expand Down
6 changes: 5 additions & 1 deletion docs/integrations/arq.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ comparable to a websocket connection.
worker that starts again on the same container, after a restart or in a test that
runs the worker twice, reopens it on startup.

Since modern-di-arq 4.1.0, `setup_di(worker_settings, container, manage_lifespan=False)`
leaves the root container's open and close to you, for a container shared with another
app; see [Lifecycle](../providers/lifecycle.md#closing-and-reopening).

A hook you already defined on `WorkerSettings` still runs. Yours runs after ours
on startup and job start, and before ours on shutdown and job end. The root
container is open in all of them, but for an `@inject` task the job's child is
Expand Down Expand Up @@ -169,7 +173,7 @@ async def test_run_report() -> None:

| Symbol | Description |
|---|---|
| `setup_di(worker_settings, container)` | Seed the root container into arq's `ctx` and wire root + per-job lifecycle onto arq's `on_startup`/`on_shutdown`/`on_job_start`/`on_job_end` hooks. Accepts a `WorkerSettings` class/object or a settings `dict`; composes with existing hooks; returns the container. Raises `TypeError` if called twice on the same `worker_settings`. |
| `setup_di(worker_settings, container, *, manage_lifespan=True)` | Seed the root container into arq's `ctx` and wire root + per-job lifecycle onto arq's `on_startup`/`on_shutdown`/`on_job_start`/`on_job_end` hooks; with `manage_lifespan=False`, the hooks leave the root alone. Accepts a `WorkerSettings` class/object or a settings `dict`; composes with existing hooks; returns the container. Raises `TypeError` if called twice on the same `worker_settings`. |
| `FromDI(provider_or_type)` | Marker for `Annotated[T, FromDI(...)]` in task signatures; accepts a provider instance or a plain type. |
| `@inject` | Decorator that resolves `FromDI`-annotated parameters from the per-job `Scope.REQUEST` child container. Order-insensitive; passthrough for tasks with no `FromDI`; raises `TypeError` at decoration if the task also declares `*args`/`**kwargs`; raises `RuntimeError` naming `setup_di` when a job reaches it without the modern-di hooks installed. |
| `fetch_di_container(ctx)` | Returns the root container from an arq `ctx` dict. |
9 changes: 8 additions & 1 deletion docs/integrations/celery.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ harmless.
Earlier releases store the container on `app.conf`, which cannot be pickled, and the pool
fails at startup under spawn.

Since modern-di-celery 4.1.0, `setup_di(app, container, manage_lifespan=False)` stops the
signals in the table from opening or closing the root and leaves that to you, for a container
shared with other code in the process; see
[Lifecycle](../providers/lifecycle.md#closing-and-reopening). A prefork child then inherits the
open container and nothing closes it there. If the child's APP-scoped finalizers must run, call
`container.close_sync()` from your own `worker_process_shutdown` handler.

Code that calls tasks without a worker, such as a script or a test in eager mode,
fires none of these signals. Close the root yourself when it is done, for example
with `with container:`, as in [Testing](#testing).
Expand Down Expand Up @@ -179,7 +186,7 @@ with container:

| Symbol | Description |
|---|---|
| `setup_di(app, container)` | Wire the APP-scope container into Celery: registers it for the app and opens/closes it on `worker_process_init`/`worker_process_shutdown` and `worker_init`/`worker_shutdown`. Returns the container. |
| `setup_di(app, container, *, manage_lifespan=True)` | Wire the APP-scope container into Celery: registers it for the app and opens/closes it on `worker_process_init`/`worker_process_shutdown` and `worker_init`/`worker_shutdown`, unless `manage_lifespan=False`. Returns the container. |
| `FromDI(provider_or_type)` | Marker for `Annotated[T, FromDI(...)]` in task signatures; accepts a provider instance or a plain type. |
| `@inject` | Decorator that builds a `Scope.REQUEST` child container per call, resolves `FromDI`-annotated parameters from it, and closes the child container with `close_sync()` afterwards. Raises `RuntimeError` naming `setup_di` when a task reaches it without `setup_di` called. A task with `FromDI` parameters that also declares `*args`/`**kwargs` raises `TypeError` at decoration. |
| `DITask` | `Task` subclass that applies `@inject` to a task's `run` method automatically; pass `task_cls=DITask` to `Celery(...)` or `base=DITask` to `@app.task(...)`. |
Expand Down
8 changes: 7 additions & 1 deletion docs/integrations/fastapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ a `lifespan=` you passed to `FastAPI()` stays the outer context and the state it
through, the container reopens on startup, and it closes with `close_async()` on shutdown. The
lifespan shutdown is the only thing that closes the root container.

Since modern-di-fastapi 4.1.0, `setup_di(app, container, manage_lifespan=False)` leaves the root
container's open and close to you, for a container shared with another app; see
[FastAPI and FastStream sharing a container](../recipes/shared-container.md). The flag covers only
this app's own container. The lifespan still opens and closes the container of each
[mounted app](#mounted-apps), unless that app's own `setup_di` set the flag to `False`.

### Mounted apps

A FastAPI app mounted with `app.mount` can call `setup_di` with its own container, and its routes
Expand Down Expand Up @@ -243,7 +249,7 @@ with TestClient(request_app) as client:

| Symbol | Description |
|---|---|
| `setup_di(app, container)` | Stores the container on the app, registers the request and websocket context providers, and wraps the app's lifespan (any `lifespan=` you passed included) so the container reopens on startup and closes on shutdown, along with the root container of each mounted FastAPI app that called `setup_di`; returns the container. |
| `setup_di(app, container, *, manage_lifespan=True)` | Stores the container on the app, registers the request and websocket context providers, and wraps the app's lifespan (any `lifespan=` you passed included) so the container reopens on startup and closes on shutdown, along with the root container of each mounted FastAPI app that called `setup_di`; with `manage_lifespan=False`, this app's own container is left to you. Returns the container. |
| `FromDI(dependency, *, use_cache=True)` | A `fastapi.Depends` that resolves a provider or type from the connection's child container (REQUEST for HTTP, SESSION for a websocket). Raises `RuntimeError` naming `setup_di` when a request reaches it without `setup_di` called. |
| `build_di_container(connection)` | A `fastapi.Depends` callable that yields the connection's child container: REQUEST scope for an HTTP request, SESSION scope for a websocket. |
| `fastapi_request_provider` | `ContextProvider` for `fastapi.Request` (REQUEST scope), auto-registered. |
Expand Down
7 changes: 6 additions & 1 deletion docs/integrations/faststream.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ installs the middleware on every broker. An `after_shutdown` hook closes the roo
`close_async()`, which runs APP-scoped finalizers. Because startup reopens the container,
an app that stops and starts again on the same container works.

Since modern-di-faststream 4.1.0, `setup_di(app, container, manage_lifespan=False)` registers
neither the open hook nor the close hook and leaves the root container's open and close to you, for
a container shared with another app; see
[FastAPI and FastStream sharing a container](../recipes/shared-container.md).

Since modern-di-faststream 4.0.1, the integration keeps its containers in the FastStream context
under the keys `modern_di_root_container` and `modern_di_request_container`, so a context value of
your own named `di_container` or `request_container` no longer replaces them. Code that read
Expand Down Expand Up @@ -242,7 +247,7 @@ Between `setup_di` and startup no broker carries the middleware yet; see

| Symbol | Description |
|---|---|
| `setup_di(app, container)` | Wire the APP-scope container into FastStream: at startup, installs the middleware that creates a REQUEST child container per message on every broker of the app, and raises `RuntimeError` if there is none by then; closes the APP container at shutdown. |
| `setup_di(app, container, *, manage_lifespan=True)` | Wire the APP-scope container into FastStream: at startup, installs the middleware that creates a REQUEST child container per message on every broker of the app, and raises `RuntimeError` if there is none by then; opens the APP container at startup and closes it at shutdown, unless `manage_lifespan=False`. |
| `FromDI(dependency, *, use_cache=True, cast=False)` | A `faststream.Depends` wrapper for `Annotated[T, FromDI(...)]` in subscriber signatures; accepts a provider instance or a plain type. `use_cache` and `cast` are passed through to `faststream.Depends`. Raises `RuntimeError` naming `setup_di` when a message reaches it without the middleware installed. |
| `fetch_di_container(app)` | Returns the APP-scope container registered with the FastStream app. |
| `faststream_message_provider` | `ContextProvider` for the current `faststream.StreamMessage`. |
6 changes: 5 additions & 1 deletion docs/integrations/litestar.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ thing that closes the root container, so when the server skips the lifespan, req
succeed but its finalizers never run. Close it yourself (`await container.close_async()`) at
shutdown in that case.

Since modern-di-litestar 4.1.0, `ModernDIPlugin(container, manage_lifespan=False)` adds no lifespan
hook and leaves the root container's open and close to you, for a container shared with another
app; see [Lifecycle](../providers/lifecycle.md#closing-and-reopening).

## Websockets

In a websocket handler, `di_container` is the connection's `Scope.SESSION` container, which lives
Expand Down Expand Up @@ -234,7 +238,7 @@ with TestClient(request_app) as client:

| Symbol | Description |
|---|---|
| `ModernDIPlugin(container, autowired_groups=None)` | Litestar `InitPlugin` that stores the container on the app state, registers the request and websocket context providers and the `di_container` dependency, appends a lifespan hook that closes the container on shutdown, and (if `autowired_groups` is given) exposes each provider in those groups as a Litestar dependency keyed by attribute name. |
| `ModernDIPlugin(container, autowired_groups=None, *, manage_lifespan=True)` | Litestar `InitPlugin` that stores the container on the app state, registers the request and websocket context providers and the `di_container` dependency, appends a lifespan hook that closes the container on shutdown (none with `manage_lifespan=False`), and (if `autowired_groups` is given) exposes each provider in those groups as a Litestar dependency keyed by attribute name. |
| `FromDI(dependency)` | Returns a Litestar `Provide` that resolves a provider or type from the connection's child container (REQUEST for HTTP, SESSION for a websocket). |
| `di_container` | The dependency name the plugin registers for the connection's child container; declare `di_container: NamedDependency[Container]` to receive it. |
| `fetch_di_container(app)` | Returns the root `Container` stored on the Litestar app. |
Expand Down
8 changes: 7 additions & 1 deletion docs/integrations/starlette.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ a `lifespan=` you passed to `Starlette()` stays the outer context and the state
through, the container reopens on startup, and it closes with `close_async()` on shutdown. The
lifespan shutdown is the only thing that closes the root container.

Since modern-di-starlette 4.2.0, `setup_di(app, container, manage_lifespan=False)` leaves the root
container's open and close to you, for a container shared with another app; see
[Lifecycle](../providers/lifecycle.md#closing-and-reopening). The flag covers only this app's own
container. The lifespan still opens and closes the container of each [mounted app](#mounted-apps),
unless that app's own `setup_di` set the flag to `False`.

### Mounted apps

An app mounted with `Mount` can call `setup_di` with its own container. Its routes then resolve
Expand Down Expand Up @@ -302,7 +308,7 @@ with TestClient(request_app) as client:

| Symbol | Description |
|---|---|
| `setup_di(app, container)` | Stores the container on `app.state`, registers the request and websocket context providers, wraps the lifespan so the container reopens on startup and closes on shutdown, along with the root container of each mounted app that called `setup_di`, and installs the middleware that builds a per-connection child container; returns the container. |
| `setup_di(app, container, *, manage_lifespan=True)` | Stores the container on `app.state`, registers the request and websocket context providers, wraps the lifespan so the container reopens on startup and closes on shutdown, along with the root container of each mounted app that called `setup_di`, and installs the middleware that builds a per-connection child container. With `manage_lifespan=False`, this app's own container is left to you. Returns the container. |
| `FromDI(dependency)` | Marker (used with `@inject`) that resolves a provider or type from the per-connection child container. |
| `inject` | Decorator for a sync or `async def` `handler(connection: Request | WebSocket, ...)`, a function endpoint or a method of an `HTTPEndpoint` / `WebSocketEndpoint` subclass; resolves its `FromDI`-annotated parameters. Raises `TypeError` when the handler receives no `Request` or `WebSocket` positionally, and `RuntimeError` naming `setup_di` when the connection did not pass through the middleware. |
| `fetch_di_container(app)` | Returns the root `Container` stored on `app.state`. |
Expand Down
6 changes: 5 additions & 1 deletion docs/integrations/taskiq.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ comparable to websockets.
`close_async()` on `WORKER_SHUTDOWN`, which runs APP-scoped finalizers. `taskiq worker`
fires both. With an `InMemoryBroker`, `async with broker:` fires both too.

Since modern-di-taskiq 4.1.0, `setup_di(broker, container, manage_lifespan=False)` hooks neither
event and leaves the root container's open and close to you, for a container shared with another
app; see [Lifecycle](../providers/lifecycle.md#closing-and-reopening).

!!! warning "`run_receiver_task` never closes the root"
A receiver embedded with `taskiq.api.run_receiver_task(...)` never calls
`broker.shutdown()`, so `WORKER_SHUTDOWN` never fires and APP-scoped finalizers
Expand Down Expand Up @@ -167,7 +171,7 @@ assert result.return_value == {"service": "catalog"}

| Symbol | Description |
|---|---|
| `setup_di(broker, container)` | Wire the APP-scope container into taskiq: a REQUEST child container is then built through `TaskiqDepends` for each task that uses `FromDI`; opens/closes the APP container on worker startup/shutdown. |
| `setup_di(broker, container, *, manage_lifespan=True)` | Wire the APP-scope container into taskiq: a REQUEST child container is then built through `TaskiqDepends` for each task that uses `FromDI`; opens/closes the APP container on worker startup/shutdown, unless `manage_lifespan=False`. |
| `FromDI(provider_or_type, *, use_cache=True)` | Marker for `Annotated[T, FromDI(...)]` in task signatures; accepts a provider instance or a plain type. `use_cache` is passed through to `TaskiqDepends`. Raises `RuntimeError` naming `setup_di` when a task reaches it without `setup_di` called. |
| `fetch_di_container(broker)` | Returns the APP-scope container registered with the taskiq broker. |
| `taskiq_message_provider` | `ContextProvider` for the current `taskiq.TaskiqMessage`. |
Loading
Loading