pytest-balance 0.1.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.
Files changed (52) hide show
  1. pytest_balance-0.1.0/.commitlintrc.yml +2 -0
  2. pytest_balance-0.1.0/.github/workflows/ci.yml +115 -0
  3. pytest_balance-0.1.0/.github/workflows/release.yml +115 -0
  4. pytest_balance-0.1.0/.gitignore +33 -0
  5. pytest_balance-0.1.0/CHANGELOG.md +77 -0
  6. pytest_balance-0.1.0/LICENSE +21 -0
  7. pytest_balance-0.1.0/Makefile +28 -0
  8. pytest_balance-0.1.0/PKG-INFO +358 -0
  9. pytest_balance-0.1.0/README.md +325 -0
  10. pytest_balance-0.1.0/cliff.toml +58 -0
  11. pytest_balance-0.1.0/pyproject.toml +98 -0
  12. pytest_balance-0.1.0/src/pytest_balance/__init__.py +3 -0
  13. pytest_balance-0.1.0/src/pytest_balance/__main__.py +5 -0
  14. pytest_balance-0.1.0/src/pytest_balance/algorithms/__init__.py +0 -0
  15. pytest_balance-0.1.0/src/pytest_balance/algorithms/lpt.py +68 -0
  16. pytest_balance-0.1.0/src/pytest_balance/algorithms/partitioner.py +62 -0
  17. pytest_balance-0.1.0/src/pytest_balance/ci/__init__.py +0 -0
  18. pytest_balance-0.1.0/src/pytest_balance/ci/detect.py +209 -0
  19. pytest_balance-0.1.0/src/pytest_balance/ci/splitter.py +46 -0
  20. pytest_balance-0.1.0/src/pytest_balance/cli.py +367 -0
  21. pytest_balance-0.1.0/src/pytest_balance/plugin.py +255 -0
  22. pytest_balance-0.1.0/src/pytest_balance/py.typed +0 -0
  23. pytest_balance-0.1.0/src/pytest_balance/report.py +64 -0
  24. pytest_balance-0.1.0/src/pytest_balance/store/__init__.py +0 -0
  25. pytest_balance-0.1.0/src/pytest_balance/store/merger.py +53 -0
  26. pytest_balance-0.1.0/src/pytest_balance/store/models.py +41 -0
  27. pytest_balance-0.1.0/src/pytest_balance/store/reader.py +94 -0
  28. pytest_balance-0.1.0/src/pytest_balance/store/writer.py +53 -0
  29. pytest_balance-0.1.0/src/pytest_balance/xdist/__init__.py +0 -0
  30. pytest_balance-0.1.0/src/pytest_balance/xdist/hooks.py +37 -0
  31. pytest_balance-0.1.0/src/pytest_balance/xdist/scheduler.py +154 -0
  32. pytest_balance-0.1.0/tests/__init__.py +0 -0
  33. pytest_balance-0.1.0/tests/conftest.py +1 -0
  34. pytest_balance-0.1.0/tests/test_algorithms/__init__.py +0 -0
  35. pytest_balance-0.1.0/tests/test_algorithms/test_lpt.py +121 -0
  36. pytest_balance-0.1.0/tests/test_algorithms/test_partitioner.py +61 -0
  37. pytest_balance-0.1.0/tests/test_ci/__init__.py +0 -0
  38. pytest_balance-0.1.0/tests/test_ci/test_detect.py +116 -0
  39. pytest_balance-0.1.0/tests/test_ci/test_splitter.py +67 -0
  40. pytest_balance-0.1.0/tests/test_cli.py +158 -0
  41. pytest_balance-0.1.0/tests/test_plugin.py +186 -0
  42. pytest_balance-0.1.0/tests/test_report.py +39 -0
  43. pytest_balance-0.1.0/tests/test_store/__init__.py +0 -0
  44. pytest_balance-0.1.0/tests/test_store/test_merger.py +85 -0
  45. pytest_balance-0.1.0/tests/test_store/test_models.py +65 -0
  46. pytest_balance-0.1.0/tests/test_store/test_reader.py +103 -0
  47. pytest_balance-0.1.0/tests/test_store/test_writer.py +70 -0
  48. pytest_balance-0.1.0/tests/test_xdist/__init__.py +0 -0
  49. pytest_balance-0.1.0/tests/test_xdist/_simulator.py +212 -0
  50. pytest_balance-0.1.0/tests/test_xdist/test_scheduler.py +392 -0
  51. pytest_balance-0.1.0/tests/test_xdist/test_scheduler_stateful.py +39 -0
  52. pytest_balance-0.1.0/uv.lock +531 -0
@@ -0,0 +1,2 @@
1
+ extends:
2
+ - "@commitlint/config-conventional"
@@ -0,0 +1,115 @@
1
+ name: CI
2
+
3
+ permissions: {}
4
+
5
+ concurrency:
6
+ group: ${{ github.workflow }}-${{ github.ref }}
7
+ cancel-in-progress: true
8
+
9
+ on:
10
+ push:
11
+ branches: [main]
12
+ pull_request:
13
+
14
+ jobs:
15
+ commitlint:
16
+ name: Commit messages
17
+ if: github.event_name == 'pull_request'
18
+ runs-on: ubuntu-latest
19
+ permissions:
20
+ contents: read
21
+ steps:
22
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
23
+ with:
24
+ fetch-depth: 0
25
+ - uses: wagoid/commitlint-github-action@f133a0d95090ef2609192b4a21f54e20af819ea9 # v6
26
+
27
+ format:
28
+ name: Format
29
+ runs-on: ubuntu-latest
30
+ permissions:
31
+ contents: read
32
+ steps:
33
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
34
+ - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
35
+ with:
36
+ enable-cache: true
37
+ - run: uv sync
38
+ - run: uv run ruff format --check src tests
39
+
40
+ lint:
41
+ name: Lint
42
+ runs-on: ubuntu-latest
43
+ permissions:
44
+ contents: read
45
+ steps:
46
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
47
+ - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
48
+ with:
49
+ enable-cache: true
50
+ - run: uv sync
51
+ - run: uv run ruff check src tests
52
+
53
+ lock:
54
+ name: Lockfile
55
+ runs-on: ubuntu-latest
56
+ permissions:
57
+ contents: read
58
+ steps:
59
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
60
+ - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
61
+ with:
62
+ enable-cache: true
63
+ - run: uv lock --check
64
+
65
+ typecheck:
66
+ name: Typecheck
67
+ runs-on: ubuntu-latest
68
+ permissions:
69
+ contents: read
70
+ steps:
71
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
72
+ - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
73
+ with:
74
+ enable-cache: true
75
+ - run: uv sync
76
+ - run: uv run mypy src
77
+
78
+ test:
79
+ name: Test (Python ${{ matrix.python-version }})
80
+ runs-on: ubuntu-latest
81
+ permissions:
82
+ contents: read
83
+ strategy:
84
+ fail-fast: false
85
+ matrix:
86
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
87
+ steps:
88
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
89
+ - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
90
+ with:
91
+ enable-cache: true
92
+ - run: uv sync --python ${{ matrix.python-version }}
93
+ - run: uv run pytest --cov --cov-report=xml --tb=short
94
+ if: matrix.python-version == '3.14'
95
+ - run: uv run pytest --tb=short
96
+ if: matrix.python-version != '3.14'
97
+ - uses: codecov/codecov-action@75cd11691c0faa626561e295848008c8a7dddffe # v5
98
+ if: matrix.python-version == '3.14'
99
+ with:
100
+ files: coverage.xml
101
+ token: ${{ secrets.CODECOV_TOKEN }}
102
+ fail_ci_if_error: false
103
+
104
+ test-without-xdist:
105
+ name: Test (without xdist)
106
+ runs-on: ubuntu-latest
107
+ permissions:
108
+ contents: read
109
+ steps:
110
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
111
+ - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
112
+ with:
113
+ enable-cache: true
114
+ - run: uv sync --no-group dev
115
+ - run: uv run pytest --tb=short -k "not test_xdist"
@@ -0,0 +1,115 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ permissions: {}
8
+
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: false
12
+
13
+ jobs:
14
+ ci-check:
15
+ name: CI Gate
16
+ runs-on: ubuntu-latest
17
+ timeout-minutes: 10
18
+ permissions:
19
+ contents: read
20
+ steps:
21
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
22
+ - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
23
+ with:
24
+ enable-cache: true
25
+
26
+ - name: Verify tag matches package version
27
+ env:
28
+ TAG_REF: ${{ github.ref }}
29
+ run: |
30
+ TAG="${TAG_REF#refs/tags/v}"
31
+ PY_VER=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
32
+ echo "Tag: $TAG | pyproject: $PY_VER"
33
+ if [ "$TAG" != "$PY_VER" ]; then
34
+ echo "::error::Tag v$TAG does not match pyproject.toml version $PY_VER"
35
+ exit 1
36
+ fi
37
+
38
+ - run: uv sync
39
+ - run: uv run ruff format --check src tests
40
+ - run: uv run ruff check src tests
41
+ - run: uv run mypy src
42
+ - run: uv run pytest --tb=short
43
+
44
+ build:
45
+ name: Build package
46
+ runs-on: ubuntu-latest
47
+ timeout-minutes: 5
48
+ permissions:
49
+ contents: read
50
+ steps:
51
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
52
+ - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
53
+ - run: uv build
54
+ - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
55
+ with:
56
+ name: dist
57
+ path: dist/
58
+
59
+ publish:
60
+ name: Publish to PyPI
61
+ needs: [build, ci-check]
62
+ runs-on: ubuntu-latest
63
+ timeout-minutes: 5
64
+ environment: PyPI
65
+ permissions:
66
+ id-token: write
67
+ steps:
68
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
69
+ with:
70
+ name: dist
71
+ path: dist/
72
+ - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
73
+ with:
74
+ attestations: true
75
+
76
+ release:
77
+ name: GitHub Release
78
+ needs: [build, ci-check]
79
+ runs-on: ubuntu-latest
80
+ timeout-minutes: 5
81
+ permissions:
82
+ contents: write
83
+ steps:
84
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
85
+ with:
86
+ fetch-depth: 0
87
+
88
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
89
+ with:
90
+ name: dist
91
+ path: dist/
92
+
93
+ - name: Generate checksums
94
+ run: |
95
+ cd dist
96
+ sha256sum * > SHA256SUMS
97
+
98
+ - name: Generate release notes
99
+ id: cliff
100
+ uses: orhun/git-cliff-action@c93ef52f3d0ddcdcc9bd5447d98d458a11cd4f72 # v4
101
+ with:
102
+ config: cliff.toml
103
+ args: --latest --strip header
104
+ env:
105
+ GITHUB_REPO: ${{ github.repository }}
106
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
107
+
108
+ - uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2
109
+ with:
110
+ body: ${{ steps.cliff.outputs.content }}
111
+ prerelease: ${{ contains(github.ref_name, 'a') || contains(github.ref_name, 'b') || contains(github.ref_name, 'rc') }}
112
+ files: |
113
+ dist/*.whl
114
+ dist/*.tar.gz
115
+ dist/SHA256SUMS
@@ -0,0 +1,33 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+
9
+ # Tool caches (pytest, mypy, ruff)
10
+ .cache/
11
+
12
+ # Virtual environments
13
+ .venv/
14
+
15
+ # Duration store
16
+ .balance/
17
+
18
+ # IDE
19
+ .idea/
20
+ .vscode/
21
+ *.swp
22
+
23
+ # OS
24
+ .DS_Store
25
+
26
+ # Coverage
27
+ .coverage
28
+
29
+ # Review
30
+ .full-review/
31
+
32
+ # Ongoing documentation
33
+ docs/
@@ -0,0 +1,77 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.1.0] - 2026-06-15
6
+
7
+ ### Added
8
+ - **store:** Estimate from call-phase durations only
9
+ - **store:** Dedup merged records per phase
10
+ - **plugin:** Set outcome on recorded durations
11
+ - **store:** Record test outcome in durations
12
+
13
+
14
+ ## [0.1.0a3] - 2026-06-10
15
+
16
+ ### Added
17
+ - **cli:** Add --alpha to the standalone plan command
18
+ - **cli:** Expose --balance-ema-alpha to the ema estimator
19
+ - **cli:** Validate the EMA alpha range
20
+
21
+
22
+ ### Changed
23
+ - **store:** Name the default EMA alpha constant
24
+
25
+
26
+ ## [0.1.0a2] - 2026-06-09
27
+
28
+ ### Added
29
+ - **algorithms:** Add LPT compute_order
30
+ - **xdist:** Record scheduling history for invariant diagnostics
31
+ - **xdist:** Fail loud on scheduler bookkeeping invariant
32
+
33
+
34
+ ### Changed
35
+ - **xdist:** Subclass WorkStealingScheduling
36
+
37
+
38
+ ## [0.1.0a1] - 2026-04-09
39
+
40
+ ### Added
41
+ - Add release workflow, changelog config, and commit linting
42
+ - **xdist:** Add duration-aware scheduler with work-stealing
43
+ - Add plugin core with CI splitting and duration recording
44
+ - Add post-run balance report
45
+ - **ci:** Add duration-aware CI test splitter
46
+ - **ci:** Add CI environment auto-detection
47
+ - **store:** Add JSONL merger with dedup
48
+ - **store:** Add JSONL reader with EMA/median/last estimation
49
+ - **store:** Add JSONL duration writer
50
+ - **algorithms:** Add scope-aware test partitioner
51
+ - **algorithms:** Add LPT partitioning algorithm
52
+ - **store:** Add TestDuration and DurationEstimate models
53
+ - Initialize project scaffolding
54
+
55
+
56
+ ### Changed
57
+ - Fix warnings, naming, and scheduler performance
58
+ - Defer plugin imports to reduce pytest startup overhead
59
+ - Stream JSONL reads to reduce memory usage
60
+
61
+
62
+ ### Fixed
63
+ - **xdist:** Initialize numnodes from tx spec config
64
+ - Correct README CI file patterns and sanitize run_id
65
+ - Align build config and clean up project metadata
66
+ - Address critical review findings
67
+
68
+
69
+
70
+ [0.1.0]: https://github.com/ggueret/pytest-balance/compare/v0.1.0a3...v0.1.0
71
+
72
+ [0.1.0a3]: https://github.com/ggueret/pytest-balance/compare/v0.1.0a2...v0.1.0a3
73
+
74
+ [0.1.0a2]: https://github.com/ggueret/pytest-balance/compare/v0.1.0a1...v0.1.0a2
75
+
76
+ [0.1.0a1]: https://github.com/ggueret/pytest-balance/tree/v0.1.0a1
77
+ <!-- generated by git-cliff -->
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Geoffrey Guéret
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,28 @@
1
+ .PHONY: install lint typecheck test format check all clean
2
+
3
+ install:
4
+ uv sync
5
+
6
+ lint:
7
+ uv run ruff check src tests
8
+
9
+ typecheck:
10
+ uv run mypy src
11
+
12
+ test:
13
+ uv run pytest
14
+
15
+ format:
16
+ uv run ruff format src tests
17
+ uv run ruff check --fix src tests
18
+
19
+ check:
20
+ uv run ruff format --check src tests
21
+ uv run ruff check src tests
22
+ uv run mypy src
23
+
24
+ all: lint typecheck test
25
+
26
+ clean:
27
+ rm -rf .cache .coverage htmlcov dist build *.egg-info
28
+ find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true