### Install Dependencies with Just Source: https://github.com/modern-python/that-depends/blob/main/docs/dev/contributing.md Install project dependencies using the 'just install' command. This ensures your development environment is set up correctly. ```bash just install ``` -------------------------------- ### Setup for Context Resources Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/context-resources.md Defines asynchronous and synchronous resources using `providers.ContextResource` within a `BaseContainer`. This setup is required before resolving these resources. ```python import typing from that_depends import BaseContainer, providers, inject, Provide async def my_async_resource() -> typing.AsyncIterator[str]: print("Initializing async resource") try: yield "async resource" finally: print("Teardown of async resource") def my_sync_resource() -> typing.Iterator[str]: print("Initializing sync resource") try: yield "sync resource" finally: print("Teardown of sync resource") class MyContainer(BaseContainer): async_resource = providers.ContextResource(my_async_resource) sync_resource = providers.ContextResource(my_sync_resource) ``` -------------------------------- ### Install that-depends Source: https://context7.com/modern-python/that-depends/llms.txt Install the library using pip. Optional extras for web framework integration can be included. ```bash pip install that-depends # Optional extras for web framework integration: pip install that-depends[fastapi] ``` -------------------------------- ### List Provider Example Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/collections.md Demonstrates how to use the List provider to create a sequence of dependencies. The List provider resolves into an immutable sequence. ```python import random from that_depends import BaseContainer, providers class DIContainer(BaseContainer): random_number = providers.Factory(random.random) numbers_sequence = providers.List(random_number, random_number) DIContainer.numbers_sequence.resolve_sync() # (0.3035656170071561, 0.8280498192037787) ``` -------------------------------- ### Clone Project Repository Source: https://github.com/modern-python/that-depends/blob/main/docs/dev/contributing.md Clone the project repository and navigate into the project directory. Ensure you have uv and just installed. ```bash git@github.com:modern-python/that-depends.git cd that-depends ``` -------------------------------- ### FastStream Example with Lifespan Events and Dependencies Source: https://github.com/modern-python/that-depends/blob/main/docs/integrations/faststream.md This example demonstrates FastStream with `that-depends` including lifespan management and dependency injection for asynchronous resources. It publishes a message after startup to trigger the handler. ```python import datetime import contextlib import typing from faststream import FastStream, Depends, Logger from faststream.rabbit import RabbitBroker from tests import container @contextlib.asynccontextmanager def lifespan_manager() -> typing.AsyncIterator[None]: try: yield finally: await container.DIContainer.tear_down() broker = RabbitBroker() app = FastStream(broker, lifespan=lifespan_manager) @broker.subscriber("in") def read_root( logger: Logger, some_dependency: typing.Annotated[ container.DependentFactory, Depends(container.DIContainer.dependent_factory) ], ) -> datetime.datetime: startup_time = some_dependency.async_resource logger.info(startup_time) return startup_time @app.after_startup async def t() -> None: await broker.publish(None, "in") ``` -------------------------------- ### providers.Singleton Example Source: https://context7.com/modern-python/that-depends/llms.txt Illustrates creating a cached singleton instance using a sync factory. The instance is reused until tear_down is called. ```python from that_depends import BaseContainer, providers class Settings: def __init__(self) -> None: self.debug = False self.db_url = "sqlite:///app.db" class Container(BaseContainer): settings = providers.Singleton(Settings) # First call creates the instance; subsequent calls return the cached value. s1 = Container.settings.resolve_sync() s2 = Container.settings.resolve_sync() assert s1 is s2 # same object # Tear down clears the cache; next resolution recreates. await Container.settings.tear_down() s3 = await Container.settings.resolve() assert s3 is not s1 ``` -------------------------------- ### Install FastStream Integration for that-depends Source: https://github.com/modern-python/that-depends/blob/main/docs/integrations/faststream.md Install the `that-depends` library with the `faststream` extra to enable integration with FastStream. ```shell pip install that-depends[faststream] ``` -------------------------------- ### Singleton with Pydantic Settings Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/singleton.md Shows an example of using a Singleton provider with pydantic-settings to ensure configuration is parsed and cached only once. ```python from pydantic_settings import BaseSettings from pydantic import BaseModel class DatabaseConfig(BaseModel): address: str = "127.0.0.1" port: int = 5432 db_name: str = "postgres" class Settings(BaseSettings): auth_key: str = "my_auth_key" db: DatabaseConfig = DatabaseConfig() ``` -------------------------------- ### providers.AsyncSingleton Example Source: https://context7.com/modern-python/that-depends/llms.txt Shows how to create a cached singleton instance using an async factory. Resolution requires `await` and the instance is cached after the first successful resolution. ```python import asyncio from that_depends import BaseContainer, providers async def fetch_remote_config() -> dict: await asyncio.sleep(0) # simulate async I/O return {"feature_flag": True} class Container(BaseContainer): remote_config = providers.AsyncSingleton(fetch_remote_config) async def main(): cfg1 = await Container.remote_config.resolve() cfg2 = await Container.remote_config.resolve() assert cfg1 is cfg2 # cached after first await await Container.remote_config.tear_down() ``` -------------------------------- ### Setup Dependency Injection Container with Providers Source: https://github.com/modern-python/that-depends/blob/main/docs/index.md Set up a dependency injection container using BaseContainer and providers.Resource to manage the lifecycle of the async resource. ```python from that_depends import BaseContainer, providers class Container(BaseContainer): provider = providers.Resource(create_async_resource) ``` -------------------------------- ### BaseContainer IoC Container Example Source: https://context7.com/modern-python/that-depends/llms.txt Demonstrates setting up a BaseContainer with various providers like Singleton, Resource, and Factory, and using them with @inject and Provide. ```python import random import typing from that_depends import BaseContainer, providers, inject, Provide # --- resource creators --- def create_config() -> dict: return {"env": "production", "db_url": "postgresql://localhost/mydb"} def create_db_session(db_url: str) -> typing.Iterator[object]: print(f"Opening session to {db_url}") try: yield object() # replace with real session finally: print("Closing session") class UserRepository: def __init__(self, session: object, config: dict) -> None: self.session = session self.config = config # --- container --- class AppContainer(BaseContainer): config = providers.Singleton(create_config) db_url = config.db_url # AttrGetter: resolved from config db_session = providers.Resource(create_db_session, db_url.cast) user_repo = providers.Factory(UserRepository, session=db_session.cast, config=config.cast) # --- usage --- async def main(): await AppContainer.init_resources() # pre-warm Singletons and Resources @inject async def handle(repo=Provide[AppContainer.user_repo]): print(repo.session, repo.config) await handle() await AppContainer.tear_down() # finalize all resources ``` -------------------------------- ### providers.Factory Example Source: https://context7.com/modern-python/that-depends/llms.txt Demonstrates the Factory provider, which calls a sync factory on every resolution, ensuring a new instance each time. It can accept other providers as arguments. ```python import uuid from that_depends import BaseContainer, providers, inject, Provide def make_request_id(prefix: str) -> str: return f"{prefix}-{uuid.uuid4()}" class Container(BaseContainer): prefix = providers.Object("req") request_id = providers.Factory(make_request_id, prefix.cast) @inject def process(rid: str = Provide[Container.request_id]) -> str: return rid id1 = process() id2 = process() assert id1 != id2 # new UUID each call print(id1) # e.g. "req-3f2504e0-..." ``` -------------------------------- ### AsyncFactory Provider Example Source: https://context7.com/modern-python/that-depends/llms.txt Use AsyncFactory for creators that are async or return awaitables. Resolution requires `await`. ```python import httpx from that_depends import BaseContainer, providers, inject, Provide async def fetch_user(user_id: int) -> dict: # simulate async API call return {"id": user_id, "name": "Alice"} class Container(BaseContainer): user_id = providers.Object(42) user = providers.AsyncFactory(fetch_user, user_id.cast) async def main(): @inject async def handler(user: dict = Provide[Container.user]) -> dict: return user result = await handler() print(result) # {"id": 42, "name": "Alice"} ``` -------------------------------- ### Install that-depends with FastAPI extra Source: https://github.com/modern-python/that-depends/blob/main/docs/integrations/fastapi.md Install the package with the `fastapi` extra using pip. ```bash pip install that-depends[fastapi] ``` -------------------------------- ### List and Dict Providers Example Source: https://context7.com/modern-python/that-depends/llms.txt List and Dict providers aggregate multiple providers into sequences or mappings, resolving them in a single call. ```python from that_depends import BaseContainer, providers, inject, Provide class Container(BaseContainer): a = providers.Factory(lambda: 1) b = providers.Factory(lambda: 2) c = providers.Factory(lambda: 3) numbers = providers.List(a, b, c) named = providers.Dict(x=a, y=b, z=c) # Sync resolution print(tuple(Container.numbers.resolve_sync())) # (1, 2, 3) print(dict(Container.named.resolve_sync())) # {"x": 1, "y": 2, "z": 3} # Async resolution import asyncio print(asyncio.run(Container.numbers.resolve())) # (1, 2, 3) ``` -------------------------------- ### Dict Provider Example Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/collections.md Illustrates the usage of the Dict provider for creating a collection of named dependencies. The Dict provider resolves into a read-only mapping. ```python import random from that_depends import BaseContainer, providers class DIContainer(BaseContainer): random_number = providers.Factory(random.random) numbers_map = providers.Dict(key1=random_number, key2=random_number) DIContainer.numbers_map.resolve_sync() # mappingproxy({'key1': 0.6851384528299208, 'key2': 0.41044920948045294}) ``` -------------------------------- ### Object Provider Example Source: https://context7.com/modern-python/that-depends/llms.txt Object providers wrap existing objects, returning them on every resolution. Ideal for configuration values and constants. ```python from that_depends import BaseContainer, providers, inject, Provide class Container(BaseContainer): base_url = providers.Object("https://api.example.com") timeout = providers.Object(30) @inject def call_api(url: str = Provide[Container.base_url], timeout: int = Provide[Container.timeout]) -> str: return f"GET {url} (timeout={timeout}s)" print(call_api()) # GET https://api.example.com (timeout=30s) ``` -------------------------------- ### Install that-depends with uv Source: https://github.com/modern-python/that-depends/blob/main/docs/index.md Use uv to add the that-depends package to your project. ```bash uv add that-depends ``` -------------------------------- ### Selector Provider Example Source: https://context7.com/modern-python/that-depends/llms.txt Selector providers dynamically choose from multiple named providers at runtime using a callable or string selector. ```python import os from that_depends import BaseContainer, providers, inject, Provide class LocalStorage: def save(self, data: str) -> str: return f"local:{data}" class S3Storage: def save(self, data: str) -> str: return f"s3:{data}" def pick_storage() -> str: return os.getenv("STORAGE_BACKEND", "local") class Container(BaseContainer): storage = providers.Selector( pick_storage, local=providers.Factory(LocalStorage), s3=providers.Factory(S3Storage), ) @inject def upload(payload: str, store=Provide[Container.storage]) -> str: return store.save(payload) print(upload("hello")) # "local:hello" (when STORAGE_BACKEND is unset) ``` -------------------------------- ### Resource Provider Example Source: https://context7.com/modern-python/that-depends/llms.txt Resource providers resolve once, cache results, and execute teardown logic. They accept sync/async generators and context managers. ```python import typing from that_depends import BaseContainer, providers async def db_connection() -> typing.AsyncIterator[dict]: conn = {"connected": True} print("DB connected") try: yield conn finally: conn["connected"] = False print("DB disconnected") class Container(BaseContainer): db = providers.Resource(db_connection) async def main(): conn = await Container.db.resolve() print(conn) # {"connected": True} same = await Container.db.resolve() assert conn is same # cached await Container.db.tear_down() print(conn) # {"connected": False} — teardown ran ``` -------------------------------- ### Install that-depends Package Source: https://github.com/modern-python/that-depends/blob/main/README.md Use pip to install the 'that-depends' package. This is the primary method for adding the library to your project. ```bash pip install that-depends ``` -------------------------------- ### Tear Down Propagation Example Source: https://github.com/modern-python/that-depends/blob/main/docs/introduction/tear-down.md Demonstrates how tear-down propagates to dependent providers. Calling `tear_down()` on a dependency also tears down the dependent provider. ```python class MyContainer(BaseContainer): B = providers.Singleton(lambda: random.random()) A = providers.Singleton(lambda x: x, B) b = await MyContainer.B() a = await MyContainer.A() assert a == b await MyContainer.B.tear_down() a_new = await MyContainer.A() assert a_new != a ``` -------------------------------- ### ContextResource Provider Example Source: https://context7.com/modern-python/that-depends/llms.txt ContextResource is request/scope-scoped and requires explicit context initialization. It uses ContextVar for concurrent context isolation. ```python import typing from that_depends import BaseContainer, providers, container_context, inject, Provide async def session_factory() -> typing.AsyncIterator[str]: print("session open") try: yield "db-session-abc" finally: print("session closed") class Container(BaseContainer): session = providers.ContextResource(session_factory) @inject async def endpoint(sess: str = Provide[Container.session]) -> str: return sess async def main(): # Wrap with context_async so the resource is properly initialized and torn down. async with container_context(Container): result = await endpoint() print(result) # "db-session-abc" # "session closed" printed after block exits ``` -------------------------------- ### Async Context Manager for Container Initialization Source: https://context7.com/modern-python/that-depends/llms.txt The container_context manager initializes ContextResource providers and sets global context items. This example shows its async usage, demonstrating how to fetch both a context resource and a global item within the managed context. ```python import typing from that_depends import BaseContainer, providers, container_context, fetch_context_item async def session() -> typing.AsyncIterator[str]: yield "session-xyz" class Container(BaseContainer): db = providers.ContextResource(session) # As async context manager: async def as_context_manager(): async with container_context(Container, global_context={"request_id": "abc-123"}): val = await Container.db.resolve() rid = fetch_context_item("request_id") print(val, rid) # "session-xyz" "abc-123" ``` -------------------------------- ### Resolve Factory with Parameters Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/factories.md Call the `.provider_sync` callable of a factory that has parameters to get the resolved value. Dependencies are resolved automatically. ```python >>> greeting_sync_fn = MyContainer.greeting.provider_sync >>> print(greeting_sync_fn()) Hello, Alice! ``` -------------------------------- ### FastAPI App with Request-Scoped Dependencies Source: https://github.com/modern-python/that-depends/blob/main/docs/integrations/fastapi.md Define a FastAPI app and use `Depends(init_di_context)` to set up request-scoped dependency injection. This example shows how to fetch the request object itself from the context. ```python from fastapi import FastAPI, Request, Depends from starlette.responses import JSONResponse from request_deps import init_di_context from mycontainer import MyContainer app = FastAPI() # Notice no DIContextMiddleware here, but you could combine them @app.get("/request-based", dependencies=[Depends(init_di_context)]) async def get_request_info( # This provider fetches the request from context: request_in_container: Request = Depends(MyContainer.resolver(lambda: fetch_context_item("request"))), ): # The provider can read from that-depends context. We used .resolver(...) here as an example, # but you can define a dedicated provider in MyContainer that returns fetch_context_item("request"). return JSONResponse({"request_url": str(request_in_container.url)}) ``` -------------------------------- ### ThreadLocalSingleton Provider Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/singleton.md Demonstrates the ThreadLocalSingleton provider, which caches an instance per thread. Each thread gets its own unique instance, which is then reused within that specific thread. ```python import random import threading from that_depends.providers import ThreadLocalSingleton def factory() -> int: """Return a random int between 1 and 100.""" return random.randint(1, 100) # ThreadLocalSingleton caches an instance per thread singleton = ThreadLocalSingleton(factory) # In a single thread: instance1 = singleton.resolve_sync() # e.g. 56 instance2 = singleton.resolve_sync() # 56 (cached in the same thread) # In multiple threads: def thread_task(): return singleton.resolve_sync() thread1 = threading.Thread(target=thread_task) thread2 = threading.Thread(target=thread_task) thread1.start() thread2.start() # thread1 and thread2 each get a different cached value ``` -------------------------------- ### Concurrency Safety Examples Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/singleton.md Illustrates the thread-safe and async-safe nature of the Singleton provider. In concurrent scenarios, the factory function is guaranteed to be called only once, and all callers receive the same cached instance. ```python import threading import asyncio # In async code: async def main(): # calling resolve concurrently in different coroutines results = await asyncio.gather( MyContainer.singleton.resolve(), MyContainer.singleton.resolve(), ) # Both results point to the same instance # In threaded code: def thread_task(): instance = MyContainer.singleton.resolve_sync() ... threads = [threading.Thread(target=thread_task) for _ in range(5)] for t in threads: t.start() ``` -------------------------------- ### Build and Host Documentation Locally Source: https://github.com/modern-python/that-depends/blob/main/docs/dev/contributing.md Build and host the project documentation locally by running the 'just docs' command. This allows you to preview documentation changes. ```bash just docs ``` -------------------------------- ### Initialize State Provider (Sync) Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/state.md Initialize the State provider with a value using a sync context manager. Resolve the value using `resolve_sync()`. ```python with Container.my_state.init(42): print(Container.my_state.resolve_sync()) # 42 ``` -------------------------------- ### Initialize State Provider (Async) Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/state.md Initialize the State provider with a value using an async context manager. Resolve the value using `resolve()`. ```python with Container.my_state.init(42): print(await Container.my_state.resolve()) # 42 ``` -------------------------------- ### Initializing Global Context with Context Manager Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/context-resources.md Shows how to initialize a global context with key-value pairs using the `container_context` context manager. Items can be retrieved using `fetch_context_item`. ```python from that_depends import container_context, fetch_context_item async with container_context(global_context={"key": "value"}): # run some code fetch_context_item("key") # returns 'value' ``` -------------------------------- ### Define an asynchronous resource creator Source: https://github.com/modern-python/that-depends/blob/main/docs/index.md Define an async function that yields a resource. This is useful for managing resources with setup and teardown logic. ```python async def create_async_resource(): logger.debug("Async resource initiated") try: yield "async resource" finally: logger.debug("Async resource destructed") ``` -------------------------------- ### Using @inject with a specific scope Source: https://github.com/modern-python/that-depends/blob/main/docs/introduction/scopes.md Decorate a function with `@inject` and specify a scope using the `scope` argument. This example uses `ContextScopes.APP`. ```python from that_depends.providers import inject, ContextScopes @inject(scope=ContextScopes.APP) def foo(...): ... ``` -------------------------------- ### Replace init_async_resources with init_resources Source: https://github.com/modern-python/that-depends/blob/main/docs/migration/v2.md The `BaseContainer.init_async_resources()` method has been removed. Use `BaseContainer.init_resources()` instead for initializing container resources. ```python from that_depends import BaseContainer class MyContainer(BaseContainer): # Define your providers here ... ``` ```python await MyContainer.init_async_resources() ``` ```python await MyContainer.init_resources() ``` -------------------------------- ### Using Object Provider Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/object.md Demonstrates how to use the Object provider to inject a simple integer value. Ensure the provider is correctly defined within a BaseContainer. ```python from that_depends import BaseContainer, providers class DIContainer(BaseContainer): object_provider = providers.Object(1) assert DIContainer.object_provider() == 1 ``` -------------------------------- ### Litestar Integration with Dependency Injection Source: https://github.com/modern-python/that-depends/blob/main/docs/testing/provider-overriding.md Demonstrates how to use dependency injection with Litestar, including overriding dependencies for testing. It shows how to define a service, register it in a DI container, and inject it into a route handler using Annotated and Dependency. ```python import typing from functools import partial from typing import Annotated from unittest.mock import Mock from litestar import Litestar, Router, get from litestar.di import Provide from litestar.params import Dependency from litestar.testing import TestClient from that_depends import BaseContainer, providers class ExampleService: def do_smth(self) -> str: return "something" class DIContainer(BaseContainer): example_service = providers.Factory(ExampleService) @get(path="/another-endpoint", dependencies={"example_service": Provide(DIContainer.example_service)}) async def endpoint_handler( example_service: Annotated[ExampleService, Dependency(skip_validation=True)], ) -> dict[str, typing.Any]: return {"object": example_service.do_smth()} ``` -------------------------------- ### Define Inner and Outer Containers Source: https://github.com/modern-python/that-depends/blob/main/docs/introduction/multiple-containers.md Define an InnerContainer with resources and an OuterContainer that uses these resources in a list. This setup allows for dependency injection across containers. ```python from tests import container from that_depends import BaseContainer, providers class InnerContainer(BaseContainer): sync_resource = providers.Resource(container.create_sync_resource) async_resource = providers.Resource(container.create_async_resource) class OuterContainer(BaseContainer): sequence = providers.List(InnerContainer.sync_resource, InnerContainer.async_resource) ``` -------------------------------- ### Define a simple container with a factory provider Source: https://github.com/modern-python/that-depends/blob/main/docs/integrations/fastapi.md Create a `BaseContainer` and define providers, such as a factory that creates a `datetime.datetime` object. ```python # mycontainer.py import datetime from that_depends import BaseContainer, providers def create_time() -> datetime.datetime: """Example sync resource creator.""" return datetime.datetime.now() class MyContainer(BaseContainer): # A simple provider that always returns a "datetime.datetime" object current_time = providers.Factory(create_time) ``` -------------------------------- ### Context Initialization Failure (Mismatched Scope) Source: https://github.com/modern-python/that-depends/blob/main/docs/introduction/scopes.md Entering a scope that does not match the provider's configured scope will result in an `InvalidContextError` if attempting to resolve. ```python async with container_context(p, scope=ContextScopes.REQUEST): # will raise and InvalidContextError since you are entering `REQUEST` scope ... ``` -------------------------------- ### Resolve and Tear Down Synchronous Resource Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/resources.md Explicitly resolve a synchronous resource using `resolve_sync()` to get its instance and `tear_down_sync()` to clean it up. Resolving again after teardown will recreate the resource. ```python # Synchronous resource usage value_sync = MyContainer.sync_resource.resolve_sync() print(value_sync) # "sync resource" MyContainer.sync_resource.tear_down_sync() ``` -------------------------------- ### Fetching Global Context Item by Type Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/context-resources.md Demonstrates retrieving an item from the global context by its type using `fetch_context_item_by_type`. Note that only the first matching item is returned if multiple exist. ```python from that_depends import fetch_context_item_by_type with container_context(global_context={"key": 4}): fetch_context_item_by_type(int) # returns 4 ``` -------------------------------- ### Litestar App with DI and Lifespan Source: https://github.com/modern-python/that-depends/blob/main/docs/integrations/litestar.md Defines a Litestar application with dependency injection and a lifespan manager for container tear-down. Use this setup to integrate the DI container with your Litestar application. ```python import typing import fastapi import contextlib from litestar import Litestar, get from litestar.di import Provide from litestar.status_codes import HTTP_200_OK from litestar.testing import TestClient from tests import container @get("/") async def index(injected: str) -> str: return injected @contextlib.asynccontextmanager async def lifespan_manager(_: fastapi.FastAPI) -> typing.AsyncIterator[None]: try: yield finally: await container.DIContainer.tear_down() app = Litestar( route_handlers=[index], dependencies={"injected": Provide(container.DIContainer.async_resource)}, lifespan=[lifespan_manager], ) def test_litestar_di() -> None: with (TestClient(app=app) as client): response = client.get("/") assert response.status_code == HTTP_200_OK, response.text assert response.text == "async resource" ``` -------------------------------- ### Using Provide[...] as Default Parameter Source: https://github.com/modern-python/that-depends/blob/main/docs/introduction/injection.md Wrap provider references in Provide[...] when using them as default parameter values for correct type resolution by mypy and IDEs. ```python @inject def greet_user_direct( greeting: str = Provide[MyContainer.greeting_provider] # (1)! ) -> str: return f"Greeting: {greeting}" ``` -------------------------------- ### Define Container with Singleton Settings Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/singleton.md Define a container with a Singleton provider for settings and an async factory for a database connection that uses those settings. The Singleton ensures settings are parsed only once. ```python from that_depends import BaseContainer, providers async def get_db_connection(address: str, port: int, db_name: str): # e.g., create an async DB connection ... class MyContainer(BaseContainer): # We'll parse settings only once config = providers.Singleton(Settings) # We'll pass the config's DB fields into an async factory for a DB connection db_connection = providers.AsyncFactory( get_db_connection, config.db.address, config.db.port, config.db.db_name, ) ``` -------------------------------- ### Enter Sync Context Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/context-resources.md Use `container_context` to enter a synchronous context, which only allows resolution of synchronous dependencies. ```python with container_context(MyContainer): ... ``` -------------------------------- ### Async Function Injection with @inject and Provide Source: https://context7.com/modern-python/that-depends/llms.txt The @inject decorator enables automatic dependency resolution for function parameters using Provide. This example demonstrates its use with an async function and overriding injected values with explicit arguments. ```python from that_depends import BaseContainer, providers, inject, Provide class Container(BaseContainer): greeting = providers.Singleton(lambda: "Hello") name = providers.Factory(lambda: "World") @inject async def say_hello( greeting: str = Provide[Container.greeting], name: str = Provide[Container.name], ) -> str: return f"{greeting}, {name}!" import asyncio print(asyncio.run(say_hello())) # "Hello, World!" print(asyncio.run(say_hello("Hi"))) # "Hi, World!" — explicit arg overrides injection ``` -------------------------------- ### Define a Resource Using a Sync Context Manager Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/resources.md Utilize a standard Python synchronous context manager class with `Resource`. The `Resource` provider will automatically handle entering and exiting the context manager during resolution and teardown. ```python import typing from that_depends.providers import Resource class SyncFileManager: def __enter__(self) -> str: print("Opening file") return "/path/to/file" def __exit__(self, exc_type, exc_val, exc_tb): print("Closing file") sync_file_resource = Resource(SyncFileManager) # usage file_path = sync_file_resource.resolve_sync() print(file_path) sync_file_resource.tear_down_sync() ``` -------------------------------- ### Initialize Context for Specific Resource Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/context-resources.md Initialize a new context for a specific resource only. ```python # this will init a new context for the specific resource only. async with container_context(MyContainer.async_resource): ... ``` -------------------------------- ### Run Tests with Just Source: https://github.com/modern-python/that-depends/blob/main/docs/dev/contributing.md Execute all project tests using the 'just tests' command. This verifies the functionality of the codebase. ```bash just tests ``` -------------------------------- ### Enter Async Context Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/context-resources.md Use `container_context` to enter an asynchronous context, allowing resolution of both sync and async dependencies. ```python async with container_context(MyContainer): ... ``` -------------------------------- ### Connecting Multiple Containers Source: https://context7.com/modern-python/that-depends/llms.txt Shows how to cross-reference providers across different containers and link their lifecycle management using `connect_containers`. This ensures that initialization and teardown cascade across connected containers. ```python from that_depends import BaseContainer, providers class InfraContainer(BaseContainer): db_url = providers.Object("postgresql://localhost/app") class AppContainer(BaseContainer): db_url = InfraContainer.db_url # reference provider from another container repo = providers.Factory(lambda url: {"url": url}, db_url.cast) # Link lifecycles so init/teardown cascade: AppContainer.connect_containers(InfraContainer) async def main(): await AppContainer.init_resources() # also inits InfraContainer r = await AppContainer.repo.resolve() print(r) # {"url": "postgresql://localhost/app"} await AppContainer.tear_down() # also tears down InfraContainer ``` -------------------------------- ### Create Lazy Provider with Separate Module and Provider Strings Source: https://github.com/modern-python/that-depends/blob/main/docs/experimental/lazy.md Alternatively, specify the module and provider path separately for clarity when creating a LazyProvider. ```python from that_depends.experimental import LazyProvider lazy_p = LazyProvider(module_string="my.module", provider_string="attribute.path") ``` -------------------------------- ### FastStream Context Middleware Integration Source: https://github.com/modern-python/that-depends/blob/main/docs/integrations/faststream.md Integrate `DIContextMiddleware` with your `RabbitBroker` to initialize a context before processing messages. Configure the `Container` and `ContextScopes` as needed. ```python from that_depends.integrations.faststream import DIContextMiddleware from that_depends import ContextScopes from faststream.rabbit import RabbitBroker broker = RabbitBroker(middlewares=[DIContextMiddleware(Container, scope=ContextScopes.REQUEST)]) ``` -------------------------------- ### Singleton Provider Usage Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/singleton.md Demonstrates how to define and use a Singleton provider. The factory function is called only on the first resolution, and subsequent calls return the cached instance. Supports synchronous and asynchronous resolution, as well as injection. ```python import random from that_depends import BaseContainer, Provide, inject, providers def some_function() -> float: """Generate number between 0.0 and 1.0""" return random.random() # define container with `Singleton` provider: class MyContainer(BaseContainer): singleton = providers.Singleton(some_function) # The provider will call `some_function` once and cache the return value # 1) Synchronous resolution MyContainer.singleton.resolve_sync() # e.g. 0.3 MyContainer.singleton.resolve_sync() # 0.3 (cached) # 2) Asynchronous resolution await MyContainer.singleton.resolve() # 0.3 (same cached value) # 3) Injection example @inject async def with_singleton(number: float = Provide[MyContainer.singleton]): # number == 0.3 ... ``` -------------------------------- ### Set up FastAPI app with DIContextMiddleware Source: https://github.com/modern-python/that-depends/blob/main/docs/integrations/fastapi.md Integrate `DIContextMiddleware` into a FastAPI application to automatically manage container context for each request. ```python # main.py from fastapi import FastAPI, Depends from starlette.responses import Response from that_depends.providers import DIContextMiddleware from mycontainer import MyContainer app = FastAPI() # Attach the middleware, optionally passing the container and/or a global_context app.add_middleware( DIContextMiddleware, MyContainer, global_context={"app_name": "MyApp"}, # optional dictionary available in the context ) ``` -------------------------------- ### Create a State Provider Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/state.md Define a State provider within a BaseContainer. It does not accept arguments during creation. ```python from that_depends import BaseContainer, providers class Container(BaseContainer): my_state: providers.State[int] = providers.State() ``` -------------------------------- ### DIContextMiddleware for ASGI applications Source: https://context7.com/modern-python/that-depends/llms.txt Wrap an ASGI application with `DIContextMiddleware` to automatically initialize container contexts for each request. It can also set global context and define the request scope. ```python import typing from fastapi import FastAPI, Depends from that_depends import BaseContainer, providers from that_depends.providers import DIContextMiddleware from that_depends.providers.context_resources import ContextScopes async def db_session() -> typing.AsyncIterator[str]: yield "pg-session" class Container(BaseContainer): default_scope = ContextScopes.REQUEST session = providers.ContextResource(db_session) app = FastAPI() app.add_middleware( DIContextMiddleware, Container, # initialize all Container ContextResources global_context={"app": "demo"}, scope=ContextScopes.REQUEST, ) @app.get("/ping") async def ping(session: str = Depends(Container.session)) -> dict: return {"session": session} # "pg-session" ``` -------------------------------- ### Define FastAPI Settings with Singleton Provider Source: https://github.com/modern-python/that-depends/blob/main/docs/integrations/fastapi.md Define application settings using Pydantic and provide a single, shared instance using `providers.Singleton` in your container. ```python from that_depends import BaseContainer, providers from pydantic import BaseModel class AppSettings(BaseModel): database_url: str = "sqlite:///:memory:" class MyAdvancedContainer(BaseContainer): # Provide a single, shared settings instance settings = providers.Singleton(AppSettings) ``` -------------------------------- ### Initialize container_context with arguments in v3.* Source: https://github.com/modern-python/that-depends/blob/main/docs/migration/v3.md In v3.*, `container_context` must be called with at least one argument or keyword argument. To reset context for all containers, explicitly pass them. ```python async with container_context(MyContainer_1, MyContainer_2, ...): ... ``` -------------------------------- ### Attach Resources to a Container Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/resources.md Instantiate the `Resource` provider with your creation functions and assign them as attributes to a `BaseContainer` subclass. This makes them available for injection. ```python from that_depends import BaseContainer from that_depends.providers import Resource class MyContainer(BaseContainer): sync_resource = Resource(create_sync_resource) async_resource = Resource(create_async_resource) ``` -------------------------------- ### Incompatible `container_context()` Initialization Source: https://github.com/modern-python/that-depends/blob/main/docs/migration/v2.md Avoid importing containers or resolving context resources after entering `container_context()` without arguments, as this is incompatible with v2. ```python from that_depends import container_context async def some_async_function(): # Enter a new context but import `MyContainer` later async with container_context(): from some_other_module import MyContainer # Attempt to resolve a `ContextResource` resource my_resource = await MyContainer.my_context_resource.async_resolve() # ❌ Error! ``` -------------------------------- ### Run Linters with Just Source: https://github.com/modern-python/that-depends/blob/main/docs/dev/contributing.md Execute static analysis checks using Ruff and mypy by running the 'just lint' command. This helps maintain code quality. ```bash just lint ``` -------------------------------- ### Define a Basic Container Source: https://github.com/modern-python/that-depends/blob/main/docs/introduction/ioc-container.md Subclass `BaseContainer` and define providers as class attributes to create a new container. ```python from that_depends import BaseContainer class Container(BaseContainer): # define your providers here ``` -------------------------------- ### Factory with Parameters Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/factories.md Define factories that accept parameters, including other providers. The factory function will be called with the resolved dependencies. ```python def greet(name: str) -> str: return f"Hello, {name}!" class MyContainer(BaseContainer): name = Factory(lambda: "Alice") greeting = Factory(greet, name) ``` -------------------------------- ### Create Lazy Provider with Single Import String Source: https://github.com/modern-python/that-depends/blob/main/docs/experimental/lazy.md Use LazyProvider with a single string representing the full import path to the provider. ```python from that_depends.experimental import LazyProvider lazy_p = LazyProvider("my.module.attribute.path") ``` -------------------------------- ### Configure custom Route class with context Source: https://github.com/modern-python/that-depends/blob/main/docs/integrations/fastapi.md Initialize the custom route class with arguments to manage container context, such as global context data and scope. ```python my_route_class = create_fastapi_route_class(Container, global_context={"key": "value"}, scope=ContextScopes.REQUEST) ``` -------------------------------- ### Override Multiple Providers with Context Manager Source: https://github.com/modern-python/that-depends/blob/main/docs/testing/provider-overriding.md Use a context manager to override multiple providers simultaneously. This is useful for setting up specific test environments. The overrides are automatically reverted upon exiting the context. ```python def main(): pg_container = PostgresContainer(image='postgres:alpine3.19') pg_container.start() db_url = pg_container.get_connection_url() local_testing_settings = Settings(db_url=db_url) providers_for_overriding = { 'settings': local_testing_settings, # more values... } with DIContainer.override_providers_sync(providers_for_overriding): try: result = exec_query_example() assert result == (234,) finally: pg_container.stop() ``` -------------------------------- ### Resolving Context Resources within Container Context Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/context-resources.md Shows how to initialize a new context for a `MyContainer` using `container_context` and then resolve both asynchronous and synchronous resources within that context. ```python async with container_context(MyContainer): # this will initialize a new context for MyContainer await MyContainer.async_resource.resolve() # "async resource" MyContainer.sync_resource.resolve_sync() # "sync resource" ``` -------------------------------- ### Use async resolve() method in v3.* Source: https://github.com/modern-python/that-depends/blob/main/docs/migration/v3.md In v3.*, default provider and container methods are async. Use `.resolve()` for async resolution, which replaces `.async_resolve()` from v2.*. ```python # In v3.* await container.some_dependency.resolve() ``` -------------------------------- ### Manually Resolve Singleton and AsyncFactory Dependencies Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/singleton.md Manually resolve a Singleton configuration object synchronously and an asynchronously created database connection. This shows direct access to resolved dependencies when decorators are not used. ```python # Synchronously resolve the config cfg = MyContainer.config.resolve_sync() # Asynchronously resolve the DB connection connection = await MyContainer.db_connection.resolve() ``` -------------------------------- ### Forcing Context Initialization (p.context_async) Source: https://github.com/modern-python/that-depends/blob/main/docs/introduction/scopes.md Force the initialization of a ContextResource's context outside its defined scope using `p.context_async(force=True)`. The current scope will be `None` after this. ```python p = providers.ContextResource(my_resource).with_config(scope=ContextScopes.APP) async with p.context_async(force=True): assert get_current_scope() == None await p.resolve() # will resolve ``` -------------------------------- ### Use override() for async overrides in v2.* Source: https://github.com/modern-python/that-depends/blob/main/docs/migration/v3.md In v2.*, the `.override()` method was used for overriding provider values. In v3.*, this is now synchronous and `.override_sync()` should be used. ```python provider.override(value) ``` -------------------------------- ### Build Dependency Graph in Container Source: https://github.com/modern-python/that-depends/blob/main/docs/introduction/ioc-container.md Define providers like `Singleton` and `Factory` within a container, specifying their dependencies. Use `.cast` to ensure type compatibility between providers. ```python from that_depends import providers class Container(BaseContainer): config = providers.Singleton(Config) session = providers.Factory(create_db_session, config=config.db) # (1)! user_repository = providers.Factory( UserRepository, session=session.cast, # (3)! config.users ) # (2)! ``` -------------------------------- ### Teardown Propagation in Providers Source: https://context7.com/modern-python/that-depends/llms.txt Demonstrates how tearing down a parent provider automatically invalidates dependent (child) providers. Shows how to opt-out of this propagation using `propagate=False`. ```python import random from that_depends import BaseContainer, providers class Container(BaseContainer): seed = providers.Singleton(random.random) derived = providers.Singleton(lambda x: x * 2, seed.cast) async def main(): s1 = await Container.seed.resolve() d1 = await Container.derived.resolve() assert d1 == s1 * 2 # Tearing down 'seed' also invalidates 'derived' await Container.seed.tear_down() s2 = await Container.seed.resolve() d2 = await Container.derived.resolve() assert s2 != s1 assert d2 == s2 * 2 # derived was recomputed from new seed # Opt-out of propagation: await Container.seed.tear_down(propagate=False) # 'derived' still holds its old cached value ``` -------------------------------- ### Initialize Context for Specific Container Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/context-resources.md Initialize a new context for all `ContextResources` within a specific container and any connected containers. ```python # this will init a new context for all ContextResources in MyContainer and any connected containers. async with container_context(MyContainer): ... ``` -------------------------------- ### Initializing Global Context with Decorator Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/context-resources.md Illustrates using `container_context` as a decorator to initialize a global context. This allows access to context items within the decorated function. ```python @container_context(global_context={"key": "value"}) async def func(): # run some code fetch_context_item("key") ``` -------------------------------- ### Use reset_override_sync() for synchronous reset overrides in v3.* Source: https://github.com/modern-python/that-depends/blob/main/docs/migration/v3.md In v3.*, reset overriding methods are async by default. Use `.reset_override_sync()` for synchronous reset overrides, replacing `.reset_override()` from v2.*. ```python provider.reset_override_sync() ``` -------------------------------- ### DIContextMiddleware for ASGI Applications Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/context-resources.md The `DIContextMiddleware` manages context resources for ASGI applications. It automatically initializes contexts for specified containers or resources when an endpoint is called. ```python import fastapi from that_depends.providers import DIContextMiddleware, ContextResource from that_depends import BaseContainer MyContainer: BaseContainer my_context_resource_provider: ContextResource my_app: fastapi.FastAPI # This will initialize the context for `my_context_resource_provider` and `MyContainer` whenever an endpoint is called. my_app.add_middleware(DIContextMiddleware, MyContainer, my_context_resource_provider) # This will initialize the context for all containers when an endpoint is called. my_app.add_middleware(DIContextMiddleware) ``` -------------------------------- ### Forcing Context Initialization (Container.context) Source: https://github.com/modern-python/that-depends/blob/main/docs/introduction/scopes.md Force context initialization for providers within a container using the `@Container.context(force=True)` decorator on an injected function. This allows resolution outside the defined scope. ```python class Container(BaseContainer): p = providers.ContextResource(my_resource).with_config(scope=ContextScopes.APP) @Container.context(force=True) @inject async def injected(val = Provide[Container.p]): return p await injected() # will resolve ``` -------------------------------- ### Use override_context_sync() for synchronous context overrides in v3.* Source: https://github.com/modern-python/that-depends/blob/main/docs/migration/v3.md In v3.*, context overriding methods are async by default. Use `.override_context_sync()` for synchronous context overrides, replacing `.override_context()` from v2.*. ```python provider.override_context_sync() ``` -------------------------------- ### Custom Dependency to Initialize DI Context with Request Source: https://github.com/modern-python/that-depends/blob/main/docs/integrations/fastapi.md Create a custom FastAPI dependency that uses `container_context` to set up the `that-depends` context, making the `fastapi.Request` available to providers. ```python # request_deps.py from fastapi import Request from typing import AsyncIterator from that_depends import container_context, fetch_context_item async def init_di_context(request: Request) -> AsyncIterator[None]: # We store the request in a that_depends global context async with container_context(global_context={"request": request}): yield ``` -------------------------------- ### INJECT scope resolution with explicit provider scope Source: https://github.com/modern-python/that-depends/blob/main/docs/introduction/scopes.md Shows how to explicitly set the scope for a provider using `.with_config(scope=...)`. Resolving a provider whose context is not initialized will raise a `RuntimeError`. ```python import typing import random from that_depends.providers import providers, inject, Provide, ContextScopes, BaseContainer, get_current_scope def iterator() -> typing.Iterator[float]: yield random.random() class Container(BaseContainer): default_scope = ContextScopes.INJECT provider = providers.ContextResource(iterator).with_config(scope=ContextScopes.INJECT) another_provider = providers.ContextResource(iterator).with_config(scope=ContextScopes.INJECT) @inject(scope=ContextScopes.INJECT) def injected(v: int = Provide[Container.provider]) -> float: # (2)! assert get_current_scope() == None assert v == Container.provider.resolve_sync() # (3)! Container.another_provider.resolve_sync() # (1)! return v injected() ``` -------------------------------- ### Type-Based Injection with Provide() Source: https://context7.com/modern-python/that-depends/llms.txt Bind providers to types and inject dependencies using type annotations directly with Provide(). This method supports contravariance and avoids explicit provider referencing in the injection point. ```python import random from that_depends import BaseContainer, providers, inject, Provide class Container(BaseContainer): rand_float = providers.Factory(random.random).bind(float) rand_int = providers.Factory(lambda: random.randint(1, 100)).bind(int) @Container.inject async def compute(x: float = Provide(), n: int = Provide()) -> str: return f"float={x:.4f}, int={n}" import asyncio print(asyncio.run(compute())) # e.g. "float=0.3421, int=57" ``` -------------------------------- ### Resolving Context Resource with Decorator Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/context-resources.md Demonstrates resolving an `async_resource` using its context decorator and the `inject` utility. A new context for `async_resource` is initialized on each call. ```python @MyContainer.async_resource.context @inject async def func(dep: str = Provide[MyContainer.async_resource]): return dep await func() # returns "async resource" ``` -------------------------------- ### Pass Explicit Arguments to `DIContextMiddleware` Source: https://github.com/modern-python/that-depends/blob/main/docs/migration/v2.md When using `DIContextMiddleware` with ASGI applications, you can now pass additional arguments like container classes and resource providers. ```python import fastapi from that_depends.providers import DIContextMiddleware, ContextResource from that_depends import BaseContainer MyContainer: BaseContainer my_context_resource_provider: ContextResource my_app: fastapi.FastAPI my_app.add_middleware(DIContextMiddleware, MyContainer, my_context_resource_provider) ``` -------------------------------- ### Define Container and Provider Source: https://github.com/modern-python/that-depends/blob/main/docs/introduction/injection.md Define a BaseContainer subclass and declare providers using Singleton, Factory, or other provider types. ```python from that_depends import BaseContainer from that_depends.providers import Singleton class MyContainer(BaseContainer): greeting_provider = Singleton(lambda: "Hello from MyContainer") ``` -------------------------------- ### String-Based Injection with Provide["Container.provider"] Source: https://context7.com/modern-python/that-depends/llms.txt Inject providers by their string name, formatted as "ContainerName.provider_name[.attr...]". This is useful for avoiding import-time circular dependencies. ```python from that_depends import BaseContainer, providers, inject, Provide from pydantic_settings import BaseSettings class Config(BaseSettings): app_name: str = "MyApp" class AppContainer(BaseContainer): alias = "App" # optional alias replaces class name in string lookups config = providers.Factory(Config) @inject def get_name(name: str = Provide["App.config.app_name"]) -> str: return name print(get_name()) # "MyApp" ``` -------------------------------- ### Selector with Callable Source: https://github.com/modern-python/that-depends/blob/main/docs/providers/selector.md Use a callable that returns a string to dynamically determine which provider to select. The default value 'local' is used if the environment variable 'STORAGE_BACKEND' is not set. ```python import os from typing import Protocol from that_depends import BaseContainer, providers class StorageService(Protocol): ... class StorageServiceLocal(StorageService): ... class StorageServiceRemote(StorageService): ... class DIContainer(BaseContainer): storage_service = providers.Selector( lambda: os.getenv("STORAGE_BACKEND", "local"), local=providers.Factory(StorageServiceLocal), remote=providers.Factory(StorageServiceRemote), ) ``` -------------------------------- ### Reset all containers with reset_all_containers=True Source: https://github.com/modern-python/that-depends/blob/main/docs/migration/v2.md To reset the context for all `providers.ContextResource` instances globally when setting a global context, use the `reset_all_containers=True` argument in `container_context()`. ```python async with container_context(global_context=my_global_context, reset_all_containers=True): assert fetch_context_item("some_key") == "some_value" ```