From 06e592febe888b5c0f66ea8fa864900b71e052cd Mon Sep 17 00:00:00 2001 From: SkyeAv Date: Fri, 31 Jul 2026 14:23:27 -0700 Subject: [PATCH 1/3] perf: parallelize the test suite and trim slow pre-commit hooks Two complementary changes that make the suite (and commits) fast: 1. pytest-xdist in the dev group + '-n auto' in addopts, so every run is parallel by default. Coverage is identical (same missing lines). serial: 622 passed, 27 skipped, 90% cov, 152.9s -n auto: 622 passed, 27 skipped, 90% cov, 21.96s (~7x faster) Disable for one serial run with: pytest -n 0 2. Pre-commit now keeps only the fast, auto-fixing hooks (ruff --fix, ruff-format, cargo-fmt). The slow gates it used to run on every commit (pyright, a full pytest suite that rebuilt the Rust extension, cargo-clippy, cargo-test) are owned by CI on every PR. 'pre-commit run --all-files' drops from ~2.5+min to 0.3s. --- .pre-commit-config.yaml | 31 +++---------------------------- pyproject.toml | 5 ++++- uv.lock | 26 +++++++++++++++++++++++++- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5f11efb3..2d0b9894 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,6 @@ +# Fast, auto-fixing checks only. Slow, whole-repo gates (pyright, the full pytest +# suite, cargo-clippy, cargo-test) run in CI on every PR instead — running them in a +# pre-commit hook rebuilds the Rust extension and runs the whole suite on every commit. repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.15.12 @@ -9,20 +12,6 @@ repos: files: ^(src|tests)/.*\.py$ - repo: local hooks: - - id: pyright - name: pyright - entry: uv run pyright - language: system - types: [python] - files: ^(src|tests)/.*\.py$ - pass_filenames: false - - id: pytest - name: pytest - entry: bash -c 'uv run maturin develop --manifest-path rust/Cargo.toml && uv run pytest' - language: system - types: [python] - files: ^(src|tests|rust)/ - pass_filenames: false - id: cargo-fmt name: cargo-fmt entry: cargo fmt --check --manifest-path rust/Cargo.toml @@ -30,17 +19,3 @@ repos: types: [rust] files: ^rust/ pass_filenames: false - - id: cargo-clippy - name: cargo-clippy - entry: cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings - language: system - types: [rust] - files: ^rust/ - pass_filenames: false - - id: cargo-test - name: cargo-test - entry: cargo test --manifest-path rust/Cargo.toml - language: system - types: [rust] - files: ^rust/ - pass_filenames: false diff --git a/pyproject.toml b/pyproject.toml index 38f9fab4..b6edd556 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,11 +98,14 @@ dev = [ "pytest>=9.0.2", "pytest-cov>=7.1.0", "ruff>=0.15.6", + "pytest-xdist>=3.8.0", ] [tool.pytest.ini_options] testpaths = ["tests"] -addopts = "--cov=tablassert --cov-report=term-missing" +# Parallel by default via pytest-xdist (~7x faster full suite, identical coverage). +# Disable for a single serial run with `-n 0` (e.g. debugging one test). +addopts = "--cov=tablassert --cov-report=term-missing -n auto" markers = ["network: requires internet"] [tool.coverage.run] diff --git a/uv.lock b/uv.lock index 953b40d3..9566af16 100644 --- a/uv.lock +++ b/uv.lock @@ -823,6 +823,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, ] +[[package]] +name = "execnet" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622, upload-time = "2025-11-12T09:56:37.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" }, +] + [[package]] name = "fastexcel" version = "0.20.2" @@ -2881,6 +2890,19 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/dc/1e/fb11174c9eaebcec27d36e9e994b90ffa168bc3226925900b9dbbf16c9da/pytest-logging-2015.11.4.tar.gz", hash = "sha256:cec5c85ecf18aab7b2ead5498a31b9f758680ef5a902b9054ab3f2bdbb77c896", size = 3916, upload-time = "2015-11-04T12:15:54.122Z" } +[[package]] +name = "pytest-xdist" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "execnet" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -3900,7 +3922,7 @@ wheels = [ [[package]] name = "tablassert" -version = "8.0.0" +version = "8.0.1" source = { editable = "." } dependencies = [ { name = "biolink-model" }, @@ -3937,6 +3959,7 @@ dev = [ { name = "pyright" }, { name = "pytest" }, { name = "pytest-cov" }, + { name = "pytest-xdist" }, { name = "ruff" }, ] @@ -3969,6 +3992,7 @@ dev = [ { name = "pyright", specifier = ">=1.1.411" }, { name = "pytest", specifier = ">=9.0.2" }, { name = "pytest-cov", specifier = ">=7.1.0" }, + { name = "pytest-xdist", specifier = ">=3.8.0" }, { name = "ruff", specifier = ">=0.15.6" }, ] From ee9fdba0ccf0273403ee0293e63253aa60a6a532 Mon Sep 17 00:00:00 2001 From: SkyeAv Date: Fri, 31 Jul 2026 14:23:40 -0700 Subject: [PATCH 2/3] chore(ci): cache uv, parallelize CI tests, bump gh-pages to v4 - ci.yml: enable uv caching (enable-cache) on all three setup-uv steps so 'uv sync' reuses the package cache across runs; make the test step's '-n auto' explicit (it is also the addopts default). - docs.yml: bump peaceiris/actions-gh-pages v3 -> v4 (v3 runs on the deprecated Node 16 runtime; v4 is the maintained Node 20 release, no breaking changes for the inputs used). --- .github/workflows/ci.yml | 10 +++++++++- .github/workflows/docs.yml | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f15b8669..2f007bc6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,8 @@ jobs: uses: actions/checkout@v4 - name: Install uv uses: astral-sh/setup-uv@v4 + with: + enable-cache: true - name: Set up Python uses: actions/setup-python@v5 with: @@ -36,6 +38,8 @@ jobs: uses: actions/checkout@v4 - name: Install uv uses: astral-sh/setup-uv@v4 + with: + enable-cache: true - name: Set up Python uses: actions/setup-python@v5 with: @@ -58,6 +62,8 @@ jobs: workspaces: rust - name: Install uv uses: astral-sh/setup-uv@v4 + with: + enable-cache: true - name: Set up Python uses: actions/setup-python@v5 with: @@ -67,7 +73,9 @@ jobs: - name: Build Rust extension run: uv run maturin develop --manifest-path rust/Cargo.toml - name: Run tests - run: uv run pytest + # `-n auto` is also set in pyproject [tool.pytest.ini_options] addopts; kept + # explicit here so the intent is visible. Ubuntu runners have 2-4 cores. + run: uv run pytest -n auto rust: runs-on: ubuntu-latest diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index dde3f4ef..068a78f2 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -17,7 +17,7 @@ jobs: - name: Build documentation run: uv run --group dev mkdocs build --strict - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./site From 111979027c566b9f9fe8bfbb76b843644ac6fda1 Mon Sep 17 00:00:00 2001 From: SkyeAv Date: Fri, 31 Jul 2026 14:23:40 -0700 Subject: [PATCH 3/3] docs: sync contributor guide with the parallel suite and trimmed hooks Update CONTRIBUTING.md and docs/installation.md to match the new reality: the suite is parallel by default (~600+ tests, ~20-30s, ~90% cov in the default --extra qc env), and pre-commit runs only the fast lint/format hooks (the full gates run in CI). The stale '324 tests / 40-45s / 94%' figures and the removed pyright/pytest/cargo-clippy/cargo-test hook entries are corrected. --- CONTRIBUTING.md | 8 ++------ docs/installation.md | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a6a82ab2..1c60d91d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -100,13 +100,13 @@ What the gates cover: - **Ruff linting and formatting.** The current tree enforces core pycodestyle/pyflakes safety checks plus stale-suppression detection. It also enforces an expanded rule set covering common bug patterns (bugbear), simplifications, Python-version upgrades, pytest style, import order, and comprehensions. Treat `uv run ruff check .` and `uv run ruff format --check .` as the stable interface rather than relying on individual rule codes. - **Pyright.** Type checking runs through `uv run pyright`; the project is tightening this as a strict-inference ratchet over time. -- **Python tests.** The suite is offline and currently runs 324 tests in roughly 40-45 seconds, reporting coverage around 94%. +- **Python tests.** The suite is offline and runs in parallel by default via [pytest-xdist](https://pypi.org/project/pytest-xdist/) (`-n auto` in `pyproject.toml`): over 600 tests in ~20-30 seconds, reporting around 90% coverage in the default CI environment (`--extra qc`). Disable parallelism for a single serial run with `pytest -n 0`. - **Rust tests.** `cargo test --manifest-path rust/Cargo.toml` currently runs 46 Rust unit tests for the extension. - **Rust style and lints.** `cargo fmt --check` enforces formatting; clippy runs all targets with warnings denied. ## Pre-commit hooks -Install hooks after setup if you want the same checks to run automatically: +Install hooks after setup to run the fast, auto-fixing checks automatically on every commit. The slower whole-repo gates (pyright, the full pytest suite, cargo-clippy, cargo-test) are intentionally **not** pre-commit hooks — they run in CI on every pull request instead, so committing stays fast. ```bash uv run pre-commit install @@ -116,11 +116,7 @@ Configured hooks: - `ruff`: fixes lint issues in `src/` and `tests/` when possible. - `ruff-format`: formats Python files in `src/` and `tests/`. -- `pyright`: runs `uv run pyright` once per commit attempt. -- `pytest`: rebuilds the extension with `uv run maturin develop --manifest-path rust/Cargo.toml`, then runs `uv run pytest`. - `cargo-fmt`: runs `cargo fmt --check --manifest-path rust/Cargo.toml`. -- `cargo-clippy`: runs `cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings`. -- `cargo-test`: runs `cargo test --manifest-path rust/Cargo.toml`. ## Running subsets diff --git a/docs/installation.md b/docs/installation.md index b7a61934..b1ca98c3 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -126,7 +126,7 @@ make check The underlying stable gates are `ruff check` / `ruff format --check`, `pyright`, `pytest`, and `cargo fmt --check` / `cargo test` / `cargo clippy --all-targets -- -D warnings` (see [Development](development.md)). -Install pre-commit hooks if you want the gates to run automatically before commits: +Install pre-commit hooks to run the fast lint/format checks automatically before commits (the full gates run in CI): ```bash uv run pre-commit install