that-depends is a typed dependency-injection framework for Python
3.10+. It is the org's most-used package and the only repo here with real external
contributor traffic: most merged PRs come from someone other than the maintainer.
Assume a human reviews your diff.
modern-di is the newer framework from the same author, and this repo links to it, but
that-depends is still actively maintained. Do not rewrite anything
here toward modern-di's design, and do not carry conventions across in either
direction; the two repos diverge deliberately.
just (task runner) and uv (package manager). The justfile is the
source of truth: just --list, or read it. Two behaviours to know before running
anything:
just lintrewrites files.just lint-ciis the read-only twin and is what CI runs. Uselint-ciwhen you only want to check.just installalso installs the.pre-commit-config.yamlhooks, so committing can change your working tree before the commit lands.just unhookremoves them.
Type checking is two checkers, mypy in strict mode and pyrefly, and both must
be clean. There is no ty here, despite the org-level tooling note.
Docs are MkDocs Material, but Read the Docs builds and hosts them
(.readthedocs.yaml → docs/requirements.txt → mkdocs.yml),
unlike the rest of the org, which uses the GitHub Pages workflow. Two consequences:
- Nothing builds the docs in CI. A broken link or a bad snippet lands on
mainand only surfaces in the Read the Docs build afterwards.just docsserves the site locally, but it ismkdocs servewithout--strict: a bad link warns in the log rather than failing, so read the output instead of just checking that the page renders. - A user-facing page needs two entries in
mkdocs.yml: thenavtree, and thellmstxtplugin'ssections. Miss the second and the page is absent fromllms.txt/llms-full.txt, which is what agent readers and the Context7 index (context7.json) consume. Thedev/andmigration/pages arenav-only.
that_depends/.agents/skills/that-depends/SKILL.md
sits inside the package directory, so it ships in the wheel and users get it with
the dependency. Treat it as public surface: a change to recommended usage, a renamed
provider, or a new provider belongs there as well as in docs/.
Every module under that_depends/ is named for what it does; read it. What a
single-file read will not tell you:
- Containers are global, registered by name.
BaseContainerMetakeeps a process-wide_instancesmap (meta.py); that is what makes wiring-free injection and string injection (Provide["Container.provider"]) work. Two containers sharing a name warn and the later one wins, which bites tests that declare containers at module scope. default_scopemust be assigned before anyContextResourcein a container body._ContainerMetaDict.__setitem__reads it while the class body is still executing and raisesDefaultScopeNotDefinedErrorotherwise. Statement order inside the class body is load-bearing.- Every public operation has a sync and an async twin:
resolve/resolve_sync,override/override_sync,tear_down/tear_down_sync,context_async/context_sync. Adding one half of a pair is an incomplete change.
tests/container.py holds the shared DIContainer, with resource creators split
between it and tests/creators.py; tests/conftest.py carries one autouse fixture that
resets overrides and tears the container down after each test. pytest-randomly
shuffles test order and asyncio_mode is auto, so a test that leaves provider state
behind fails somewhere else, non-deterministically. A test module that defines its own
container owns its own teardown.
CI runs the suite against both faststream major lines, so
that_depends/integrations/faststream.py has to work on each: a change there that only
passes locally is not verified. Coverage is uploaded to Codecov and the local run sets
no fail_under, so just test passing says nothing about coverage.
Fill in .github/PULL_REQUEST_TEMPLATE.md. It is
this repo's own copy, kept local because it names mypy and pyrefly, and contributors
do fill it in. Do not replace it with a free-form body, and do not edit the template.
Every link in README.md must be absolute: https://github.com/modern-python/<repo>/blob/main/<path>,
or .../tree/main/<path> for a directory. Never a relative path: README.md is also the PyPI long
description, and PyPI does not rewrite relative links, so a relative one 404s on the package page.
Unlike the rest of the org, you are not the last reader of your diff: a second party reviews and merges it. Write for them: what changed, why, and what you chose not to do. Keep one PR to one thing.
PRs are squash-merged in practice and the merge subject is the PR title plus (#N).
Conventional prefixes (fix:, docs:, chore:, ci:) are the norm for maintenance
work.
This repo has no docs/adr/ and no CONTEXT.md. A decision that outlives the PR goes
in docs/dev/main-decisions.md, which is a user-facing
page: write it for a reader of the docs site, not as an internal record. Issues live
on modern-python/that-depends itself (gh issue create), not on the org repo.
Before pushing a raised dependency floor, run the floors steps from
_checks.yml locally. If resolution fails, raise the direct floor
it names.
pyproject.toml carries version = "0" as a placeholder. The published version comes
from the git tag: publishing triggers on a GitHub release and just publish runs
uv version $GITHUB_REF_NAME. Never hand-edit version; cut a release instead.
ruff(select = ["ALL"], with an ignore list) andmypyin strict mode are configured inpyproject.toml. Two consequences that catch new code: a public class, function or method without a docstring fails lint, and the package targets Python 3.10, so no 3.11+ syntax.tests/is exempt from the docstring and private-access rules.- Docstrings use the
Args:/Returns:form ofproviders/base.py, addingRaises:where a caller needs it (injection.py,providers/selector.py). State the contract; do not narrate the implementation.
GitHub issues on modern-python/that-depends, via gh. See docs/agents/issue-tracker.md.
The five canonical roles, each label string equal to its name. See docs/agents/triage-labels.md.
Single-context: CONTEXT.md and docs/adr/ at the repo root. See docs/agents/domain.md.