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.