From 634bccb4fe1d27e8090d13b218511b0b29126385 Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 5 May 2026 13:33:30 +0200 Subject: [PATCH 1/4] misc: added pyrefly badge. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 587ad0a..ea1f4b2 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ == [![Test Coverage](https://codecov.io/gh/modern-python/that-depends/branch/main/graph/badge.svg)](https://codecov.io/gh/modern-python/that-depends) [![MyPy Strict](https://img.shields.io/badge/mypy-strict-blue)](https://mypy.readthedocs.io/en/stable/getting_started.html#strict-mode-and-configuration) +[![pyrefly](https://img.shields.io/endpoint?url=https://pyrefly.org/badge.json)](https://github.com/facebook/pyrefly) [![Supported versions](https://img.shields.io/pypi/pyversions/that-depends.svg)](https://pypi.python.org/pypi/that-depends) [![PyPI Downloads](https://static.pepy.tech/badge/that-depends/month)](https://pepy.tech/projects/that-depends) [![GitHub stars](https://img.shields.io/github/stars/modern-python/that-depends)](https://github.com/modern-python/that-depends/stargazers) From aa3de265d159c8234428373e932ed395303aeeeb Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 5 May 2026 14:13:49 +0200 Subject: [PATCH 2/4] feat: add skill. --- .agents/skills/that-depends/SKILL.md | 256 +++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 .agents/skills/that-depends/SKILL.md diff --git a/.agents/skills/that-depends/SKILL.md b/.agents/skills/that-depends/SKILL.md new file mode 100644 index 0000000..9db7303 --- /dev/null +++ b/.agents/skills/that-depends/SKILL.md @@ -0,0 +1,256 @@ +--- +name: that-depends +description: Guide for using the `that-depends` library, a Python dependency injection framework. +--- + +# `that-depends` + +`that-depends` is a typed dependency-injection framework for Python. The core workflow is: + +1. Define a `BaseContainer`. +2. Register providers as class attributes. +3. Consume dependencies with `@inject` and `Provide[...]`. +4. Use context decorators for `ContextResource` providers. +5. Override providers in tests instead of patching call sites. + +## Recommended usage + +### Define providers on a container + +Keep providers on `BaseContainer` subclasses instead of as standalone globals, especially if you need context features. + +```python +from that_depends import BaseContainer, providers + + +class Settings: + def __init__(self) -> None: + self.base_url = "https://api.example.com" + + +class ApiClient: + def __init__(self, base_url: str) -> None: + self.base_url = base_url + + +class UserService: + def __init__(self, client: ApiClient) -> None: + self.client = client + + +class Container(BaseContainer): + settings = providers.Singleton(Settings) + api_client = providers.Factory(ApiClient, base_url=settings.cast.base_url) + user_service = providers.Factory(UserService, client=api_client.cast) +``` + +### Prefer the decorator API for application code + +Use `@inject` with `Provide[Container.provider]` defaults. + +```python +from that_depends import Provide, inject + + +@inject +def handle_user(service: UserService = Provide[Container.user_service]) -> UserService: + return service + +``` + +This is the preferred style over explicit `resolve()` / `resolve_sync()` calls in application code. + +## Provider cheat sheet + +| Provider | Use for | +| --- | --- | +| `Singleton` / `AsyncSingleton` | One cached instance | +| `Factory` / `AsyncFactory` | New value on each resolution | +| `Resource` | Cached value with teardown | +| `ContextResource` | Per-context / per-scope resource | +| `Sequence` / `Mapping` | Aggregate multiple providers into read-only collection types | +| `Selector` | Choose one provider from a key | +| `State` | Pass runtime state through context | + +## Best practices + +### Prefer injection over explicit resolution + +Avoid calling `resolve()` and `resolve_sync()` inside normal application code. Inject dependencies into function parameters instead. +Reserve explicit resolution for bootstrapping, one-off scripts, REPL usage, or tests. + +**Avoid:** + +```python +def create_handler() -> UserService: + return Container.user_service.resolve_sync() +``` + +**Prefer:** + +```python +@inject +def create_handler(service: UserService = Provide[Container.user_service]) -> UserService: + return service +``` + +Why this is better: + +- it keeps call sites easy to override in tests; +- it works naturally with scoped/context-managed providers; +- callers can still pass explicit arguments without overriding providers. + +### Do not call `resolve()` / `resolve_sync()` in injected function bodies + +This is especially important for `ContextResource` providers. The injection system initializes context for dependencies declared in `Provide[...]` defaults; explicit resolution inside the function body is discouraged and can fail for scoped resources. + +**Avoid:** + +```python +import typing + +from that_depends.providers.context_resources import ContextScopes + + +def open_session() -> typing.Iterator[str]: + yield "session" + + +class Container(BaseContainer): + default_scope = ContextScopes.INJECT + session = providers.ContextResource(open_session).with_config(scope=ContextScopes.INJECT) + + +@inject(scope=ContextScopes.INJECT) +def bad() -> str: + return Container.session.resolve_sync() +``` + +**Prefer:** + +```python +@inject(scope=ContextScopes.INJECT) +def good(session: str = Provide[Container.session]) -> str: + return session +``` + +### Prefer `provider.context()` over `container_context()` + +If you only need to initialize context for one provider, prefer the provider decorator/context API. It is more local and easier to read. + +**Preferred for a single provider:** + +```python +import typing + +from that_depends import Provide, inject + + +def request_id_resource() -> typing.Iterator[str]: + yield "req-123" + + +class Container(BaseContainer): + request_id = providers.ContextResource(request_id_resource) + + +@Container.request_id.context +@inject +def endpoint(request_id: str = Provide[Container.request_id]) -> str: + return request_id +``` + +Use `container_context()` when you need one of these: + +- initialize context for multiple providers or containers at once; +- pass `global_context`; +- manually control a named scope. + +```python +from that_depends import container_context +from that_depends.providers.context_resources import ContextScopes + + +async with container_context( + Container.request_id, + global_context={"trace_id": "abc-123"}, + scope=ContextScopes.REQUEST, +): + ... +``` + +If you need all `ContextResource` providers in one container, `@Container.context` is often cleaner than `container_context(Container)`. + +```python +@Container.context +@inject +async def run_endpoint(request_id: str = Provide[Container.request_id]) -> str: + return request_id +``` + +### Prefer direct provider references + +Use `Provide[Container.provider]` directly in examples and normal application code: + +```python +@inject +def fn(service: UserService = Provide[Container.user_service]) -> UserService: + return service +``` + +### Use overrides in tests + +Prefer provider or container override APIs over patching internals. + +```python +def test_handler_override() -> None: + fake_service = UserService(ApiClient("https://test.example.com")) + + with Container.user_service.override_context_sync(fake_service): + assert create_handler() is fake_service +``` + +If you override an upstream cached dependency and need dependents to refresh, use `tear_down_children=True`. + +```python +Container.settings.override_sync(Settings(), tear_down_children=True) +``` + +### Tear down cached providers in tests and shutdown hooks + +`Singleton` and `Resource` values are cached. Tear them down between tests or at application shutdown. + +```python +import pytest_asyncio +from typing import AsyncIterator + + +@pytest_asyncio.fixture(autouse=True) +async def di_teardown() -> AsyncIterator[None]: + try: + yield + finally: + await Container.tear_down() +``` + +## Notes on advanced features + +- `Generator` injection is supported, but generator injection cannot initialize `ContextResource` contexts for you. Pre-initialize the context first if needed. +- `Selector`, `Sequence`, and `Mapping` help compose providers instead of manually wiring branches and aggregates in application code. +- `State` is useful for runtime values that should flow through provider resolution. +- For framework integrations, see FastAPI, FastStream, and Litestar support in the docs. + +## Practical defaults + +When writing or reviewing code that uses `that-depends`, prefer these defaults: + +1. Put providers on a `BaseContainer`. +2. Use `@inject` plus `Provide[Container.provider]`. +3. Avoid `resolve()` / `resolve_sync()` in application code. +4. Use `provider.context()` or `Container.context()` for context-managed dependencies. +5. Reach for `container_context()` only when multiple providers/containers, global context, or explicit scope control are required. +6. Use override APIs in tests and `tear_down()` in cleanup paths. + +## Detailed documentation + +For full package documentation, read the official docs: [https://that-depends.readthedocs.io/llms.txt](https://that-depends.readthedocs.io/llms.txt) From 3b3ffb944bf527e636bd0db65962b3378c59122a Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 5 May 2026 21:45:50 +0200 Subject: [PATCH 3/4] feat: move skill to main package dir. --- .../SKILL.md => that_depends/.agents/skills/that-depends/Skill.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .agents/skills/that-depends/SKILL.md => that_depends/.agents/skills/that-depends/Skill.md (100%) diff --git a/.agents/skills/that-depends/SKILL.md b/that_depends/.agents/skills/that-depends/Skill.md similarity index 100% rename from .agents/skills/that-depends/SKILL.md rename to that_depends/.agents/skills/that-depends/Skill.md From 994532a77db152a1c2266f08e32ec800edf2cdf0 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 6 May 2026 00:36:45 +0200 Subject: [PATCH 4/4] docs: Update skill --- that_depends/.agents/skills/that-depends/Skill.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/that_depends/.agents/skills/that-depends/Skill.md b/that_depends/.agents/skills/that-depends/Skill.md index 9db7303..324027a 100644 --- a/that_depends/.agents/skills/that-depends/Skill.md +++ b/that_depends/.agents/skills/that-depends/Skill.md @@ -143,7 +143,7 @@ If you only need to initialize context for one provider, prefer the provider dec ```python import typing -from that_depends import Provide, inject +from that_depends import Provide, inject, providers, BaseContainer def request_id_resource() -> typing.Iterator[str]: @@ -253,4 +253,4 @@ When writing or reviewing code that uses `that-depends`, prefer these defaults: ## Detailed documentation -For full package documentation, read the official docs: [https://that-depends.readthedocs.io/llms.txt](https://that-depends.readthedocs.io/llms.txt) +For full package documentation, read the official [documentation](https://that-depends.readthedocs.io/llms.txt).