I want to implement my own Dependency Injection like Fastapi Depends() do actually without using external package or framework. What will be the approach? Code example will be helpful for me. Thanks in advance.
from typing import Callable, Optional, Any
class Depends:
def __init__(self, dependencies= Optional[Callable[..., Any]]):
self.dependencies = dependencies
def get_db():
pass
def get_token():
pass
def get_current_user(db= Depends(get_db), token= Depends(get_token)):
pass
A starting point could be something like this, where we create a decorator that allows us to preempt any calls to the function and resolve any dependencies.
from typing import Dict, Callable, Any
from functools import wraps
import inspect
# Our decorator which inspects the function and resolves any
# dependencies when called
def resolve_dependencies(func):
# based on https://stackoverflow.com/a/69170441/
f_sig = inspect.signature(func)
@wraps(func)
def resolve_nice_to_have(*args, **kwargs):
bound = f_sig.bind(*args, **kwargs)
bound.apply_defaults()
for k, arg in bound.arguments.items():
if type(arg) == ItWouldBeNice:
bound.arguments[k] = arg()
return func(*bound.args, **bound.kwargs)
return resolve_nice_to_have
# Our actual dependency wrapper, with a simple cache to avoid
# invocating an already resolved dependency.
# Slightly friendlier named than actually depending on something.
class ItWouldBeNice:
cache: Dict[Callable, Any] = {}
def __init__(self, dependency: Callable):
self.dependency = dependency
def __call__(self) -> Any:
if self.dependency in ItWouldBeNice.cache:
return ItWouldBeNice.cache[self.dependency]
result = self.dependency()
ItWouldBeNice.cache[self.dependency] = result
return result
Example of usage:
from iwant import ItWouldBeNice, resolve_dependencies
def late_eval():
print("late was called")
return "static string"
@resolve_dependencies
def i_want_it(s: str = ItWouldBeNice(late_eval)):
print(s)
@resolve_dependencies
def i_want_it_again(s: str = ItWouldBeNice(late_eval)):
print(s)
i_want_it()
i_want_it_again()
This doesn't support hierarchical dependencies etc., but should at least illustrate a concept you could apply to do something similar.