Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aiogram integration inject doesn't work with middlewares #185

Open
MassonNN opened this issue Jul 26, 2024 · 4 comments
Open

Aiogram integration inject doesn't work with middlewares #185

MassonNN opened this issue Jul 26, 2024 · 4 comments
Labels
enhancement New feature or request integrations

Comments

@MassonNN
Copy link

Description

As you know, aiogram integration, like any other, has a special function for preparing the main router to propagate the container into the handlers, middlewares, and etc. - setup_dishka(). But it works only with polling and webhooks, and not with manual update feeding via dp.feed_update.

Code example

Works good

async def start_bot():
    """Start the bot as coroutine."""
    bot_container = make_async_container(
        BotProvider(),
    )

    dp = await bot_container.get(Dispatcher)
    bot = await bot_container.get(Bot)

    setup_dishka(router=dp, container=bot_container, auto_inject=True)

    await dp.start_polling(bot)

And doesnt work:

async def start_bot():
    """Start the bot as coroutine."""
    bot_container = make_async_container(
        BotProvider(),
    )

    dp = await bot_container.get(Dispatcher)
    bot = await bot_container.get(Bot)

    setup_dishka(router=dp, container=bot_container, auto_inject=True)

    await dp.feed_update(bot, update=manual_update)  # KeyError: 'dishka_container'

It doesn't work because aiogram uses special kwargs on the feed_update() method and ignores dishka injection previously, as it is useful only for testing and some add-on libraries.

@Tishka17
Copy link
Collaborator

Tishka17 commented Jul 26, 2024

Can we see a stacktrace? I do not see any problems from my side, feed_update is called by aiogram itself: https://github.com/aiogram/aiogram/blob/a3d6c1615ee07eb50577b6a1b6323a8657fd9f20/aiogram/dispatcher/dispatcher.py#L309

@MassonNN
Copy link
Author

MassonNN commented Jul 26, 2024

Can we see a stacktrace? I do not see any problems from my side, feed_update is called by aiogram itself: https://github.com/aiogram/aiogram/blob/a3d6c1615ee07eb50577b6a1b6323a8657fd9f20/aiogram/dispatcher/dispatcher.py#L309

I researched this and found that it originated from the Dispatcher side and because middleware uses @Inject on the call method. So this isn't a problem with feed_update method. Here is another mre that explains this problem:

import asyncio
import datetime
from unittest.mock import AsyncMock

from aiogram import Dispatcher, BaseMiddleware
from aiogram.types import Update, Message, Chat
from dishka import make_async_container, FromDishka, Provider, Scope
from dishka.integrations.aiogram import setup_dishka, inject


class Some:
    pass


class TestMiddleware(BaseMiddleware):
    @inject
    async def __call__(
            self,
            handler,
            event,
            data,
            md: FromDishka[Some]
    ):
        print(md)
        return await handler(event, data)


async def start_bot():
    provider = Provider()
    provider.provide(source=lambda: Some(), provides=Some, scope=Scope.APP)
    bot_container = make_async_container(provider)

    dp = Dispatcher()
    dp.update.outer_middleware(TestMiddleware())
    bot = AsyncMock()

    setup_dishka(router=dp, container=bot_container, auto_inject=True)

    await dp.feed_update(
        bot,
        update=Update(
            update_id=0,
            message=Message(message_id=0, date=datetime.datetime.now(), chat=Chat(id=0, type="private"))
        )
    )


if __name__ == '__main__':
    asyncio.run(start_bot())

Stacktrace:

Traceback (most recent call last):
  File "/mre.py", line 49, in <module>
    asyncio.run(start_bot())
  File "/.pyenv/versions/3.10.2/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/.pyenv/versions/3.10.2/lib/python3.10/asyncio/base_events.py", line 641, in run_until_complete
    return future.result()
  File "/mre.py", line 39, in start_bot
    await dp.feed_update(
  File "/.venv/lib/python3.10/site-packages/aiogram/dispatcher/dispatcher.py", line 158, in feed_update
    response = await self.update.wrap_outer_middleware(
  File "/.venv/lib/python3.10/site-packages/aiogram/dispatcher/middlewares/error.py", line 25, in __call__
    return await handler(event, data)
  File "/.venv/lib/python3.10/site-packages/aiogram/dispatcher/middlewares/user_context.py", line 49, in __call__
    return await handler(event, data)
  File "/.venv/lib/python3.10/site-packages/aiogram/fsm/middleware.py", line 43, in __call__
    return await handler(event, data)
  File "/.venv/lib/python3.10/site-packages/dishka/integrations/base.py", line 147, in autoinjected_func
    container = container_getter(args, kwargs)
  File "/.venv/lib/python3.10/site-packages/dishka/integrations/aiogram.py", line 31, in <lambda>
    container_getter=lambda _, p: p[CONTAINER_NAME],
KeyError: 'dishka_container'

@Tishka17
Copy link
Collaborator

Thanks,

Middlewares in aiogram have different logic comparing to handlers so you cannot reuse @inject there. Instead, you can get dishka_container manually from data dictionary and use its .get methods.

I'll think about how can we add more automatic here, but it is more like enhancement than a bug

@Tishka17 Tishka17 changed the title Aiogram integration doesn't work with dp.feed_update() Aiogram integration inject doesn't work with middlewares Jul 26, 2024
@Tishka17 Tishka17 added enhancement New feature or request integrations labels Jul 26, 2024
@IvanKirpichnikov
Copy link
Contributor

def aiogram_middleware_inject(func):
    return wrap_injection(
        func=func,
        is_async=True,
        container_getter=lambda args, kwargs: args[3][CONTAINER_KEY],
    )

here is the finished inject decorator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request integrations
Projects
None yet
Development

No branches or pull requests

3 participants