Development environment and checks#
Use Python 3.14 and uv for development. Dependencies are declared in
pyproject.toml and resolved in uv.lock. From a source checkout, install the
development and documentation dependencies with:
uv sync --group docs
The default development group includes the test, formatting, linting, and type
checking tools, as well as Qt for the GUI tests. Run commands through uv run
to use the project environment.
Check a change#
One command runs the whole definition-of-done gate, in order, exactly as Linux CI does:
uv run poe check
It runs ruff check --fix, ruff format, ruff check (what autofix could not
fix), pyright, codespell, poe changelog-check (the pending release notes under
changelog.d/ and the changelog’s structure), and finally poe cov: the test suite
on every core
(pytest-xdist), doctests included, measuring statement and branch coverage
of the whole croak package, GUI included, and failing below 100%. The XML
report is written to build/coverage/coverage.xml.
GitHub Actions uses Python 3.14 and the committed lockfile on ubuntu-latest,
windows-latest, and macos-latest. Linux runs the full gate above, including
the 100% statement and branch coverage requirement, and uploads its coverage
report to Codecov. Windows and macOS run uv run poe test: the same complete
suite, including GUI tests and doctests, without coverage instrumentation.
Lint, formatting, type, spelling, and changelog checks run once, on Linux.
Linux installs Qt system libraries and runs under Xvfb; Windows and macOS run
their tests directly. Qt uses its offscreen backend on all three platforms.
A failure on one platform leaves the others running so all failures can be
inspected together. Each test log lists the 20 slowest test phases to help
diagnose differences in runtime.
The final lint-type-test check succeeds only when every platform passes,
preserving the check name required by the main branch’s repository rules.
The workflow runs for pull requests and pushes to main or master, and can
also be started manually from the Actions tab. CI fails if the gate’s lint
autofixes or formatting leave a tracked-file diff.
For focused work run single files serially, without coverage:
uv run pytest tests/test_preprocess.py
uv run poe test # the whole suite on every core, no coverage
The suite is strict, and each rule is set in pyproject.toml:
Warnings are errors. A test that expects a warning asserts it with
pytest.warns; a warning nobody expects is a bug to fix, not to filter.Random order. pytest-randomly shuffles the tests (the seed is printed; rerun with
-p randomly --randomly-seed=N), and xdist spreads modules over workers, so tests must not depend on each other or share writable files. Write undertmp_path.Doctests run. Every
Examplessection is executed, so examples must be deterministic (seed every random generator).Full coverage. New code arrives with tests that exercise its branches and error paths, with assertions about its behaviour. Code that cannot be reached is removed rather than excluded;
# pragma: no coveris reserved for code that cannot run under test at all (anif __name__ == "__main__":block, a guarded optional import) and carries its reason on the same line. The threshold and the exclusions are not lowered to make a change pass.
Keep orchestration regressions small and separate from numerical convergence tests. For example, the two-phase LM polish tests use a 32-point SHG pulse and 17 delays: live snapshots show the delay offset held during the first phase, then the joint phase recovers it and reduces the remaining trace error. The separate thickness-fitting tests exercise slab-thickness recovery. Reserve that larger dispersive convergence problem for those dedicated tests, and use the compact example to check the two-phase sequence.
GUI tests live in tests/gui/ and run offscreen through pytest-qt; the shared
tests/conftest.py points QSettings at a per-test directory for them. The
evo extra (evosax) is part of the development group so the cma-es retriever
is tested.
Tests live in tests/ and follow the package’s domain structure. Numerical tests
should state their tolerances and check physical or mathematical invariants as
well as individual values. Document public behaviour in NumPy-style docstrings
and in the manual, and record notable changes in a changelog.d/ fragment (see
releasing).
See numerical validation for model and gradient checks, and building the documentation for executed tutorial caches.