10000 Confusing AsyncResource closing Mechanism with FastAPI · Issue #719 · ets-labs/python-dependency-injector · GitHub
[go: up one dir, main page]

Skip to content
Confusing AsyncResource closing Mechanism with FastAPI #719
Closed
@rumbarum

Description

@rumbarum

Hello ets-labs, I felt really great on this morning for applying this awesome tool!

I almost had been finishing organizing dependency injection with fastapi during today, but there was a issue when closing AsyncResouces with on_shutdown of FastAPI.

I am on Mac with python 3.11.4, current version of installed D.I. is 4.41.0.

This works unexpectedly, remaining unclosed session.

from dependency_injector import providers, containers, resources, wiring
from aiohttp import ClientSession
import asyncio

from fastapi import FastAPI

class AsyncHttpClient(resources.AsyncResource):
    async def init(self) -> ClientSession:
        return ClientSession()

    async def shutdown(self, client_session: ClientSession | None):
        if client_session is not None:
            await client_session.close()

class Container(containers.DeclarativeContainer):
    wiring_config = containers.WiringConfiguration(
        modules=[__name__]
    )
    async_resource = providers.Resource(AsyncHttpClient)

async def main():
    container = Container()
    await container.init_resources()
    app = FastAPI(
        on_shutdown=[container.shutdown_resources],
    )

if __name__ == "__main__":
    asyncio.run(main())

 >>>
Unclosed client session
client_session: <aiohttp.client.ClientSession object at xxxxxxx>

Below is working clean, remaining no unclosed session.

from dependency_injector import providers, containers, resources, wiring
from aiohttp import ClientSession
import asyncio

from fastapi import FastAPI

class AsyncHttpClient(resources.AsyncResource):
    async def init(self) -> ClientSession:
        return ClientSession()

    async def shutdown(self, client_session: ClientSession | None):
        if client_session is not None:
            await client_session.close()

class Container(containers.DeclarativeContainer):
    wiring_config = containers.WiringConfiguration(
        modules=[__name__]
    )
    async_resource = providers.Resource(AsyncHttpClient)

@wiring.inject
async def inner(client=wiring.Provide[Container.async_resource]):
    resp = await client.get("https://ipconfig.io/json")
    print(await resp.json())

async def main():
    container = Container()
    await container.init_resources()
    app = FastAPI(
        on_shutdown=[container.shutdown_resources],
    )

if __name__ == "__main__":
    asyncio.run(main())

The only difference is existence of injected function. If injected function is, it succeed to closing and vice versa.(Even, I am not using it)
I had tried making closing first for hours, so I could not find solution. Luckily, I tested another way and found the way.

Would you tell me any mechanism about this ?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0