Batch preparation and distribution of expensive test resources for pytest.
Project description
pytest-warmup
pytest-warmup is a pytest plugin for batch preparation and distribution of expensive test resources.
Use it when ordinary fixture-by-fixture setup becomes too slow or too hard to reason about because objects are expensive to create, depend on one another, or require extra orchestration after creation.
Typical cases:
- creating external-domain objects in batches before a module or session runs;
- waiting for synchronization, indexing, or propagation after creation;
- reusing one prepared upstream object across multiple tests;
- creating per-test instances only where the declaration explicitly asks for it;
- replacing selected prepared values from a snapshot file for debugging.
Installation
pip install pytest-warmup
Public API
This package is intentionally narrow:
WarmupPlanWarmupRequirementWarmupError@warmup_param(...)warmup_mgr.use(...).prepare(...)
Quick Start
Declare resource requirements in plan classes:
from pytest_warmup import WarmupPlan, WarmupRequirement
class ProfilePlan(WarmupPlan):
def require(
self,
*,
profile_name: str,
id: str | None = None,
is_per_test: bool | None = None,
) -> WarmupRequirement:
return super().require(
payload={"profile_name": profile_name},
dependencies={},
id=id,
is_per_test=is_per_test,
)
def prepare(self, nodes, runtime) -> None:
for node in nodes:
runtime.set(
node,
{
"profile_id": f"profile-{node.payload['profile_name']}",
"profile_name": node.payload["profile_name"],
},
)
Build requirements from those plans:
profile = ProfilePlan("profile")
profile_main = profile.require(profile_name="main", id="profile_main")
Create one explicit producer fixture:
import pytest
@pytest.fixture(scope="module")
def prepare_data(warmup_mgr):
return warmup_mgr.use(profile).prepare()
Inject the prepared resource into a test or fixture:
from pytest_warmup import warmup_param
@warmup_param("prepared_profile", profile_main)
def test_profile(prepare_data, prepared_profile):
assert prepared_profile["profile_id"].startswith("profile-")
is_per_test=True on a requirement means that requirement is materialized separately for each collected test item. If omitted, the requirement inherits per-test behavior from upstream dependencies, otherwise it stays shared within the producer scope.
For full runnable examples, see:
examples/basic_usage.pyexamples/autoresolve_usage.pyfor fixture-side autoresolve bindingexamples/named_producer_usage.py
Producer Patterns
The default model stays explicit: a test or fixture depends on a producer fixture in the ordinary pytest dependency chain.
Recommended order:
- use an explicit producer argument for the default, most readable path;
- use
warmup_autoresolve_producerwhen you want less producer boilerplate but still keep one clearly defined producer seam; - use
producer_fixture="..."only to disambiguate between producers that are already present in the pytest dependency chain.
Producer resolution rules:
- if
producer_fixture="..."is provided, that fixture must already be part of the pytest dependency chain and is used as the producer; - otherwise, if the dependency chain already contains exactly one prepared producer, that producer is used;
- otherwise,
warmup_autoresolve_produceris used as a narrow fallback if it exists; - otherwise, producer resolution fails fast.
Producer convenience does not bypass normal pytest scope rules. A narrower producer is still invalid for a wider-scope consumer, and warmup_autoresolve_producer does not widen fixture visibility.
Example of the fallback fixture:
@pytest.fixture
def warmup_autoresolve_producer(prepare_data):
return prepare_data
@pytest.fixture
@warmup_param("prepared_profile", profile_main)
def prepared_profile_fixture(prepared_profile):
return prepared_profile
Snapshot File Overrides
Debug replacement is file-based through prepare(snapshot_file=...).
The current JSON shape is:
{
"shared": {
"profile_main": {
"profile_id": "debug-profile"
}
},
"tests": {
"tests/test_module.py::test_case": {
"items_alpha": {
"items_id": "debug-items"
}
}
}
}
Rules:
- shared nodes are addressed by
id; - per-test nodes are addressed by
tests[nodeid][id]; - declarations that are effectively per-test may not be overridden through
shared.
Troubleshooting
Common producer-resolution errors usually mean one of these:
no producer fixture found in pytest dependency chain ...The decorated test or fixture is not connected to any producer fixture, and nowarmup_autoresolve_producerfallback exists.multiple producer fixtures found in pytest dependency chainThe current dependency chain exposes more than one prepared producer. Simplify the chain or useproducer_fixture="..."to pick one explicitly.producer fixture '...' is not in this dependency chainThe named producer exists, but the current test or fixture does not depend on it through ordinary pytest wiring.producer fixture '...' must return a prepared warmup scopeThe selected fixture returned an ordinary value instead of the prepared scope returned bywarmup_mgr.use(...).prepare(...).... cannot be shared because dependency ... is per-testA shared declaration depends on a branch that is effectively per-test. Either inherit that branch or split the declaration differently.
Scope Boundary
pytest-warmup is not trying to be:
- a general-purpose factory framework;
- a generic snapshot assertion library;
- a container or infrastructure manager;
- a hidden autouse preparation layer;
- a domain-specific toolkit.
It focuses on one problem: batch creation and targeted distribution of expensive test resources.
Further design details live in:
Development
uv venv .venv
uv pip install --python .venv/bin/python -e ".[dev]"
./.venv/bin/python -m pytest -q
./.venv/bin/python -m build
Before publishing, also do one smoke check from the built wheel in a fresh virtual environment.
Attribution
The initial code, tests, and documentation in this repository were generated and iteratively refined with ChatGPT/Codex plus collaborating agents.
Named collaborating agents from the design and spike process:
- Lovelace is an adversarial, QA-minded reviewer focused on user-facing clarity. She pushed the ergonomics tests, debug/override edge cases, and the API critiques that kept the package honest from a user perspective.
- Herschel is a graph-minded, skeptical contributor who prefers explicit execution boundaries over hidden magic. He drove the selected-roots to reachable-subgraph execution model and kept edge-case behavior small and defensible.
- Chandrasekhar is a no-magic, contract-first contributor focused on clear binding and injection rules. He helped shape the public injection model and the readable fail-fast behavior around overrides and producer discovery.
- Pauli is a direct, readability-first contributor who focused on the debug surface and shared-vs-per-test semantics. He helped keep snapshot addressing and distributed-declaration behavior explicit and testable.
- Kuhn is a pragmatic builder who prefers simple, reviewable orchestration over framework cleverness. He contributed to the manager/runtime seams and the explicit lifecycle shape used by the public prototype.
All generated material still requires human review. The repository treats generated output as draft engineering work, not as an authority.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pytest_warmup-0.1.1.tar.gz.
File metadata
- Download URL: pytest_warmup-0.1.1.tar.gz
- Upload date:
- Size: 23.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2de3a5b6d71af75903109c77cabef3aa63ceb4a9aefee4e892a752dff7cffd15
|
|
| MD5 |
7f73835a385086665c791970a95b6f6a
|
|
| BLAKE2b-256 |
0a37c7ffe0fdbaf91e422207affc6b034040dbfd33cd7a06bbcad9c132bba0e9
|
Provenance
The following attestation bundles were made for pytest_warmup-0.1.1.tar.gz:
Publisher:
publish.yml on kaor4bp/pytest-warmup
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_warmup-0.1.1.tar.gz -
Subject digest:
2de3a5b6d71af75903109c77cabef3aa63ceb4a9aefee4e892a752dff7cffd15 - Sigstore transparency entry: 1280092510
- Sigstore integration time:
-
Permalink:
kaor4bp/pytest-warmup@b9b65b0ea71b9f0e01e1d310225ce3f6d66c00ba -
Branch / Tag:
refs/tags/0.1.1 - Owner: https://github.com/kaor4bp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b9b65b0ea71b9f0e01e1d310225ce3f6d66c00ba -
Trigger Event:
release
-
Statement type:
File details
Details for the file pytest_warmup-0.1.1-py3-none-any.whl.
File metadata
- Download URL: pytest_warmup-0.1.1-py3-none-any.whl
- Upload date:
- Size: 14.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3807d336e5d27fd29ee230dd7a94c87de7e5755c909825e7a3d3aba073735d23
|
|
| MD5 |
b76a52ce2f80c9c22ca5171c34224399
|
|
| BLAKE2b-256 |
3cbff8c60ebf9b63ef3944257561c3f472ab14bc87c552da7622d2029cab367e
|
Provenance
The following attestation bundles were made for pytest_warmup-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on kaor4bp/pytest-warmup
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pytest_warmup-0.1.1-py3-none-any.whl -
Subject digest:
3807d336e5d27fd29ee230dd7a94c87de7e5755c909825e7a3d3aba073735d23 - Sigstore transparency entry: 1280092512
- Sigstore integration time:
-
Permalink:
kaor4bp/pytest-warmup@b9b65b0ea71b9f0e01e1d310225ce3f6d66c00ba -
Branch / Tag:
refs/tags/0.1.1 - Owner: https://github.com/kaor4bp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b9b65b0ea71b9f0e01e1d310225ce3f6d66c00ba -
Trigger Event:
release
-
Statement type: