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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
This commit is contained in:
Krzysztof Rudnicki 2026-08-23 16:29:24 +02:00
parent 1351a1460f
commit e4b8ad0cfe
4 changed files with 102 additions and 2 deletions

View File

@ -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

View File

@ -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

View File

@ -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."
]
}

View File

@ -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