Skip to content

Dependency Cache

Often, you will have dependencies that share a sub dependency. For example, you probably only want to load your configuration from environment variables once and then re-use the same object in multiple dependencies. To avoid re-computing the shared dependency, di will cache shared dependencies.

How caching works

Dependencies are cached by their cache key, computed in Dependent.cache_key. See dependents for more information on Dependent.cache_key. Dependencies are cached by default, but this behavior can be changed on a per-dependency basis using the use_cache=False parameter to Dependent.

from random import random

from di import Container
from di.dependent import Dependent, Marker
from di.executors import SyncExecutor
from di.typing import Annotated


def controller(
    # no marker is equivalent to Dependent(object)
    v1: object,
    # the default value is use_cache=True
    v2: Annotated[object, Marker(object, scope="request")],
    # but you can set use_cache=False
    v3: Annotated[float, Marker(random, use_cache=False, scope="request")],
) -> None:
    assert v1 is v2
    assert v1 is not v3 and v2 is not v3


def main() -> None:
    container = Container()
    solved = container.solve(Dependent(controller, scope="request"), scopes=["request"])
    with container.enter_scope("request") as state:
        solved.execute_sync(executor=SyncExecutor(), state=state)

Caching and scopes

Dependencies are cached within their scope and any inner scopes. Once a dependency's scope exits, it's cached value is discarded and the next time the scope is entered a fresh value will be computed.