Description
Hi.
Docs inlcude the FastAPI
example, python-dependency-injector
itself works with FastAPI
, but I found a case when it doesn't. Let's consider a simple FastAPI application with two dependencies:
from fastapi import Depends, FastAPI
async def service_process():
return "foo"
async def dep1():
await service_process()
print("dep1 - A")
yield
print("dep1 - B")
async def dep2():
print("dep2 - A")
yield
print("dep2 - B")
app = FastAPI(dependencies=[Depends(dep1), Depends(dep2)])
@app.api_route("/")
async def index():
return
The result of calling /
would be:
dep1 - A
dep2 - A
dep2 - B
dep1 - B
It's because of how yield
works in FastAPI
dependencies - everything that happens before yield
happens at the beginning of an endpoint, everything after -> happens after a response is returned.
Okay, let's throw python-dependency-injector
into the mix. I took this example from examples/miniapps/fastapi-simple/fastapi_di_example.py
and added dependencies - one with @inject
. The example itself contains meaningless prints, but in the real app that I'm working on, the app configuration is injected in the dependencies.
from fastapi import FastAPI, Depends
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject
class Service:
async def process(self) -> str:
return "OK"
class Container(containers.DeclarativeContainer):
service = providers.Factory(Service)
@inject
async def dep1(service: Service = Depends(Provide[Container.service])):
await service.process()
print("dep1 - A")
yield
print("dep1 - B")
async def dep2():
print("dep2 - A")
yield
print("dep2 - B")
app = FastAPI(dependencies=[Depends(dep1), Depends(dep2)])
@app.api_route("/")
async def index():
return
container = Container()
container.wire(modules=[__name__])
The output is:
dep2 - A
dep2 - B
However, if I remove yield
from dep1
:
@inject
async def dep1(service: Service = Depends(Provide[Container.service])):
await service.process()
print("dep1 - A")
print("dep1 - B")
, the output is:
dep1 - A
dep1 - B
dep2 - A
dep2 - B
The different order here is because without yield
, the prints in dep1
happen at the beginning of def index()
, in case of dep2
one happens right after the prints from dep1
, the other after the response is returned.
But this doesn't matter. What I'm curious about is why does it happen? Why is yield
an issue here. It looks like the dependency is not applied at all (no code from dep1
gets executed). I'm not aware of the internals of @inject
, so I'm not really sure what happens here. Before you ask - I got to have this yield
for various reasons - for example I set a logger context at the beginning of the request and reset it and the end (after the response is returned).
So my question would be: is it possible to use @inject
with FastAPI dependencies that with yield
?