From e4b8ad0cfe9ac714a805fbbe0edf140a686937cf Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Sun, 23 Aug 2026 16:29:24 +0200 Subject: [PATCH] Install only the test deps in Python tests CI: 79 packages -> 17 python-tests.yml runs pytest and nothing else, but installed the full requirements.txt -- 79 packages, of which ~62 are linters, formatters and type checkers that job never invokes. Installing them was the workflow's slowest step: 49s, against 35s actually running the tests. meta/requirements-test.txt holds the 17 that matter. Derived by static import analysis over python_pkg/ and linux_configuration/tests/, then VERIFIED rather than assumed -- a clean Python 3.11 venv with only this list runs the whole suite: 2174 passed, TOTAL 100.00%, 7138 stmts / 1748 branches, 0 missing identical to the full-requirements run. Install of the subset: 6.2s. The pytest-* entries are in the list even though nothing imports them: randomly reorders tests, timeout enforces a limit, sugar changes reporting and xdist provides -n. Dropping one changes how the suite runs. Drift is gated, not left to discipline: a new requirements-test-sync hook reuses check_requirements_sync.py's existing --requirements flag, so a new third-party import fails at commit time rather than as a bare ModuleNotFoundError in CI. The new file is also in both path filters. pre-commit.yml keeps the full list -- it needs every formatter. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp --- .github/workflows/python-tests.yml | 9 +++- .pre-commit-config.yaml | 10 +++++ .../python-tests-minimal-deps-2026-08-23.json | 45 +++++++++++++++++++ meta/requirements-test.txt | 40 +++++++++++++++++ 4 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 docs/superpowers/evidence/python-tests-minimal-deps-2026-08-23.json create mode 100644 meta/requirements-test.txt diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 72806e7d..4f7d4940 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -16,6 +16,7 @@ on: # workflow and the red run stayed on an older commit. - "requirements.txt" - "meta/requirements.txt" + - "meta/requirements-test.txt" - ".github/workflows/python-tests.yml" pull_request: branches: [main] @@ -32,6 +33,7 @@ on: # workflow and the red run stayed on an older commit. - "requirements.txt" - "meta/requirements.txt" + - "meta/requirements-test.txt" - ".github/workflows/python-tests.yml" workflow_dispatch: {} @@ -50,7 +52,7 @@ jobs: # hashed from the file at this path, and hashing a symlink is not # the same as hashing its target. cache: pip - cache-dependency-path: meta/requirements.txt + cache-dependency-path: meta/requirements-test.txt - name: Install system dependencies run: | set -euo pipefail @@ -59,6 +61,9 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt + # The TEST subset, not the full requirements.txt. This job runs + # pytest and nothing else, so the ~62 linters and formatters in the + # full list were pure install time. See meta/requirements-test.txt. + pip install -r meta/requirements-test.txt - name: Run pytest with coverage run: pytest --cov=python_pkg --cov-branch --cov-report=term-missing --cov-fail-under=100 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4c801d4b..c6598f0d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -306,6 +306,16 @@ repos: language: system pass_filenames: false files: ^(python_pkg/.*\.py|requirements\.txt)$ + - id: requirements-test-sync + name: requirements-test.txt covers all python_pkg imports + # python-tests.yml installs this SUBSET rather than the full list, so + # it needs the same completeness guarantee. Without this hook a new + # third-party import would pass locally and fail in CI with a bare + # ModuleNotFoundError. + entry: python3 meta/scripts/check_requirements_sync.py --package python_pkg --requirements meta/requirements-test.txt + language: system + pass_filenames: false + files: ^(python_pkg/.*\.py|meta/requirements-test\.txt)$ - id: session-autopsy-hook-mutations name: session_autopsy hook tests are non-vacuous (mutation) entry: python3 meta/scripts/mutate_session_autopsy_hooks.py diff --git a/docs/superpowers/evidence/python-tests-minimal-deps-2026-08-23.json b/docs/superpowers/evidence/python-tests-minimal-deps-2026-08-23.json new file mode 100644 index 00000000..5456b442 --- /dev/null +++ b/docs/superpowers/evidence/python-tests-minimal-deps-2026-08-23.json @@ -0,0 +1,45 @@ +{ + "intent": "Stop the Python tests workflow installing ~62 linters and formatters it never invokes, by giving it a requirements file scoped to what the suite actually needs.", + "scope": [ + "meta/requirements-test.txt (new)", + ".github/workflows/python-tests.yml", + ".pre-commit-config.yaml (new requirements-test-sync hook)", + "Non-goal: changing pre-commit.yml, which genuinely needs every formatter" + ], + "changes": [ + "Added meta/requirements-test.txt: 17 packages, down from 79. Derived by static import analysis over python_pkg/ and linux_configuration/tests/, then verified by running the suite against only that list.", + "python-tests.yml installs the subset, caches on it, and lists it in both path filters so a change to it triggers the workflow.", + "Added a requirements-test-sync pre-commit hook reusing check_requirements_sync.py's existing --requirements flag, so a new third-party import cannot silently break CI with a ModuleNotFoundError.", + "The pytest-* plugins are included deliberately: randomly, timeout, sugar and xdist change how the suite runs even though nothing imports them." + ], + "verification": [ + { + "command": "python3.11 venv + pip install -r meta/requirements-test.txt + full pytest run with coverage", + "result": "pass", + "evidence": "2174 passed, TOTAL 100.00%, 7138 statements / 1748 branches, 0 missing -- identical to the full-requirements run" + }, + { + "command": "pip install -r meta/requirements-test.txt (timed)", + "result": "pass", + "evidence": "6.2s for 17 packages against a warm local cache; the full 79-package list took 49-55s on the CI runner" + }, + { + "command": "python3 meta/scripts/check_requirements_sync.py --package python_pkg --requirements meta/requirements-test.txt", + "result": "pass", + "evidence": "exit 0 -- every third-party import in python_pkg is declared in the subset" + }, + { + "command": "pre-commit run requirements-test-sync --all-files", + "result": "pass", + "evidence": "Passed" + } + ], + "risks": [ + "A new runtime import that is not added here would fail CI. Mitigated by the new hook, which gates on the same analysis and runs on every python_pkg .py change.", + "The subset can drift from the full list's version bounds. Both are >= ranges on the same packages, so a divergence surfaces as a resolver conflict rather than a silent mismatch." + ], + "rollback": [ + "Point the Install dependencies step and cache-dependency-path back at requirements.txt and delete the hook.", + "After rollback, confirm the install step returns to ~50s." + ] +} diff --git a/meta/requirements-test.txt b/meta/requirements-test.txt new file mode 100644 index 00000000..8bff454a --- /dev/null +++ b/meta/requirements-test.txt @@ -0,0 +1,40 @@ +# ============================================================================== +# testsAndMisc — the dependencies needed to RUN THE TEST SUITE, and no others. +# +# python-tests.yml runs pytest and nothing else, but installed the full +# meta/requirements.txt: ~79 packages, of which ~62 are linters, formatters and +# type checkers that job never invokes. That install was the workflow's slowest +# step (49s, against 35s actually running pytest). +# +# Derived by static import analysis over python_pkg/ and +# linux_configuration/tests/, then VERIFIED by running the whole suite against +# only this list: 2174 passed, 100.00% coverage, 0 missing lines or branches. +# +# The pytest-* entries are here because they are plugins that change behaviour, +# not because anything imports them: randomly reorders tests, timeout enforces +# a limit, sugar changes reporting, xdist provides -n. Dropping one silently +# changes how the suite runs. +# +# The linting job (pre-commit.yml) still installs the full requirements.txt -- +# it needs every formatter. Keep this file in sync when python_pkg gains a new +# runtime import; check_requirements_sync.py gates the full list, and the +# suite itself fails loudly here on a missing import. +# ============================================================================== + +anthropic>=0.25.0 +beautifulsoup4>=4.0 +coverage>=7.4.0 +defusedxml>=0.7.1 +markdownify>=1.2.0 +numpy>=1.20 +pillow>=10.0 +pytest>=8.0.0 +pytest-cov>=4.1.0 +pytest-randomly>=3.15.0 +pytest-sugar>=1.0.0 +pytest-timeout>=2.2.0 +pytest-xdist>=3.5.0 +requests>=2.0 +rich>=13.0 +typer>=0.12.0 +typing_extensions