Reputation: 31
In FastAPI path operations you can use FastAPIs Dependency injection. I have the requirement to dynamically register dependencies at startup and I have to be able to define a set of dependencies per path operation and collect their return values.
My problem now is that those dependencies can depend on sub-dependencies. My question is if and how I can realize this situation with FastAPI.
My current approach is this (can be copied, should run as is, if fastapi and uvicorn are installed):
from random import randint
from typing import List
import uvicorn
from fastapi import FastAPI, Depends
app = FastAPI()
def secondary_dep():
return randint(0, 100)
class DepCaller:
def __init__(self, *dependencies):
self._dependencies = dependencies
def __call__(self) -> List:
return [dep() for dep in self._dependencies]
def primary_dep1(random_int: int = Depends(secondary_dep)):
return random_int
def primary_dep2(random_int: int = Depends(secondary_dep)):
return random_int
def dep_generator(primary_deps: List[str]) -> DepCaller:
# This dict is a simplified version of "dynamically registered" dependencies
defined_deps = {
"A": primary_dep1,
"B": primary_dep2
}
# Simplified; Error handling is missing for the sake of the example
selected_deps = [defined_deps[dep] for dep in primary_deps]
# <---!! Here is my problem. The dependencies do not resolve from this point on
return DepCaller(*selected_deps)
@app.get("/")
def root(generated_dep=Depends(dep_generator(["A", "B"]))):
return generated_dep
if __name__ == "__main__":
uvicorn.run(app)
If I now query the resource: curl -X 'GET' 'http://127.0.0.1:8000/' -H 'accept: application/json'
the result is [{"dependency":{},"use_cache":true},{"dependency":{},"use_cache":true}]
My desired result would be something like: [3, 65]
Some motivation to avoid me asking for a solution for B when I actually want to solve C (A->B->C):
I have a FastAPI app in which new resources can be added via plugins (python entrypoints). My dependables (primary_dep1, primary_dep2) are authentication strategies on which those new resources should be able to depend. Those strategies are also added via plugins. A path operation should now be able to depend on a set of strategies of which only one has to resolve to a successful authentication. Thus the list of dependencies to use instead of defining them explicit as parameters.
I hope I could make my problem clear and appreciate any answers, Thanks.
Upvotes: 1
Views: 1415
Reputation: 31
I could find a solution to my problem in the meantime:
def merge_dependencies(*dependencies: Callable) -> Callable:
"""
This function wraps the given callables (dependencies) and
wraps
them in FastAPIs Depends. It returns
a function containing these dependencies in its signature.
:param dependencies: The dependencies to wrap
:return: A callable which returns a list of the results of
the dependencies
"""
def merged_dependencies(**kwargs):
return list(kwargs.values())
merged_dependencies.__signature__ = inspect.Signature( # type: ignore
parameters=[
inspect.Parameter(f"dep{key}",
inspect.Parameter.KEYWORD_ONLY, default=Depends(dep))
for key, dep in enumerate(dependencies)
]
)
return merged_dependencies
Upvotes: 2