python-dependency-injector icon indicating copy to clipboard operation
python-dependency-injector copied to clipboard

The library can't handle async generators

Open alexted opened this issue 2 years ago • 4 comments

Based on the example FastAPI + SQLAlchemy example from the documentation i made a myself implementation for work with connections of Async SQLA Core. But I came across the fact that your library does not support working with asynchronous generators. Could you please fix it somehow? Here, here's my code:

from sqlalchemy.ext.asyncio import create_async_engine, AsyncEngine

from src.app.config import AppConfig


class Database:
    def __init__(self, config: AppConfig) -> None:
        self._engine: AsyncEngine = create_async_engine(config.DB_DSN)

    async def get_connection(self)  -> AsyncConnection:
        async with self._engine.begin() as connection:
            yield connection

class Container(containers.DeclarativeContainer):
    wiring_config = containers.WiringConfiguration(packages=["src.api"])

    config = providers.Configuration(pydantic_settings=[AppConfig(_env_file=".env")])
    app_cfg = config.get_pydantic_settings()[0]

    logging = providers.Resource(dictConfig, get_logging_config(app_cfg))

    db = providers.Resource(Database, config=app_cfg)

    language_repository = providers.Factory(
        repositories.LanguageRepository,
        connection=db.provided.get_connection.call(),
    )
    ...

I expect to get AsyncConnection, when initializing language_repository, but in fact I get <async_generator object Database.get_connection at 0x7ff63b913c40> for some reason. It seems that this should not be the case. I have to additionally do await anext(self._connection) in my repositories to get a live sqla connection. I also tried doing connection=db.provided.provided.get_connection.call().provided.__anext__.call(), but for some reason I get an inactive sqla connection which is impossible to work with.

alexted avatar Sep 14 '23 22:09 alexted

I hit this as well, and I think that this is supposed to be a feature. You can avoid it by wrapping with a class or another object that is not a coroutine

colonelpanic8 avatar Dec 18 '23 16:12 colonelpanic8

I hit this as well, and I think that this is supposed to be a feature. You can avoid it by wrapping with a class or another object that is not a coroutine

I'm not sure I understand you. Could you use my example to show how it should look like in the code?

alexted avatar Dec 20 '23 11:12 alexted

I hit this as well, and I think that this is supposed to be a feature. You can avoid it by wrapping with a class or another object that is not a coroutine

Why do you think this it's not a bug? It's clearly a bug!

alexted avatar Jul 09 '24 07:07 alexted

Why do you think this it's not a bug? It's clearly a bug!

it's not. If you read the code you will see that they clearly do this intentionally.

I think the reason is probably something like:

Well if you are exectuting the fetching of a dependency, its not at all clear on which event loop the asynchronous call is supposed to execute. Often, you would want it to execute on the current event loop, but that may not always be the case.

colonelpanic8 avatar Jul 09 '24 17:07 colonelpanic8