pytest-warmup 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. pytest_warmup-0.1.1/.github/workflows/ci.yml +52 -0
  2. pytest_warmup-0.1.1/.github/workflows/publish.yml +58 -0
  3. pytest_warmup-0.1.1/.gitignore +7 -0
  4. pytest_warmup-0.1.1/CHANGELOG.md +14 -0
  5. pytest_warmup-0.1.1/CONTRIBUTING.md +52 -0
  6. pytest_warmup-0.1.1/LICENSE +21 -0
  7. pytest_warmup-0.1.1/PKG-INFO +269 -0
  8. pytest_warmup-0.1.1/README.md +222 -0
  9. pytest_warmup-0.1.1/docs/design.md +78 -0
  10. pytest_warmup-0.1.1/docs/publishing.md +61 -0
  11. pytest_warmup-0.1.1/docs/releases/0.1.1.md +59 -0
  12. pytest_warmup-0.1.1/examples/README.md +16 -0
  13. pytest_warmup-0.1.1/examples/autoresolve_usage.py +133 -0
  14. pytest_warmup-0.1.1/examples/basic_usage.py +123 -0
  15. pytest_warmup-0.1.1/examples/named_producer_usage.py +135 -0
  16. pytest_warmup-0.1.1/examples/warmup.snapshot.json +9 -0
  17. pytest_warmup-0.1.1/pyproject.toml +63 -0
  18. pytest_warmup-0.1.1/src/pytest_warmup/__init__.py +20 -0
  19. pytest_warmup-0.1.1/src/pytest_warmup/core.py +996 -0
  20. pytest_warmup-0.1.1/src/pytest_warmup/pytest_plugin.py +38 -0
  21. pytest_warmup-0.1.1/tests/__init__.py +1 -0
  22. pytest_warmup-0.1.1/tests/conftest.py +16 -0
  23. pytest_warmup-0.1.1/tests/snapshots/test_overrides.snapshot.json +26 -0
  24. pytest_warmup-0.1.1/tests/support/__init__.py +1 -0
  25. pytest_warmup-0.1.1/tests/support/demo_domain.py +119 -0
  26. pytest_warmup-0.1.1/tests/support/fake_external_api.py +72 -0
  27. pytest_warmup-0.1.1/tests/test_autoresolve_producer.py +236 -0
  28. pytest_warmup-0.1.1/tests/test_distributed_declarations.py +330 -0
  29. pytest_warmup-0.1.1/tests/test_end_to_end.py +76 -0
  30. pytest_warmup-0.1.1/tests/test_examples.py +41 -0
  31. pytest_warmup-0.1.1/tests/test_failfast.py +352 -0
  32. pytest_warmup-0.1.1/tests/test_indirect_chain.py +58 -0
  33. pytest_warmup-0.1.1/tests/test_overrides.py +107 -0
  34. pytest_warmup-0.1.1/tests/test_package_surface.py +16 -0
  35. pytest_warmup-0.1.1/tests/test_public_docstrings.py +16 -0
  36. pytest_warmup-0.1.1/tests/test_scope_mismatch.py +103 -0
  37. pytest_warmup-0.1.1/tests/test_snapshot_file.py +87 -0
@@ -0,0 +1,52 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ - master
8
+ pull_request:
9
+
10
+ jobs:
11
+ test:
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: ["3.13", "3.14"]
17
+
18
+ steps:
19
+ - name: Checkout
20
+ uses: actions/checkout@v4
21
+
22
+ - name: Set up Python
23
+ uses: actions/setup-python@v5
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+
27
+ - name: Upgrade pip
28
+ run: python -m pip install --upgrade pip
29
+
30
+ - name: Install package
31
+ run: python -m pip install -e ".[dev]"
32
+
33
+ - name: Run tests
34
+ run: pytest -q
35
+
36
+ - name: Build package
37
+ run: python -m build
38
+
39
+ - name: Check package metadata rendering
40
+ run: python -m twine check dist/*
41
+
42
+ - name: Smoke test built wheel
43
+ shell: bash
44
+ run: |
45
+ set -euo pipefail
46
+ tmpdir="$(mktemp -d)"
47
+ python -m venv "$tmpdir/.venv"
48
+ "$tmpdir/.venv/bin/python" -m pip install --upgrade pip
49
+ "$tmpdir/.venv/bin/python" -m pip install dist/pytest_warmup-*.whl pytest
50
+ cp examples/basic_usage.py "$tmpdir/test_basic_usage.py"
51
+ cp examples/warmup.snapshot.json "$tmpdir/warmup.snapshot.json"
52
+ (cd "$tmpdir" && "$tmpdir/.venv/bin/python" -m pytest -q)
@@ -0,0 +1,58 @@
1
+ name: Publish
2
+
3
+ on:
4
+ release:
5
+ types:
6
+ - published
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ contents: read
14
+
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.14"
23
+
24
+ - name: Upgrade pip
25
+ run: python -m pip install --upgrade pip
26
+
27
+ - name: Install build tooling
28
+ run: python -m pip install build twine
29
+
30
+ - name: Build package
31
+ run: python -m build
32
+
33
+ - name: Check package metadata rendering
34
+ run: python -m twine check dist/*
35
+
36
+ - name: Upload distributions
37
+ uses: actions/upload-artifact@v4
38
+ with:
39
+ name: python-package-distributions
40
+ path: dist/
41
+
42
+ publish-pypi:
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ environment:
46
+ name: pypi
47
+ permissions:
48
+ id-token: write
49
+
50
+ steps:
51
+ - name: Download distributions
52
+ uses: actions/download-artifact@v4
53
+ with:
54
+ name: python-package-distributions
55
+ path: dist/
56
+
57
+ - name: Publish to PyPI
58
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ __pycache__/
3
+ .pytest_cache/
4
+ *.pyc
5
+ .idea/
6
+ dist/
7
+ build/
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ## 0.1.1
6
+
7
+ - Initial public alpha extraction from the prototype workspace.
8
+ - Explicit `WarmupPlan.require(...)` declaration model.
9
+ - `@warmup_param(...)` injection for fixtures and test functions.
10
+ - Optional producer conveniences through `producer_fixture="..."` and a project-local `warmup_autoresolve_producer` fixture.
11
+ - File-based snapshot overrides through `prepare(snapshot_file=...)`.
12
+ - Initial validation coverage for cycles, producer-chain errors, distributed declarations, snapshot file failures, and package surface boundaries.
13
+ - Public-package hygiene: executable examples, built-wheel smoke testing, and `twine check` validation.
14
+ - Trusted Publishing workflow and release documentation for GitHub Actions based PyPI publication.
@@ -0,0 +1,52 @@
1
+ # Contributing
2
+
3
+ This repository is still in an early alpha phase. The most important contribution rule is to keep the public API narrow and explicit.
4
+
5
+ ## Development Setup
6
+
7
+ ```bash
8
+ uv venv .venv
9
+ uv pip install --python .venv/bin/python -e ".[dev]"
10
+ ```
11
+
12
+ ## Verification
13
+
14
+ Run the local checks before proposing changes:
15
+
16
+ ```bash
17
+ ./.venv/bin/python -m pytest -q
18
+ ./.venv/bin/python -m build
19
+ ./.venv/bin/python -m twine check dist/*
20
+ ```
21
+
22
+ Before publishing or cutting a release candidate, also do one external-user smoke check from the built wheel: create a fresh virtual environment, install the wheel from `dist/`, and run one of the example tests without editable-install path hacks.
23
+
24
+ For the repository-side release flow, see [`docs/publishing.md`](docs/publishing.md).
25
+
26
+ ## Scope Rules
27
+
28
+ Changes are welcome, but the package should stay focused on:
29
+
30
+ - requirement declaration through `WarmupPlan.require(...)`;
31
+ - explicit producer preparation through `warmup_mgr.use(...).prepare(...)`;
32
+ - injection through `@warmup_param(...)`;
33
+ - file-based snapshot overrides;
34
+ - batch preparation and distribution of expensive test resources.
35
+
36
+ The package should not grow into:
37
+
38
+ - a general factory framework;
39
+ - a generic snapshot assertion library;
40
+ - a service/container orchestration framework;
41
+ - a domain-specific toolkit.
42
+
43
+ ## Public API Hygiene
44
+
45
+ Before widening the public surface, check whether the change can stay internal instead.
46
+
47
+ In particular, keep these internal unless there is a strong reason otherwise:
48
+
49
+ - graph normalization helpers;
50
+ - runtime/store implementation details;
51
+ - test-support demo plans;
52
+ - fake external API helpers.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Timothy Rabbit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,269 @@
1
+ Metadata-Version: 2.4
2
+ Name: pytest-warmup
3
+ Version: 0.1.1
4
+ Summary: Batch preparation and distribution of expensive test resources for pytest.
5
+ Project-URL: Homepage, https://github.com/kaor4bp/pytest-warmup
6
+ Project-URL: Repository, https://github.com/kaor4bp/pytest-warmup
7
+ Project-URL: Issues, https://github.com/kaor4bp/pytest-warmup/issues
8
+ Author: OpenAI Codex collaboration
9
+ License: MIT License
10
+
11
+ Copyright (c) 2026 Timothy Rabbit
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: batch,fixtures,pytest,pytest-plugin,testing,warmup
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Framework :: Pytest
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.13
38
+ Classifier: Programming Language :: Python :: 3.14
39
+ Classifier: Topic :: Software Development :: Testing
40
+ Requires-Python: >=3.13
41
+ Requires-Dist: pytest>=8.4
42
+ Provides-Extra: dev
43
+ Requires-Dist: build>=1.2; extra == 'dev'
44
+ Requires-Dist: pytest>=8.4; extra == 'dev'
45
+ Requires-Dist: twine>=6.1; extra == 'dev'
46
+ Description-Content-Type: text/markdown
47
+
48
+ # pytest-warmup
49
+
50
+ `pytest-warmup` is a pytest plugin for batch preparation and distribution of expensive test resources.
51
+
52
+ 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.
53
+
54
+ Typical cases:
55
+
56
+ - creating external-domain objects in batches before a module or session runs;
57
+ - waiting for synchronization, indexing, or propagation after creation;
58
+ - reusing one prepared upstream object across multiple tests;
59
+ - creating per-test instances only where the declaration explicitly asks for it;
60
+ - replacing selected prepared values from a snapshot file for debugging.
61
+
62
+ ## Installation
63
+
64
+ ```bash
65
+ pip install pytest-warmup
66
+ ```
67
+
68
+ ## Public API
69
+
70
+ This package is intentionally narrow:
71
+
72
+ - `WarmupPlan`
73
+ - `WarmupRequirement`
74
+ - `WarmupError`
75
+ - `@warmup_param(...)`
76
+ - `warmup_mgr.use(...).prepare(...)`
77
+
78
+ ## Quick Start
79
+
80
+ Declare resource requirements in plan classes:
81
+
82
+ ```python
83
+ from pytest_warmup import WarmupPlan, WarmupRequirement
84
+
85
+
86
+ class ProfilePlan(WarmupPlan):
87
+ def require(
88
+ self,
89
+ *,
90
+ profile_name: str,
91
+ id: str | None = None,
92
+ is_per_test: bool | None = None,
93
+ ) -> WarmupRequirement:
94
+ return super().require(
95
+ payload={"profile_name": profile_name},
96
+ dependencies={},
97
+ id=id,
98
+ is_per_test=is_per_test,
99
+ )
100
+
101
+ def prepare(self, nodes, runtime) -> None:
102
+ for node in nodes:
103
+ runtime.set(
104
+ node,
105
+ {
106
+ "profile_id": f"profile-{node.payload['profile_name']}",
107
+ "profile_name": node.payload["profile_name"],
108
+ },
109
+ )
110
+ ```
111
+
112
+ Build requirements from those plans:
113
+
114
+ ```python
115
+ profile = ProfilePlan("profile")
116
+ profile_main = profile.require(profile_name="main", id="profile_main")
117
+ ```
118
+
119
+ Create one explicit producer fixture:
120
+
121
+ ```python
122
+ import pytest
123
+
124
+
125
+ @pytest.fixture(scope="module")
126
+ def prepare_data(warmup_mgr):
127
+ return warmup_mgr.use(profile).prepare()
128
+ ```
129
+
130
+ Inject the prepared resource into a test or fixture:
131
+
132
+ ```python
133
+ from pytest_warmup import warmup_param
134
+
135
+
136
+ @warmup_param("prepared_profile", profile_main)
137
+ def test_profile(prepare_data, prepared_profile):
138
+ assert prepared_profile["profile_id"].startswith("profile-")
139
+ ```
140
+
141
+ `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.
142
+
143
+ For full runnable examples, see:
144
+
145
+ - [`examples/basic_usage.py`](examples/basic_usage.py)
146
+ - [`examples/autoresolve_usage.py`](examples/autoresolve_usage.py) for fixture-side autoresolve binding
147
+ - [`examples/named_producer_usage.py`](examples/named_producer_usage.py)
148
+
149
+ ## Producer Patterns
150
+
151
+ The default model stays explicit: a test or fixture depends on a producer fixture in the ordinary pytest dependency chain.
152
+
153
+ Recommended order:
154
+
155
+ 1. use an explicit producer argument for the default, most readable path;
156
+ 2. use `warmup_autoresolve_producer` when you want less producer boilerplate but still keep one clearly defined producer seam;
157
+ 3. use `producer_fixture="..."` only to disambiguate between producers that are already present in the pytest dependency chain.
158
+
159
+ Producer resolution rules:
160
+
161
+ 1. if `producer_fixture="..."` is provided, that fixture must already be part of the pytest dependency chain and is used as the producer;
162
+ 2. otherwise, if the dependency chain already contains exactly one prepared producer, that producer is used;
163
+ 3. otherwise, `warmup_autoresolve_producer` is used as a narrow fallback if it exists;
164
+ 4. otherwise, producer resolution fails fast.
165
+
166
+ 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.
167
+
168
+ Example of the fallback fixture:
169
+
170
+ ```python
171
+ @pytest.fixture
172
+ def warmup_autoresolve_producer(prepare_data):
173
+ return prepare_data
174
+
175
+
176
+ @pytest.fixture
177
+ @warmup_param("prepared_profile", profile_main)
178
+ def prepared_profile_fixture(prepared_profile):
179
+ return prepared_profile
180
+ ```
181
+
182
+ ## Snapshot File Overrides
183
+
184
+ Debug replacement is file-based through `prepare(snapshot_file=...)`.
185
+
186
+ The current JSON shape is:
187
+
188
+ ```json
189
+ {
190
+ "shared": {
191
+ "profile_main": {
192
+ "profile_id": "debug-profile"
193
+ }
194
+ },
195
+ "tests": {
196
+ "tests/test_module.py::test_case": {
197
+ "items_alpha": {
198
+ "items_id": "debug-items"
199
+ }
200
+ }
201
+ }
202
+ }
203
+ ```
204
+
205
+ Rules:
206
+
207
+ - shared nodes are addressed by `id`;
208
+ - per-test nodes are addressed by `tests[nodeid][id]`;
209
+ - declarations that are effectively per-test may not be overridden through `shared`.
210
+
211
+ ## Troubleshooting
212
+
213
+ Common producer-resolution errors usually mean one of these:
214
+
215
+ - `no producer fixture found in pytest dependency chain ...`
216
+ The decorated test or fixture is not connected to any producer fixture, and no `warmup_autoresolve_producer` fallback exists.
217
+ - `multiple producer fixtures found in pytest dependency chain`
218
+ The current dependency chain exposes more than one prepared producer. Simplify the chain or use `producer_fixture="..."` to pick one explicitly.
219
+ - `producer fixture '...' is not in this dependency chain`
220
+ The named producer exists, but the current test or fixture does not depend on it through ordinary pytest wiring.
221
+ - `producer fixture '...' must return a prepared warmup scope`
222
+ The selected fixture returned an ordinary value instead of the prepared scope returned by `warmup_mgr.use(...).prepare(...)`.
223
+ - `... cannot be shared because dependency ... is per-test`
224
+ A shared declaration depends on a branch that is effectively per-test. Either inherit that branch or split the declaration differently.
225
+
226
+ ## Scope Boundary
227
+
228
+ `pytest-warmup` is not trying to be:
229
+
230
+ - a general-purpose factory framework;
231
+ - a generic snapshot assertion library;
232
+ - a container or infrastructure manager;
233
+ - a hidden autouse preparation layer;
234
+ - a domain-specific toolkit.
235
+
236
+ It focuses on one problem: batch creation and targeted distribution of expensive test resources.
237
+
238
+ Further design details live in:
239
+
240
+ - [`docs/design.md`](docs/design.md)
241
+ - [`docs/publishing.md`](docs/publishing.md)
242
+ - [`examples/README.md`](examples/README.md)
243
+ - [`CONTRIBUTING.md`](CONTRIBUTING.md)
244
+ - [`CHANGELOG.md`](CHANGELOG.md)
245
+
246
+ ## Development
247
+
248
+ ```bash
249
+ uv venv .venv
250
+ uv pip install --python .venv/bin/python -e ".[dev]"
251
+ ./.venv/bin/python -m pytest -q
252
+ ./.venv/bin/python -m build
253
+ ```
254
+
255
+ Before publishing, also do one smoke check from the built wheel in a fresh virtual environment.
256
+
257
+ ## Attribution
258
+
259
+ The initial code, tests, and documentation in this repository were generated and iteratively refined with ChatGPT/Codex plus collaborating agents.
260
+
261
+ Named collaborating agents from the design and spike process:
262
+
263
+ - 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.
264
+ - 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.
265
+ - 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.
266
+ - 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.
267
+ - 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.
268
+
269
+ All generated material still requires human review. The repository treats generated output as draft engineering work, not as an authority.