pytest-gpu-proof 0.4.0__tar.gz → 0.5.0__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.
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/.github/workflows/ci.yml +1 -1
- pytest_gpu_proof-0.5.0/.github/workflows/docs.yml +69 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/.gitignore +3 -1
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/CHANGELOG.md +61 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/CONTRIBUTING.md +1 -1
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/PKG-INFO +34 -10
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/README.md +28 -6
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/docs/architecture.md +9 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/docs/ci_gpu_mode.md +4 -1
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/docs/index.md +4 -1
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/docs/policy.md +2 -2
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/docs/security_model.md +7 -3
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/docs/sharding.md +18 -4
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/mkdocs.yml +2 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/pyproject.toml +6 -4
- pytest_gpu_proof-0.5.0/requirements.txt +3 -0
- pytest_gpu_proof-0.5.0/src/pytest_gpu_proof/__init__.py +1 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/src/pytest_gpu_proof/cli.py +108 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/src/pytest_gpu_proof/config.py +13 -3
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/src/pytest_gpu_proof/gitutils.py +9 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/src/pytest_gpu_proof/merge.py +131 -3
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/src/pytest_gpu_proof/plugin.py +23 -1
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/src/pytest_gpu_proof/receipt.py +55 -8
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/src/pytest_gpu_proof/verify.py +58 -15
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/tests/conftest.py +13 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/tests/test_cli.py +23 -4
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/tests/test_config.py +37 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/tests/test_merge.py +24 -3
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/tests/test_receipt.py +76 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/tests/test_sharding.py +230 -1
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/tests/test_verifier.py +94 -3
- pytest_gpu_proof-0.5.0/website/README.md +85 -0
- pytest_gpu_proof-0.5.0/website/build_site.py +82 -0
- pytest_gpu_proof-0.5.0/website/check_site.py +90 -0
- pytest_gpu_proof-0.5.0/website/favicon.svg +1 -0
- pytest_gpu_proof-0.5.0/website/index.html +127 -0
- pytest_gpu_proof-0.5.0/website/main.js +17 -0
- pytest_gpu_proof-0.5.0/website/overrides/main.html +4 -0
- pytest_gpu_proof-0.5.0/website/provenance.json +10 -0
- pytest_gpu_proof-0.5.0/website/requirements.txt +2 -0
- pytest_gpu_proof-0.5.0/website/style.css +67 -0
- pytest_gpu_proof-0.5.0/website/test_site.py +89 -0
- pytest_gpu_proof-0.5.0/website/workflow-mobile.svg +32 -0
- pytest_gpu_proof-0.5.0/website/workflow.svg +35 -0
- pytest_gpu_proof-0.4.0/.github/workflows/docs.yml +0 -29
- pytest_gpu_proof-0.4.0/requirements.txt +0 -2
- pytest_gpu_proof-0.4.0/src/pytest_gpu_proof/__init__.py +0 -1
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/.github/workflows/publish-testpypi.yml +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/.github/workflows/publish.yml +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/AGENTS.md +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/CLAUDE.md +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/LICENSE +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/RELEASING.md +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/ROADMAP.md +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/SECURITY.md +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/docs/landscape.md +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/docs/local_mode.md +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/docs/quickstart.md +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_ctypes_matmul/Makefile +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_ctypes_matmul/README.md +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_ctypes_matmul/cuda_ctypes_matmul.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_ctypes_matmul/matmul_kernel.cu +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_ctypes_matmul/test_cuda_ctypes_matmul.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_nanobind_matmul/CMakeLists.txt +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_nanobind_matmul/README.md +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_nanobind_matmul/src/bindings.cpp +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_nanobind_matmul/src/matmul_cuda.h +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_nanobind_matmul/src/matmul_kernel.cu +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_nanobind_matmul/test_cuda_nanobind_matmul.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_pybind11_matmul/CMakeLists.txt +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_pybind11_matmul/README.md +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_pybind11_matmul/src/bindings.cpp +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_pybind11_matmul/src/matmul_cuda.h +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_pybind11_matmul/src/matmul_kernel.cu +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/cuda_pybind11_matmul/test_cuda_pybind11_matmul.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/github_gpu_runner/.github/workflows/gpu-test.yml +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/jax_ffi_cuda_matmul/CMakeLists.txt +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/jax_ffi_cuda_matmul/README.md +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/jax_ffi_cuda_matmul/jax_ffi_matmul.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/jax_ffi_cuda_matmul/src/matmul_ffi.cu +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/jax_ffi_cuda_matmul/test_jax_ffi_cuda_matmul.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/local_receipt_verify/.github/workflows/verify.yml +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/minimal_python_only/conftest.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/minimal_python_only/test_minimal.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/wrapped_cuda_ctypes/fake_cuda_lib.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/examples/wrapped_cuda_ctypes/test_cuda_wrapper.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/install.sh +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/requirements-dev.txt +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/src/pytest_gpu_proof/__main__.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/src/pytest_gpu_proof/compare.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/src/pytest_gpu_proof/fingerprint.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/src/pytest_gpu_proof/signers/__init__.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/src/pytest_gpu_proof/signers/base.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/src/pytest_gpu_proof/signers/ed25519.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/tests/test_compare.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/tests/test_fingerprint.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/tests/test_gitutils.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/tests/test_plugin_capture.py +0 -0
- {pytest_gpu_proof-0.4.0 → pytest_gpu_proof-0.5.0}/tests/test_signing.py +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
paths:
|
|
8
|
+
- 'docs/**'
|
|
9
|
+
- 'website/**'
|
|
10
|
+
- 'mkdocs.yml'
|
|
11
|
+
- '.github/workflows/docs.yml'
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: docs-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: false
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
build:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- uses: actions/setup-python@v5
|
|
27
|
+
with:
|
|
28
|
+
python-version: "3.12"
|
|
29
|
+
|
|
30
|
+
- name: Install website dependencies
|
|
31
|
+
run: pip install -r website/requirements.txt
|
|
32
|
+
|
|
33
|
+
- name: Build and check the complete website
|
|
34
|
+
run: python website/build_site.py
|
|
35
|
+
|
|
36
|
+
- name: Test website checks
|
|
37
|
+
run: python -m unittest discover -s website -p 'test_*.py'
|
|
38
|
+
|
|
39
|
+
- uses: actions/upload-artifact@v4
|
|
40
|
+
with:
|
|
41
|
+
name: website-preview
|
|
42
|
+
path: build/website
|
|
43
|
+
include-hidden-files: true
|
|
44
|
+
|
|
45
|
+
deploy:
|
|
46
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
47
|
+
needs: build
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
permissions:
|
|
50
|
+
contents: write
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v4
|
|
53
|
+
with:
|
|
54
|
+
fetch-depth: 0
|
|
55
|
+
- uses: actions/setup-python@v5
|
|
56
|
+
with:
|
|
57
|
+
python-version: "3.12"
|
|
58
|
+
- run: pip install -r website/requirements.txt
|
|
59
|
+
- uses: actions/download-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: website-preview
|
|
62
|
+
path: build/website
|
|
63
|
+
- name: Configure git author
|
|
64
|
+
run: |
|
|
65
|
+
git config user.name "github-actions[bot]"
|
|
66
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
67
|
+
|
|
68
|
+
- name: Deploy to gh-pages
|
|
69
|
+
run: ghp-import --no-jekyll --push --branch gh-pages build/website
|
|
@@ -4,6 +4,67 @@ All notable changes to pytest-gpu-proof are documented here. The format is
|
|
|
4
4
|
based on [Keep a Changelog](https://keepachangelog.com/); versions follow
|
|
5
5
|
[SemVer](https://semver.org/) (pre-1.0: minor bumps may break).
|
|
6
6
|
|
|
7
|
+
## [0.5.0] — 2026-10-28
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Python 3.10 support with a conditional `tomli` dependency and a CI test lane.
|
|
12
|
+
- Signed `environment.nvcc_version` records the stripped output of
|
|
13
|
+
`nvcc --version` from PATH, or null when unavailable. Fresh shard merges
|
|
14
|
+
require matching compiler metadata; existing receipts without the field
|
|
15
|
+
remain supported.
|
|
16
|
+
|
|
17
|
+
## [0.4.1] — 2026-10-12
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Shard entries record `dirty` (the recording tree's state); merge ORs shard
|
|
22
|
+
flags into the merged `repo.dirty`, carry-forward refuses a dirty shard, and
|
|
23
|
+
verification refuses any dirty shard unless `allow_dirty` is set.
|
|
24
|
+
- `gpu-proof merge --carry-from` refuses a source receipt recorded with a dirty
|
|
25
|
+
working tree. The merged `repo.dirty` is OR-ed over the fresh inputs only, so a
|
|
26
|
+
carried shard from a dirty recording was laundered into a clean-looking merged
|
|
27
|
+
receipt (found by GRiD's split driver, 2026-10-10). Carried shards now record
|
|
28
|
+
`carried.original_dirty`, and verification refuses a carried shard recorded
|
|
29
|
+
dirty unless `allow_dirty` is set.
|
|
30
|
+
|
|
31
|
+
- The expected-skips mismatch names the delta (unexpected skips and stale
|
|
32
|
+
baseline entries) instead of only reporting that the sets differ.
|
|
33
|
+
- The receipt's `environment.gpu_info` falls back to the same torch probe the
|
|
34
|
+
`gpu_required` marker uses when nvidia-smi is absent or fails (`source`
|
|
35
|
+
records which probe answered), so a torch-only box no longer records
|
|
36
|
+
`gpu_info: null` and then fails a `require_gpu` policy.
|
|
37
|
+
|
|
38
|
+
- Verification refuses a receipt whose session started more than five
|
|
39
|
+
minutes before its commit existed (committer time): a cheap replay and
|
|
40
|
+
forgery guard that needs no nonce.
|
|
41
|
+
|
|
42
|
+
- `mode: ci-gpu` is now bound to the signing key: a ci-gpu receipt verifies
|
|
43
|
+
only when the policy lists `allowed_key_fingerprints` and the receipt was
|
|
44
|
+
signed by one of them (previously the mode was an unverified label).
|
|
45
|
+
- Without an explicit `carried_max_age_days`, a carried shard's original run
|
|
46
|
+
may not be older than the receipt's `max_age_days` (was a fixed 30 days).
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- `gpu-proof merge --verify-inputs`: authenticate every input shard against
|
|
51
|
+
its signer's current GitHub keys before merging (unsigned or tampered inputs
|
|
52
|
+
are refused).
|
|
53
|
+
- `gpu-proof status PATH...`: one-line summaries of receipt files or
|
|
54
|
+
directories (commit, dirty, signed, outcome, counts, shards, carried).
|
|
55
|
+
- `gpu-proof durations RECEIPT... --out map.json`: fold per-test durations
|
|
56
|
+
into a JSON map for shard bin-packing.
|
|
57
|
+
- Command-line equivalents for the toml-only options:
|
|
58
|
+
`--gpu-proof-no-gpu-uuid`, `--gpu-proof-require-gpu`,
|
|
59
|
+
`--gpu-proof-max-age-days N`.
|
|
60
|
+
- `gpu-proof plan --receipt OLD [--repo .] [--json]`: a dry run of the
|
|
61
|
+
carry-forward conditions against the current tree, reporting per shard
|
|
62
|
+
whether it could be carried or must be re-run (and why). Drivers that plan
|
|
63
|
+
partial re-runs no longer reimplement the checks.
|
|
64
|
+
- `record_gpu_uuid` (`[tool.gpu_proof]`, default `true`): set `false` to omit each GPU's
|
|
65
|
+
nvidia-smi UUID from the receipt environment while keeping name, driver, memory and
|
|
66
|
+
compute capability. Verification does not read the UUID.
|
|
67
|
+
|
|
7
68
|
## [0.4.0] — 2026-08-17
|
|
8
69
|
|
|
9
70
|
### Added
|
|
@@ -19,7 +19,7 @@ python -m build
|
|
|
19
19
|
python -m twine check --strict dist/*
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
CI runs the test suite on Python 3.11, 3.12, and 3.13, independently enforces
|
|
22
|
+
CI runs the test suite on Python 3.10, 3.11, 3.12, and 3.13, independently enforces
|
|
23
23
|
100% line and branch coverage, builds docs strictly, and smoke-tests the wheel.
|
|
24
24
|
|
|
25
25
|
## Change expectations
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-gpu-proof
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: pytest plugin for GPU equivalence testing with signed receipts verified via GitHub SSH keys
|
|
5
|
-
Project-URL: Homepage, https://
|
|
6
|
-
Project-URL: Documentation, https://a2r-lab.
|
|
5
|
+
Project-URL: Homepage, https://a2r-lab.org/pytest-gpu-proof/
|
|
6
|
+
Project-URL: Documentation, https://a2r-lab.org/pytest-gpu-proof/docs/
|
|
7
7
|
Project-URL: Repository, https://github.com/A2R-Lab/pytest-gpu-proof
|
|
8
8
|
Project-URL: Issues, https://github.com/A2R-Lab/pytest-gpu-proof/issues
|
|
9
9
|
Project-URL: Changelog, https://github.com/A2R-Lab/pytest-gpu-proof/blob/main/CHANGELOG.md
|
|
@@ -16,14 +16,16 @@ Classifier: Intended Audience :: Developers
|
|
|
16
16
|
Classifier: License :: OSI Approved :: MIT License
|
|
17
17
|
Classifier: Operating System :: OS Independent
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
23
|
Classifier: Topic :: Security :: Cryptography
|
|
23
24
|
Classifier: Topic :: Software Development :: Testing
|
|
24
|
-
Requires-Python: >=3.
|
|
25
|
+
Requires-Python: >=3.10
|
|
25
26
|
Requires-Dist: cryptography>=41.0
|
|
26
27
|
Requires-Dist: pytest>=7.0
|
|
28
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
27
29
|
Provides-Extra: dev
|
|
28
30
|
Requires-Dist: build>=1.2; extra == 'dev'
|
|
29
31
|
Requires-Dist: coverage[toml]>=7.6; extra == 'dev'
|
|
@@ -39,13 +41,17 @@ Description-Content-Type: text/markdown
|
|
|
39
41
|
# pytest-gpu-proof
|
|
40
42
|
|
|
41
43
|
[](https://github.com/A2R-Lab/pytest-gpu-proof/actions/workflows/ci.yml)
|
|
42
|
-
[](https://a2r-lab.
|
|
44
|
+
[](https://a2r-lab.org/pytest-gpu-proof/docs/)
|
|
43
45
|
[](https://pypi.org/project/pytest-gpu-proof/)
|
|
44
46
|
[](https://pypi.org/project/pytest-gpu-proof/)
|
|
45
47
|
[](https://github.com/A2R-Lab/pytest-gpu-proof/actions/workflows/ci.yml)
|
|
46
48
|
|
|
47
49
|
Signed pytest receipts for local GPU runs, verified in CPU-only CI.
|
|
48
50
|
|
|
51
|
+
[Project overview](https://a2r-lab.org/pytest-gpu-proof/) ·
|
|
52
|
+
[Documentation](https://a2r-lab.org/pytest-gpu-proof/docs/) ·
|
|
53
|
+
[Website preview instructions](website/README.md)
|
|
54
|
+
|
|
49
55
|
`pytest-gpu-proof` records which marked tests ran, their outcomes, the Git
|
|
50
56
|
commit and source fingerprint, the environment, and the run time. It signs
|
|
51
57
|
that payload with an existing SSH key. A normal CPU runner can then verify the
|
|
@@ -86,7 +92,7 @@ cd pytest-gpu-proof
|
|
|
86
92
|
python -m pip install -e ".[dev]"
|
|
87
93
|
```
|
|
88
94
|
|
|
89
|
-
Requirements: Python 3.
|
|
95
|
+
Requirements: Python 3.10+, pytest 7+, and `cryptography` 41+.
|
|
90
96
|
|
|
91
97
|
## Quick start
|
|
92
98
|
|
|
@@ -126,15 +132,23 @@ fingerprint scope, no selected tests, or write failure makes pytest fail and
|
|
|
126
132
|
leaves no stale receipt behind. `--gpu-proof-best-effort` is an explicit
|
|
127
133
|
development-only opt-out.
|
|
128
134
|
|
|
129
|
-
##
|
|
135
|
+
## Five interfaces
|
|
130
136
|
|
|
131
|
-
The project exposes
|
|
137
|
+
The project exposes five small interfaces:
|
|
132
138
|
|
|
133
139
|
1. **Markers** select receipt tests.
|
|
134
|
-
2. **`gpu_proof_check`** compares a reference callable with a candidate
|
|
140
|
+
2. **`gpu_proof_check`** compares a reference callable with a candidate
|
|
141
|
+
(optional; every current user selects tests with the marker alone).
|
|
135
142
|
3. **`gpu-proof verify`** validates a receipt and repository policy.
|
|
136
143
|
4. **`gpu-proof merge`** combines separately executed shards and optionally
|
|
137
144
|
carries unchanged shards forward.
|
|
145
|
+
5. **`gpu-proof plan`** reports, before a partial re-run, which shards of an
|
|
146
|
+
old receipt could still be carried and which must be re-run.
|
|
147
|
+
|
|
148
|
+
Two maintenance helpers round it out: `gpu-proof status PATH...` summarizes
|
|
149
|
+
receipt files (commit, dirty, signed, outcome, counts, shards), and
|
|
150
|
+
`gpu-proof durations RECEIPT... --out map.json` folds per-test durations into
|
|
151
|
+
a map that shard bin-packers can calibrate from.
|
|
138
152
|
|
|
139
153
|
### Markers
|
|
140
154
|
|
|
@@ -197,6 +211,7 @@ fingerprint_excluded_paths = ["gpu-proof.json"]
|
|
|
197
211
|
required_marker = "gpu_proof"
|
|
198
212
|
max_age_days = 30
|
|
199
213
|
require_gpu = true
|
|
214
|
+
record_gpu_uuid = false # keep device name/driver/memory, omit the card's UUID
|
|
200
215
|
```
|
|
201
216
|
|
|
202
217
|
By default, the fingerprint covers every tracked file in the repository,
|
|
@@ -304,6 +319,15 @@ runtime environments, duplicate tests, and duplicate shard names. It records
|
|
|
304
319
|
input provenance but does not verify each input signature; the merger signs
|
|
305
320
|
for the union. See [sharding and carry-forward](docs/sharding.md).
|
|
306
321
|
|
|
322
|
+
Carry-forward (`gpu-proof merge ... --carry-from last-green.json`) grafts an
|
|
323
|
+
absent old shard only when its receipt commit is an ancestor of the current
|
|
324
|
+
one, both the receipt and the shard were recorded with a clean tree, its
|
|
325
|
+
narrow fingerprint recomputes identically, and it introduces no duplicate
|
|
326
|
+
test. `gpu-proof plan --receipt last-green.json` prints the same verdicts
|
|
327
|
+
without merging. Generated inputs named through `fingerprint_extra_paths`
|
|
328
|
+
should be gitignored: an untracked generated file makes the recording tree
|
|
329
|
+
dirty, and dirty recordings are never carried.
|
|
330
|
+
|
|
307
331
|
## Receipt schema
|
|
308
332
|
|
|
309
333
|
New receipts use schema `"3"`. Signer identity is inside the signed payload,
|
|
@@ -322,7 +346,7 @@ python -m build
|
|
|
322
346
|
python -m twine check --strict dist/*
|
|
323
347
|
```
|
|
324
348
|
|
|
325
|
-
CI tests Python 3.
|
|
349
|
+
CI tests Python 3.10–3.13 and enforces 100% line and branch coverage. See
|
|
326
350
|
[CONTRIBUTING.md](CONTRIBUTING.md), [SECURITY.md](SECURITY.md), and
|
|
327
351
|
[RELEASING.md](RELEASING.md).
|
|
328
352
|
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
# pytest-gpu-proof
|
|
2
2
|
|
|
3
3
|
[](https://github.com/A2R-Lab/pytest-gpu-proof/actions/workflows/ci.yml)
|
|
4
|
-
[](https://a2r-lab.
|
|
4
|
+
[](https://a2r-lab.org/pytest-gpu-proof/docs/)
|
|
5
5
|
[](https://pypi.org/project/pytest-gpu-proof/)
|
|
6
6
|
[](https://pypi.org/project/pytest-gpu-proof/)
|
|
7
7
|
[](https://github.com/A2R-Lab/pytest-gpu-proof/actions/workflows/ci.yml)
|
|
8
8
|
|
|
9
9
|
Signed pytest receipts for local GPU runs, verified in CPU-only CI.
|
|
10
10
|
|
|
11
|
+
[Project overview](https://a2r-lab.org/pytest-gpu-proof/) ·
|
|
12
|
+
[Documentation](https://a2r-lab.org/pytest-gpu-proof/docs/) ·
|
|
13
|
+
[Website preview instructions](website/README.md)
|
|
14
|
+
|
|
11
15
|
`pytest-gpu-proof` records which marked tests ran, their outcomes, the Git
|
|
12
16
|
commit and source fingerprint, the environment, and the run time. It signs
|
|
13
17
|
that payload with an existing SSH key. A normal CPU runner can then verify the
|
|
@@ -48,7 +52,7 @@ cd pytest-gpu-proof
|
|
|
48
52
|
python -m pip install -e ".[dev]"
|
|
49
53
|
```
|
|
50
54
|
|
|
51
|
-
Requirements: Python 3.
|
|
55
|
+
Requirements: Python 3.10+, pytest 7+, and `cryptography` 41+.
|
|
52
56
|
|
|
53
57
|
## Quick start
|
|
54
58
|
|
|
@@ -88,15 +92,23 @@ fingerprint scope, no selected tests, or write failure makes pytest fail and
|
|
|
88
92
|
leaves no stale receipt behind. `--gpu-proof-best-effort` is an explicit
|
|
89
93
|
development-only opt-out.
|
|
90
94
|
|
|
91
|
-
##
|
|
95
|
+
## Five interfaces
|
|
92
96
|
|
|
93
|
-
The project exposes
|
|
97
|
+
The project exposes five small interfaces:
|
|
94
98
|
|
|
95
99
|
1. **Markers** select receipt tests.
|
|
96
|
-
2. **`gpu_proof_check`** compares a reference callable with a candidate
|
|
100
|
+
2. **`gpu_proof_check`** compares a reference callable with a candidate
|
|
101
|
+
(optional; every current user selects tests with the marker alone).
|
|
97
102
|
3. **`gpu-proof verify`** validates a receipt and repository policy.
|
|
98
103
|
4. **`gpu-proof merge`** combines separately executed shards and optionally
|
|
99
104
|
carries unchanged shards forward.
|
|
105
|
+
5. **`gpu-proof plan`** reports, before a partial re-run, which shards of an
|
|
106
|
+
old receipt could still be carried and which must be re-run.
|
|
107
|
+
|
|
108
|
+
Two maintenance helpers round it out: `gpu-proof status PATH...` summarizes
|
|
109
|
+
receipt files (commit, dirty, signed, outcome, counts, shards), and
|
|
110
|
+
`gpu-proof durations RECEIPT... --out map.json` folds per-test durations into
|
|
111
|
+
a map that shard bin-packers can calibrate from.
|
|
100
112
|
|
|
101
113
|
### Markers
|
|
102
114
|
|
|
@@ -159,6 +171,7 @@ fingerprint_excluded_paths = ["gpu-proof.json"]
|
|
|
159
171
|
required_marker = "gpu_proof"
|
|
160
172
|
max_age_days = 30
|
|
161
173
|
require_gpu = true
|
|
174
|
+
record_gpu_uuid = false # keep device name/driver/memory, omit the card's UUID
|
|
162
175
|
```
|
|
163
176
|
|
|
164
177
|
By default, the fingerprint covers every tracked file in the repository,
|
|
@@ -266,6 +279,15 @@ runtime environments, duplicate tests, and duplicate shard names. It records
|
|
|
266
279
|
input provenance but does not verify each input signature; the merger signs
|
|
267
280
|
for the union. See [sharding and carry-forward](docs/sharding.md).
|
|
268
281
|
|
|
282
|
+
Carry-forward (`gpu-proof merge ... --carry-from last-green.json`) grafts an
|
|
283
|
+
absent old shard only when its receipt commit is an ancestor of the current
|
|
284
|
+
one, both the receipt and the shard were recorded with a clean tree, its
|
|
285
|
+
narrow fingerprint recomputes identically, and it introduces no duplicate
|
|
286
|
+
test. `gpu-proof plan --receipt last-green.json` prints the same verdicts
|
|
287
|
+
without merging. Generated inputs named through `fingerprint_extra_paths`
|
|
288
|
+
should be gitignored: an untracked generated file makes the recording tree
|
|
289
|
+
dirty, and dirty recordings are never carried.
|
|
290
|
+
|
|
269
291
|
## Receipt schema
|
|
270
292
|
|
|
271
293
|
New receipts use schema `"3"`. Signer identity is inside the signed payload,
|
|
@@ -284,7 +306,7 @@ python -m build
|
|
|
284
306
|
python -m twine check --strict dist/*
|
|
285
307
|
```
|
|
286
308
|
|
|
287
|
-
CI tests Python 3.
|
|
309
|
+
CI tests Python 3.10–3.13 and enforces 100% line and branch coverage. See
|
|
288
310
|
[CONTRIBUTING.md](CONTRIBUTING.md), [SECURITY.md](SECURITY.md), and
|
|
289
311
|
[RELEASING.md](RELEASING.md).
|
|
290
312
|
|
|
@@ -69,6 +69,15 @@ canonical = json.dumps(
|
|
|
69
69
|
Putting signer metadata inside this payload prevents identity or algorithm
|
|
70
70
|
substitution after signing.
|
|
71
71
|
|
|
72
|
+
The optional `environment.nvcc_version` field records the stripped stdout of
|
|
73
|
+
`nvcc --version` resolved through PATH. It is null if the executable is missing,
|
|
74
|
+
cannot run, times out after ten seconds, fails, or returns empty/undecodable output.
|
|
75
|
+
This is self-reported compiler availability, not evidence that this compiler built
|
|
76
|
+
the tested code. It is independent of GPU detection and requires no GPU.
|
|
77
|
+
Existing schema-3 receipts without the field still verify. Fresh shard merges
|
|
78
|
+
require matching values (missing and null are equivalent); carried shards retain
|
|
79
|
+
their original environment metadata.
|
|
80
|
+
|
|
72
81
|
## Fingerprint manifest
|
|
73
82
|
|
|
74
83
|
`sha256-manifest-v2` enumerates stage-0 Git index entries. Regular files bind
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
CI-GPU mode uses the same receipt and verifier but records `mode: ci-gpu`.
|
|
4
4
|
Choose it when GPU tests must run on a controlled runner rather than a
|
|
5
|
-
developer machine.
|
|
5
|
+
developer machine. The verifier binds the mode to the runner's signing key:
|
|
6
|
+
a `ci-gpu` receipt verifies only when the repository policy lists
|
|
7
|
+
`allowed_key_fingerprints` and the receipt was signed by one of them, so the
|
|
8
|
+
label cannot be claimed by an arbitrary developer key.
|
|
6
9
|
|
|
7
10
|
## Dedicated signing identity
|
|
8
11
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# pytest-gpu-proof
|
|
2
2
|
|
|
3
|
+
This is the code documentation. Use the project-overview link above for the
|
|
4
|
+
workflow figure, paper, and introduction to the tool.
|
|
5
|
+
|
|
3
6
|
**Signed pytest receipts for local GPU runs, verified in CPU-only CI.**
|
|
4
7
|
|
|
5
8
|
The plugin turns an ordinary marked pytest run into a signed, reviewable
|
|
@@ -33,5 +36,5 @@ or exact SSH key fingerprints.
|
|
|
33
36
|
- [Architecture](architecture.md): schema and data flow.
|
|
34
37
|
- [Security model](security_model.md): exact guarantees and limits.
|
|
35
38
|
|
|
36
|
-
Requirements: Python 3.
|
|
39
|
+
Requirements: Python 3.10+, pytest 7+, and an SSH private-key file whose public
|
|
37
40
|
key is registered on GitHub.
|
|
@@ -53,7 +53,7 @@ schema 3. Repositories that have finished migrating should pin
|
|
|
53
53
|
| `allowed_key_fingerprints` | SSH SHA-256 fingerprints accepted in restricted mode |
|
|
54
54
|
| `min_schema` | Minimum acceptable receipt schema version (1, 2, or 3) |
|
|
55
55
|
| `max_age_days` | Maximum age of the merged/session receipt |
|
|
56
|
-
| `require_mode` | Required receipt mode: `local` or `ci-gpu` |
|
|
56
|
+
| `require_mode` | Required receipt mode: `local` or `ci-gpu` (a `ci-gpu` receipt additionally requires `allowed_key_fingerprints` and must be signed by a listed key) |
|
|
57
57
|
| `allow_dirty` | Permit dirty recording or verification trees |
|
|
58
58
|
| `required_fingerprint_paths` | Exact tracked path list |
|
|
59
59
|
| `required_fingerprint_extra_paths` | Exact generated/ignored path list |
|
|
@@ -61,7 +61,7 @@ schema 3. Repositories that have finished migrating should pin
|
|
|
61
61
|
| `required_test_manifest` | Repository-relative file containing exact node IDs |
|
|
62
62
|
| `required_shard_fingerprints` | Exact paths/extras for every named shard |
|
|
63
63
|
| `allow_carried` | Permit carry-forward shards |
|
|
64
|
-
| `carried_max_age_days` | Maximum age of each carried shard's original run |
|
|
64
|
+
| `carried_max_age_days` | Maximum age of each carried shard's original run (default: `max_age_days`, capped at 30) |
|
|
65
65
|
|
|
66
66
|
Example shard scope:
|
|
67
67
|
|
|
@@ -75,7 +75,9 @@ escaping, or unmerged inputs fail closed.
|
|
|
75
75
|
Schema 3 rejects dirty recording and verification trees by default. A receipt
|
|
76
76
|
may verify at its exact commit or a descendant only when the source manifest
|
|
77
77
|
still matches. Freshness limits replay duration but no server-issued nonce is
|
|
78
|
-
currently used
|
|
78
|
+
currently used; a session that started more than five minutes before its
|
|
79
|
+
commit's committer time is refused, so a receipt cannot be back-dated onto a
|
|
80
|
+
commit that did not yet exist.
|
|
79
81
|
|
|
80
82
|
`allow_dirty: true`, long age limits, `--allow-skipped`, and
|
|
81
83
|
`--allow-unsigned` weaken guarantees. Unsigned mode authenticates no signer,
|
|
@@ -98,8 +100,10 @@ each input signature. The merger's signature attests to the union. A review
|
|
|
98
100
|
workflow that needs independent shard authentication should verify every
|
|
99
101
|
input before merging.
|
|
100
102
|
|
|
101
|
-
Carry-forward means a test passed at an ancestor commit
|
|
102
|
-
inputs are byte-identical now.
|
|
103
|
+
Carry-forward means a test passed at an ancestor commit, with a clean working
|
|
104
|
+
tree, and its declared shard inputs are byte-identical now. A receipt recorded
|
|
105
|
+
dirty is never a carry source: the merged `repo.dirty` is computed from fresh
|
|
106
|
+
inputs only, so carrying from it would launder the dirty flag. It does not establish that omitted dependencies
|
|
103
107
|
or the old environment remain equivalent. Carried shards are rejected unless
|
|
104
108
|
policy opts in and bounds their original age.
|
|
105
109
|
|
|
@@ -38,8 +38,10 @@ session failed.
|
|
|
38
38
|
|
|
39
39
|
The merged receipt records input source names, counts, times, and recorded
|
|
40
40
|
signers, then signs the result with the merger's key. Input signatures are
|
|
41
|
-
provenance, not
|
|
42
|
-
|
|
41
|
+
provenance, not verified during an offline merge, unless `--verify-inputs` is
|
|
42
|
+
passed: then every input must verify against its signer's current GitHub keys
|
|
43
|
+
(unsigned inputs are refused) before the union is formed. The merger attests
|
|
44
|
+
to the union either way.
|
|
43
45
|
|
|
44
46
|
## Carry forward unchanged shards
|
|
45
47
|
|
|
@@ -55,10 +57,22 @@ An absent old shard is carried only when:
|
|
|
55
57
|
1. its receipt commit is the current commit or an ancestor;
|
|
56
58
|
2. its stored narrow manifest—including explicit extra paths—recomputes
|
|
57
59
|
identically at the current tree;
|
|
58
|
-
3.
|
|
59
|
-
|
|
60
|
+
3. its receipt was recorded with a clean working tree (`repo.dirty` false): a
|
|
61
|
+
dirty recording may have exercised uncommitted bytes outside the shard's
|
|
62
|
+
narrow manifest, and the merged `repo.dirty` covers fresh inputs only;
|
|
63
|
+
4. it does not duplicate a fresh node ID;
|
|
64
|
+
5. every claimed node ID exists in the old tests list.
|
|
65
|
+
|
|
66
|
+
Every shard entry records `dirty` (the recording tree's state), merge ORs
|
|
67
|
+
shard flags into the merged `repo.dirty`, carry-forward refuses a dirty shard,
|
|
68
|
+
each carried shard records `carried.original_dirty`, and verification refuses
|
|
69
|
+
any dirty shard unless `allow_dirty` is set.
|
|
60
70
|
|
|
61
71
|
Freshly rerun shards always win. Changed or malformed scopes require a rerun.
|
|
72
|
+
`gpu-proof plan --receipt old.json --repo .` applies the same conditions as a
|
|
73
|
+
dry run and reports, per shard, `carryable` or `stale` with the reason (add
|
|
74
|
+
`--json` for drivers), so a partial re-run can be planned without reimplementing
|
|
75
|
+
the checks.
|
|
62
76
|
|
|
63
77
|
Verification rejects carried shards by default:
|
|
64
78
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
site_name: pytest-gpu-proof
|
|
2
|
+
site_url: https://a2r-lab.org/pytest-gpu-proof/docs/
|
|
2
3
|
site_description: >-
|
|
3
4
|
pytest plugin for GPU equivalence testing with signed receipts verified via
|
|
4
5
|
GitHub SSH keys
|
|
@@ -7,6 +8,7 @@ repo_name: A2R-Lab/pytest-gpu-proof
|
|
|
7
8
|
|
|
8
9
|
theme:
|
|
9
10
|
name: material
|
|
11
|
+
custom_dir: website/overrides
|
|
10
12
|
palette:
|
|
11
13
|
- scheme: default
|
|
12
14
|
primary: indigo
|
|
@@ -4,10 +4,10 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pytest-gpu-proof"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.0"
|
|
8
8
|
description = "pytest plugin for GPU equivalence testing with signed receipts verified via GitHub SSH keys"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
11
|
license = { text = "MIT" }
|
|
12
12
|
keywords = ["pytest", "gpu", "cuda", "testing", "signing", "attestation"]
|
|
13
13
|
classifiers = [
|
|
@@ -17,6 +17,7 @@ classifiers = [
|
|
|
17
17
|
"License :: OSI Approved :: MIT License",
|
|
18
18
|
"Operating System :: OS Independent",
|
|
19
19
|
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
20
21
|
"Programming Language :: Python :: 3.11",
|
|
21
22
|
"Programming Language :: Python :: 3.12",
|
|
22
23
|
"Programming Language :: Python :: 3.13",
|
|
@@ -26,11 +27,12 @@ classifiers = [
|
|
|
26
27
|
dependencies = [
|
|
27
28
|
"pytest>=7.0",
|
|
28
29
|
"cryptography>=41.0",
|
|
30
|
+
"tomli>=2.0; python_version < '3.11'",
|
|
29
31
|
]
|
|
30
32
|
|
|
31
33
|
[project.urls]
|
|
32
|
-
Homepage = "https://
|
|
33
|
-
Documentation = "https://a2r-lab.
|
|
34
|
+
Homepage = "https://a2r-lab.org/pytest-gpu-proof/"
|
|
35
|
+
Documentation = "https://a2r-lab.org/pytest-gpu-proof/docs/"
|
|
34
36
|
Repository = "https://github.com/A2R-Lab/pytest-gpu-proof"
|
|
35
37
|
Issues = "https://github.com/A2R-Lab/pytest-gpu-proof/issues"
|
|
36
38
|
Changelog = "https://github.com/A2R-Lab/pytest-gpu-proof/blob/main/CHANGELOG.md"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.5.0"
|