Cut pytest-xdist per-worker import cost on Windows unit runs#250
Conversation
pytest imports every test module during collection and only then applies `-m` deselection, so plain unit runs imported the heavy optional deps (playwright via 10 *_browser.py, textual via test_tui*.py) for nothing. On Windows each xdist worker spawns a fresh interpreter and re-imports the world, so this tax is paid per worker, every CI run. - conftest: pytest_ignore_collect hook skips *_browser.py / test_tui*.py when their marker can't be selected by the active -m expression (uses pytest's own Expression evaluator). Removes the per-worker textual import (~0.8s/worker) from unit runs; selection is unchanged. - justfile / CI unit step: `-p no:playwright` skips the unused pytest-playwright plugin on unit runs (~1s/worker playwright import). Kept out of pyproject addopts so browser runs still load it. - work/xdist-import-cost.md: findings, measurements, and the worker-count recommendation (keep -n auto; CI Windows has ~4 vCPUs — the gap-closer is per-worker import cost, not worker count). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughTwo optimizations reduce pytest startup and collection overhead for unit test runs: a new ChangesPytest collection and plugin optimizations
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Part of #248.
Problem
On Windows, pytest-xdist spawns each worker and re-imports the whole
module graph per worker (Linux forks and skips this). Measured:
-n04.5s vs-n auto18.6s → ~14s of pureworker-spawn + re-import (12 workers)
Two heavy dependencies were imported on every unit worker even though
unit tests never use them:
pytest-playwrightauto-loads via its entry point (~1s/worker)playwright/textualat module toplevel, and pytest imports every collected module before marker
deselection (~0.8s/worker for
textual)Change
test/conftest.py: apytest_ignore_collecthook that skips*_browser.py/test_tui*.pymodules only when they would bedeselected anyway, evaluated via pytest's own marker
Expression(so
-m browser,-m tui,-m "not slow", and unmarked full runs allstill collect them). Verified no unmarked test hides in those modules,
so no coverage is lost.
-p no:playwrighton every unit-invoking command (justfiletest/test-all/test-cov+ the CI unit step); browser/TUI steps areuntouched so they still load the plugin.
Results (local Windows, 12-core)
-n auto): 4.2s → 3.1s (~26%)-m browser/-m tuicountsidentical; browser + TUI runs still execute)
Worker count (deliberately not changed)
The spawn/import tax scales with worker count, but the right fix is to
make each worker cheaper, not to cap workers, so
-n autois kept. Theoptimal CI worker count depends on the runner (GitHub
windows-latesthas ~4 vCPUs vs a 12-core dev box), so no CI
-nvalue is hard-committedhere — it should be A/B'd on the real runner. Full measurements in
work/xdist-import-cost.md.Note: the collect-skip matcher keys off the filename pattern; a one-line
comment near it documents that coupling (a browser-marked test in a
non-matching filename stays collected on unit runs — harmless, just a
missed optimization).
Summary by CodeRabbit