This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This project uses just and uv. See Justfile for the source of truth.
just install—uv lock --upgradethenuv sync --all-extras --frozen --group lintjust lint— runseof-fixer,ruff format,ruff check --fix, thenty check(writes)just lint-ci— same checks in non-mutating mode (--check,--no-fix)just test—uv run --no-sync pytest, forwards extra args; no coverage (addoptsis empty)just test-ci— gated run: coverage with--cov-fail-under=100(the 100% line-coverage gate)just test-branch— liketest-ciplus--cov-branch- Run a single test:
just test tests/test_expose.py::test_expose_generates_repo_fixture(or-k <expr>) - Type checker is
ty; suppress with# ty: ignore(not# type: ignore)
Changes follow the planning convention in planning/README.md —
start at its Quick path to pick a lane (Full / Lightweight / Tiny) before
making a change. just check-planning validates planning changes; just index prints the
change/decision index. The applied convention version is in
planning/.convention-version.
This package is a thin pytest adapter over modern-di. All implementation lives in modern_di_pytest/factory.py and exposes exactly two public symbols:
modern_di_fixture(dependency, *, container_fixture="di_container", name=None, pytest_scope="function")— wraps a single type orAbstractProviderin a@pytest.fixture. At fixture time it callsrequest.getfixturevalue(container_fixture), then delegates tocontainer.resolve_dependency(dependency)— the type-or-provider dispatch lives in modern-di itself.expose(*groups, container_fixture="di_container", pytest_scope="function", module=None)— variadic: accepts one or moreGroupsubclasses. For each, iteratesvars(group)and for every attribute that is anAbstractProviderinstance, builds amodern_di_fixtureandsetattrs it onto the target module under the attribute's name. Non-Provider attributes (strings, ints, underscored, etc.) are silently skipped. A duplicate attribute name across the given groups raisesValueError; calling with no groups raisesTypeError. Whenmoduleis omitted, the caller's module is located viainspect.stack()[1]—exposetherefore only works when called from module scope of aconftest.py/ test module, not from inside a function.
Key contract: this package does not own the container. The user defines a di_container pytest fixture (any scope) that yields a modern_di.Container. Child-scoped containers (e.g. REQUEST) are accessed by passing a different container_fixture= name — see tests/conftest.py for the di_container / di_request_container pattern. Overrides are not re-implemented here; users call Container.override() / reset_override() directly.
tests/sample.py is the reference fixture model: a Group subclass holding providers.Factory instances at APP and REQUEST scopes, plus deliberately non-Provider attributes to exercise the skip path in expose.
When a change alters a capability's behavior, update the matching architecture/<capability>.md in the same PR.