How can I implement Fastapi like Depends() without using any package or using raw python code?

Question:

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
Asked By: Mahbub Rahman

||

Answers:

You can do something like this?

async def get_db(db_con=Depends(get_db_con)) -> AsyncIterable[Session]:
    session = Session(bind=db_con)
    try:
        yield session
    finally:
        session.close()

The get_db_con function could return the initialised database (or raise a connection error).

Hope this helps

cheers.

Answered By: MinatoNamikaze91

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.

Answered By: MatsLindh