Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ This project uses `just` and `uv`. See `Justfile` for the source of truth.
- `just install` — `uv lock --upgrade` then `uv sync --all-extras --frozen --group lint`
- `just lint` — runs `eof-fixer`, `ruff format`, `ruff check --fix`, then `ty check` (writes)
- `just lint-ci` — same checks in non-mutating mode (`--check`, `--no-fix`)
- `just test` — `uv run --no-sync pytest` (forwards extra args; `addopts` already adds `--cov=. --cov-report term-missing`)
- `just test-branch` — `pytest --cov-branch`
- `just test` — `uv run --no-sync pytest`, forwards extra args; no coverage (`addopts` is empty)
- `just test-ci` — gated run: coverage with `--cov-fail-under=100` (the 100% line-coverage gate)
- `just test-branch` — like `test-ci` plus `--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`)

Expand All @@ -19,7 +20,7 @@ This project uses `just` and `uv`. See `Justfile` for the source of truth.
This package is a thin pytest adapter over [`modern-di`](https://github.com/modern-python/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 or `AbstractProvider` in a `@pytest.fixture`. At fixture time it calls `request.getfixturevalue(container_fixture)`, then dispatches: `AbstractProvider` → `container.resolve_provider(...)`, otherwise `container.resolve(...)`.
- `expose(group, *, container_fixture="di_container", pytest_scope="function", module=None)` — iterates `vars(group)`, and for each attribute that is an `AbstractProvider` instance, builds a `modern_di_fixture` and `setattr`s it onto the target module under the attribute's name. Non-Provider attributes (strings, ints, underscored, etc.) are silently skipped. When `module` is omitted, the caller's module is located via `inspect.stack()[1]` — `expose` therefore only works when called from module scope of a `conftest.py` / test module, not from inside a function.
- `expose(*groups, container_fixture="di_container", pytest_scope="function", module=None)` — variadic: accepts one or more `Group` subclasses. For each, iterates `vars(group)` and for every attribute that is an `AbstractProvider` instance, builds a `modern_di_fixture` and `setattr`s 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 raises `ValueError`; calling with no groups raises `TypeError`. When `module` is omitted, the caller's module is located via `inspect.stack()[1]` — `expose` therefore only works when called from module scope of a `conftest.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.

Expand Down