Description
I was trying out the async resource provider to verify that it is actually closing the resource.
However, I noticed that it is not closing the resource.
After deep investigations I found out that the @Inject decorator on the API
makes python think that the function is not an async generator.
FastAPI decides whether the dependency is a generator on this line https://github.com/tiangolo/fastapi/blob/master/fastapi/dependencies/utils.py#L522
Which then tries to inspect the code using the builtin inspect function
def is_gen_callable(call: Callable[..., Any]) -> bool:
if inspect.isgeneratorfunction(call):
return True
call = getattr(call, "__call__", None)
return inspect.isgeneratorfunction(call)
def is_async_gen_callable(call: Callable[..., Any]) -> bool:
if inspect.isasyncgenfunction(call):
return True
call = getattr(call, "__call__", None)
return inspect.isasyncgenfunction(call)
Which is returning false for async generators decorated with @Inject
I have attached the test code.
I'll try investigating the @Inject decorator
To see whether it could be fixed
import contextlib
import os
from functools import partial
from dependency_injector import containers, providers, resources
from dependency_injector.wiring import Provide, inject, Closing
from fastapi import FastAPI, Depends
from pydantic import BaseSettings, Field
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
os.environ["sql_alchemy_str"] = "sqlite+aiosqlite:///database.db"
class DatabaseSettings(BaseSettings):
sql_alchemy_str: str = Field(env="sql_alchemy_str")
async def make(engine=None):
print(f"Make {engine}")
session = sessionmaker(
engine, expire_on_commit=False, class_=AsyncSession
)
yield session()
with open("kill.txt", "w") as f:
f.write("killed")
print("kill")
class DatabaseContainer(containers.DeclarativeContainer):
config = providers.Configuration[DatabaseSettings]("DatabaseConfig")
alchemy_engine: AsyncEngine = providers.Singleton(create_async_engine, config.sql_alchemy_str, echo=True)
async_session = providers.Resource(make, alchemy_engine)
app = FastAPI()
@app.get("/Hello")
@inject
async def say_hi(session: AsyncSession = Depends(Closing[Provide[DatabaseContainer.async_session]], use_cache=False)):
print(f"Got {session}")
container = DatabaseContainer()
container.config.from_pydantic(DatabaseSettings())
container.wire(modules=[__name__])
@app.on_event("startup")
async def startup_event():
print(container.alchemy_engine())
Metadata
Metadata
Assignees
Labels
No labels