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
3 changes: 2 additions & 1 deletion CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ updates in flight finish first, then the other shutdown handlers run, then it is
The single `Scope.REQUEST` child the middleware builds for one incoming `Update`, seeded with that
`Update` and its concrete event. Every handler, dialog getter and dialog callback that the update
reaches shares this one container, and it is closed when the middleware returns, on the error path
included.
included. An error handler runs after that close, so the same middleware on `dispatcher.errors`
builds it a second child, seeded with the failing `Update` and its event.
_Avoid_: request container. The unit of work is an update, not an HTTP request, the same reason
`modern-di` rejects "request" for `Connection`.

Expand Down
8 changes: 5 additions & 3 deletions modern_di_aiogram/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import aiogram
from aiogram import BaseMiddleware, Dispatcher
from aiogram.dispatcher.event.handler import HandlerObject
from aiogram.types import TelegramObject, Update
from aiogram.types import ErrorEvent, TelegramObject, Update
from aiogram.types.update import UpdateTypeLookupError
from modern_di import Container, Scope, integrations, providers

Expand Down Expand Up @@ -50,9 +50,10 @@ async def __call__(
event: TelegramObject,
data: dict[str, typing.Any],
) -> typing.Any: # noqa: ANN401
context: dict[type[typing.Any], typing.Any] = {Update: event}
update = event.update if isinstance(event, ErrorEvent) else event
context: dict[type[typing.Any], typing.Any] = {Update: update}
with contextlib.suppress(UpdateTypeLookupError):
context[TelegramObject] = typing.cast("Update", event).event
context[TelegramObject] = typing.cast("Update", update).event
self.in_flight += 1
try:
async with self.container.build_child_container(scope=Scope.REQUEST, context=context) as child_container:
Expand Down Expand Up @@ -116,6 +117,7 @@ def open_root() -> None:

dispatcher.startup.register(open_root)
dispatcher.update.outer_middleware(middleware)
dispatcher.errors.outer_middleware(middleware)
if auto_inject:
dispatcher.startup.register(functools.partial(_inject_router, dispatcher))
return container
Expand Down
28 changes: 27 additions & 1 deletion tests/test_inject.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import pytest
from aiogram import Bot, Dispatcher
from aiogram.dispatcher.event.bases import UNHANDLED
from aiogram.types import Message, Update
from aiogram.types import ErrorEvent, Message, Update
from modern_di import Container, Group, Scope, providers

from modern_di_aiogram import FromDI, inject, setup_di
Expand Down Expand Up @@ -104,6 +104,32 @@ async def handler(
assert teardowns == ["closed"] # per-update child closed (finalizer ran) on the error path


async def test_inject_resolves_in_an_error_handler(dispatcher: Dispatcher, bot: Bot) -> None:
seen: dict[str, typing.Any] = {}

@dispatcher.message()
async def handler(message: Message) -> None: # noqa: ARG001
msg = "boom"
raise ValueError(msg)

@dispatcher.errors()
@inject
async def on_error(
event: ErrorEvent,
request_instance: typing.Annotated[DependentCreator, FromDI(Dependencies.request_factory)],
event_type: typing.Annotated[str, FromDI(Dependencies.event_type)],
) -> None:
seen["exception"] = str(event.exception)
seen["request_ok"] = isinstance(request_instance, DependentCreator)
seen["event_type"] = event_type

await dispatcher.emit_startup()
await dispatcher.feed_update(bot, make_message_update())
await dispatcher.emit_shutdown()

assert seen == {"exception": "boom", "request_ok": True, "event_type": "Message"}


async def test_inject_without_setup_di_names_the_fix(bot: Bot) -> None:
dispatcher = Dispatcher()

Expand Down
Loading