Skip to main content

modern-di-aiogram

PyPI version Supported Python versions Downloads Coverage CI License GitHub stars uv Ruff ty

modern-di integration for aiogram 3.x.

Full guide: aiogram integration docs

Usage example: examples/

Installation

uv add modern-di-aiogram      # or: pip install modern-di-aiogram

Usage

aiogram has no dependency-injection system of its own, so modern-di-aiogram pairs an @inject decorator with inert FromDI markers (or auto_inject=True to skip the decorator entirely). By default setup_di opens the root container on dispatcher startup, closes it on shutdown, and installs an outer middleware that builds a per-update Scope.REQUEST child container automatically.

import typing

from aiogram import Dispatcher
from aiogram.types import Message
from modern_di import Container, Group, Scope, providers
from modern_di_aiogram import FromDI, inject, setup_di


class Settings:
    def __init__(self) -> None:
        self.greeting = "hello"


class AppGroup(Group):
    settings = providers.Factory(Settings, scope=Scope.APP, cache=True)


dispatcher = Dispatcher()
container = Container(groups=[AppGroup])
setup_di(dispatcher, container)
container.validate()  # optional fail-fast; must come after setup_di registers its providers


@dispatcher.message()
@inject
async def greet(
    message: Message,
    settings: typing.Annotated[Settings, FromDI(AppGroup.settings)],
) -> None:
    await message.answer(f"{settings.greeting}, {message.from_user.first_name}")

Pass auto_inject=True to setup_di to wrap every handler already registered on the dispatcher, so individual handlers don't need @inject. Register handlers before startup for this to take effect. Sync handlers work with both @inject and auto_inject: aiogram runs them in a worker thread as usual, and their FromDI parameters resolve from the same per-update child container. The current aiogram.types.Update and the concrete event it carries (Message, CallbackQuery, and so on) are resolvable within DI via the pre-built aiogram_update_provider / aiogram_event_provider context providers. aiogram-dialog getters and callbacks are supported via modern_di_aiogram.dialog; see the docs.

Shutdown

aiogram can still be handling updates when it emits shutdown. In polling mode with handle_as_tasks=True (the default), each update runs in its own task that aiogram does not wait for. The webhook handlers in aiogram.webhook.aiohttp_server behave the same way, both inline and with handle_in_background=True. Before closing the root container, setup_di waits until every update that entered its middleware has finished, so those handlers can still resolve APP-scoped dependencies and APP finalizers run after them.

Shutdown runs in three steps. First the plugin waits for the updates in flight. Then the shutdown handlers run: the dispatcher's own, including aiogram's FSM storage close and handlers registered after setup_di, and then those of every included router. Last, the root container closes and the APP finalizers run. On each startup, setup_di moves the wait to the front of the dispatcher's shutdown handlers and the close to the end of the shutdown handlers of the last router aiogram runs on shutdown. So every shutdown handler runs after the updates have finished and can still resolve APP-scoped dependencies. A shutdown handler added during or after startup, for example by a startup handler, or on a router included at that point, runs after the close until the next startup. If shutdown is emitted without a startup, the wait and the close stay where setup_di registered them among the dispatcher's shutdown handlers, so the root container still closes.

The wait has no limit by default. Pass shutdown_timeout in seconds to cap it:

setup_di(dispatcher, container, shutdown_timeout=10)

When the timeout expires, shutdown goes on, the root container is closed anyway, and a warning on the modern_di_aiogram logger says how many updates were still running. Those handlers then fail with ContainerClosedError on their next APP-scoped resolve.

Some things can close before this wait starts, and the plugin cannot change that. In webhook mode, SimpleRequestHandler.register closes the bot session on aiohttp shutdown, and when it is registered before setup_application, that happens before the dispatcher's shutdown. A handler that calls the Bot API during the wait may then fail. If shutdown is emitted without a startup, aiogram's FSM storage close and the shutdown handlers registered before setup_di also run before the wait.

Sharing a container

When one container serves several apps, for example a bot and a web API in the same process, exactly one owner should open and close it. Pass manage_lifespan=False so the dispatcher leaves the root container alone, and open it yourself around running the apps:

setup_di(dispatcher, container, manage_lifespan=False)

async with container:
    await dispatcher.start_polling(bot)

Each update still gets its own Scope.REQUEST child container, closed when the update is handled. APP-scoped finalizers run once, when the async with block exits.

With manage_lifespan=False the plugin does not wait for updates in flight either. start_polling can return while update tasks are still running, and the async with block then closes the container under them. To avoid that, poll with handle_as_tasks=False, or register your own outer update middleware that counts updates in flight and wait for the count to reach zero before leaving the block.

API

Symbol Description
setup_di(dispatcher, container, *, auto_inject=False, manage_lifespan=True, shutdown_timeout=None) Stores the container on the dispatcher, registers the update/event providers, opens it on dispatcher.startup, and installs the per-update middleware. On shutdown it waits for updates in flight before any other shutdown handler runs, for at most shutdown_timeout seconds when set, and closes the root after every shutdown handler of the dispatcher and its routers. With auto_inject=True, also wraps every handler already registered at startup. With manage_lifespan=False, never opens, waits or closes; per-update child containers are still built and closed. Returns the container
FromDI(dependency) Inert marker (used with @inject) that resolves a provider or type from the per-update child container
inject(handler) Decorator for an aiogram handler, sync or async; resolves its FromDI-annotated parameters and returns a wrapper of the same kind. 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
aiogram_update_provider ContextProvider for the current aiogram.types.Update (REQUEST scope)
aiogram_event_provider ContextProvider for the current aiogram.types.TelegramObject (REQUEST scope): the concrete event unwrapped from the Update

📦 PyPI

📝 License

Part of modern-python

Built on modern-di, a dependency-injection framework with an IoC container and scopes.

Browse the full list of templates and libraries in modern-python; the org profile has the categorized index.

Metadata

Release files for modern-di-aiogram 4.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for modern-di-aiogram 4.3.0
File Size Uploaded
modern_di_aiogram-4.3.0.tar.gz 8.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for modern-di-aiogram 4.3.0
File Interpreter ABI Platform
modern_di_aiogram-4.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 17.3 kB

Release files / modern_di_aiogram-4.3.0.tar.gz

Download URL modern_di_aiogram-4.3.0.tar.gz
Size 8.6 kB
Tags Source
SHA-256 checksum
How to use checksums
91da21a58254e377954ba3354073e9f7622cdf7755cdeee98a2b40ed8bc98d07
BLAKE2b-256 checksum
How to use checksums
8282797b44558676e05997185a948fd5754d378df94bf1a0c48e189fbab36fac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / modern_di_aiogram-4.3.0-py3-none-any.whl

Download URL modern_di_aiogram-4.3.0-py3-none-any.whl
Size 8.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
67f62ce361f174da05b72cebd05bfcb0f63e76e0704e72f876b3de56a33619cf
BLAKE2b-256 checksum
How to use checksums
1e61f9c53c57b75dc25b10bc0248e73643ea1ea2fbf0ef8a3f7030b2058d3069
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

4.3.4

2 release files

4.3.3

2 release files

4.3.2

2 release files

4.3.1

2 release files

This release

4.3.0 This release

2 release files

4.2.0

2 release files

4.1.0

2 release files

4.0.0

2 release files

3.1.0

2 release files

3.0.3

2 release files

3.0.2

2 release files

3.0.1

2 release files

3.0.0

2 release files

2.2.0

2 release files

2.1.0

2 release files

2.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page