Project · Contributing
Contributing¶
Clear math, minimal API, no hacks. Contributions are welcome — a new attractor, a sharper docstring, a bug fix, a whole analysis. The full text lives in CONTRIBUTING.md; this page is the working version.
Dev setup¶
You need Python ≥ 3.12 and uv. The native Rust
engine ships as a prebuilt abi3 wheel, so no compiler is needed to install
or work on the pure-Python side (see Install). Building
from source — only needed if you edit the Rust engine — needs a
Rust toolchain.
git clone https://github.com/El3ssar/TSDynamics.git
cd TSDynamics
uv sync --group dev # editable install + pytest, ruff, mypy, pre-commit
uv run pre-commit install # optional: ruff lint + format enforced at commit time
The everyday loop — change-scoped testing¶
The test suite is registry-driven: every per-system test is parametrised
over all 171 built-in systems, and every analysis / transform test over the
whole toolkit. A plain uv run pytest is therefore thousands of items and takes
minutes. Don't reach for the full suite as your inner loop. Instead run only
what your diff touches:
make test # change-scoped FAST tier for your diff — the everyday loop
make test-slow # change-scoped SLOW tier (long sims), if you touched heavy code
make test diffs your working tree against origin/main and selects only the
tests your change can affect — a touched system module runs that module's
systems (plus the catalogue-correctness gates), a touched analysis area runs
that area's tests, a touched test file runs itself. It is deliberately biased to
over-select: any foundational change (the engine, a family base, the
registry, pyproject, or any Rust crate) disables selection and runs the full
tier, so a mis-scoped run can never ship a regression — the full suite runs on
every merge to main and nightly. The selector prints exactly what it kept and
why.
Override the diff base when you need to (defaults to origin/main):
For a final pre-push sanity check over everything (parallel, not the nightly exhaustive sweep):
make test-all # full FAST tier over every system / analysis
make test-full # fast + slow tiers over everything
The quality gates¶
CI rejects a PR that fails any of these — run them before pushing:
uv run ruff check src/ tests/ # lint (E, F, I, N, UP, B, SIM, D rules)
uv run ruff format --check src/ tests/ # formatting (line length 100)
uv run mypy --strict src/tsdynamics # static types — CI-blocking, must be clean
make test # change-scoped fast tier
ruffis both linter and formatter;ruff check --fixauto-fixes the safe issues.mypy --strictis a hard gate. The core library is fully strict; the system catalogue (tsdynamics.systems.*) relaxes exactly three codes inherent to its framework-contract kernels —override,no-untyped-def,no-untyped-call— via a documented[tool.mypy.overrides]block, because the_equations/_step/_driftbodies receive their parameters positionally.- Docstrings follow the NumPy convention. Cite the original paper for any method — the scholarly norm (and it keeps the API docs pointing at the source, not at whichever library we happened to look at).
Documentation¶
uv sync --group docs # mkdocs + material + mkdocstrings
TSD_DOCS_FIGURES=0 uv run mkdocs build --strict # fast, figure-less validation
uv run mkdocs serve # live preview at 127.0.0.1:8000
The build must pass --strict (CI enforces it). One build-time convention is
worth knowing:
- The system catalogue documents itself.
hooks/docs_autogen.pyrenders one page per registered system — equations from the symbolic definition, a parameter table, thereference, and a cached phase portrait — so a new system needs no hand-written page.TSD_DOCS_FIGURES=0skips the (slow) figure rendering during local previews.
When you add an analysis or transform, add its prose page under
docs/analysis/, an mkdocstrings stanza on the matching docs/reference/* page,
and a nav entry in mkdocs.yml.
Commits & the PR flow¶
Commits follow Conventional Commits — the changelog and release notes are generated from them:
feat: add Sprott-N attractor to chaotic_attractors
fix(dde): reject zero or negative delay parameters
perf(engine): reuse the frozen Jacobian across SDIRK substages
docs: clarify n_exp behaviour in DelaySystem.lyapunov_spectrum
The prefix decides the release: feat: → minor, fix: / perf: → patch,
! or BREAKING CHANGE: → major; chore / ci / docs-only release nothing.
- Branch from
main—git switch -c feat/my-thing main. - Edit, then run the gates above until green.
- Open a PR. GitHub Actions runs the linters,
mypy --strict, the change-scoped test matrix (Python 3.12 + 3.13 on Linux and macOS), and a docs build. - The PR title must be a conventional commit (enforced by
pr-title.yml): PRs are squash-merged, so the title becomes the commit that decides the next release. Write it accordingly.
Releases¶
Releases are fully automated by
python-semantic-release —
nobody bumps a version or pushes a tag by hand. Every push to main runs the
full suite with coverage, computes the version bump from the conventional-commit
history, rewrites __version__ (and pyproject.toml), tags vX.Y.Z, publishes
the abi3 wheels to PyPI via Trusted Publishing
(OIDC — no API tokens), and generates the release notes. The
static [project].version is kept in lock-step because the build backend
(maturin) cannot read a Python __version__.
The two extension paths¶
Most contributions are one of two well-trodden recipes, each with its own page:
- Adding a system — a new attractor, map, DDE, or SDE for the catalogue. One class; the registry, tests, and docs follow.
- Adding a solver — a new integration kernel for the Rust engine, mirrored in the Python registry.
PR checklist¶
Before you open the PR
- Focused, minimal change.
-
make test(andmake test-slowif you touched heavy code) green locally. -
ruff checkandruff format --checkclean. -
mypy --strict src/tsdynamicsclean. - Public API changes documented in docstrings; new methods cite the original paper.
- Conventional-commit PR title.
- New system in the module / category
__all__; a new DDE also has a history intests/_sampling.py::DDE_HISTORIES, a new built-in SDE a sample inSDE_SAMPLES(guard tests remind you).