In FastAPI, I’m trying to understand why the background instance not getting created outside the route function handler and its different behaviors.
Examples:
Standard doc example working as expected:
@app.get('/')
async def index(background_tasks: BackgroundTasks):
background_tasks.add_task(some_function_reference)
#Executes non-blocking in the same asyncio loop without any issues
return "Hello"
It behaves differently when adding the background_tasks outside of the route function:
async def some_logic(background_tasks: BackgroundTasks):
#Throws a "required positional argument missing" error
background_tasks.add_task(some_function_reference)
@app.get('/')
async def index():
await some_logic()
#Executes non-blocking in the same asyncio loop
return "Hello"
meanwhile, if we try to init the BackgroundTasks in the some_logic function, the task does not run as following:
async def some_logic():
#Does not Run
background_tasks = BackgroundTasks()
background_tasks.add_task(some_function_reference)
@app.get('/')
async def index(background_tasks: BackgroundTasks):
await some_logic()
#Executes non-blocking in the same asyncio loop
return "Hello"
Why would these three cases be different? Why do i need to pass the background tasks from the route function to the following called function?
>Solution :
The BackgroundTasks instance gets populated by the FastAPI framework and injected into the callers. In the first example, you use a route which has it’s dependencies injected by FastAPI.
The second example, you are calling some_logic as a normal function. Because it’s not being called by FastAPI directly, no dependency injection is happening.
In the third example, you create a new instance of background tasks, but this is a different instance than the one that FastAPI cares about. You create tasks in it, but there is no handler registered to act on them.
In this case, if the goal is to make the background tasks easier to apply to methods, you could add the some_logic function as a dependency of the route. A dependency is called by FastAPI, and therefore the arguments will be injected.
Example:
from fastapi import FastAPI, Depends
from starlette.background import BackgroundTasks
from starlette.testclient import TestClient
app = FastAPI()
def task():
print("Hello from the Background!")
async def some_logic(background_tasks: BackgroundTasks):
background_tasks.add_task(task)
@app.get('/', dependencies=[Depends(some_logic)])
async def index():
return "Hello"
with TestClient(app) as client:
client.get("/")
Hello from the Background!