Code style¶
pyTomoAO uses ruff for both linting and formatting. The
configuration lives in pyproject.toml, and the Code health workflow enforces it on every
pull request.
Running the checks¶
pip install -e ".[dev]"
ruff check . # lint
ruff check --fix . # lint and apply the safe fixes
ruff format . # format
ruff format --check . # verify formatting without writing
CI runs exactly these two commands, with the ruff version pinned in
.github/workflows/code-health.yml:
ruff check --output-format=github .
ruff format --check --diff .
If CI disagrees with your local run, check that your ruff version matches the pin — the
[dev] extra installs the same one.
Formatting¶
The whole codebase is formatted by ruff format at a 100-column line length. Do not
hand-format around it; run the formatter and commit the result. Code blocks inside
docstrings are formatted too (docstring-code-format), so examples in the API reference
stay consistent with the code around them.
Lint rules¶
The enabled rule families are pyflakes, pycodestyle, isort, pyupgrade, bugbear,
comprehensions, pie, return, simplify, unused-arguments, pylint and ruff’s own rules. A few
are switched off deliberately, and the reasons are recorded in pyproject.toml:
- Naming rules (
N) are not enabled. This package is a translation of MATLAB adaptive-optics code. Class names such as
tomographicReconstructorare public API that user scripts import, and matrix names such asGamma,Cxx,CoxandRmatch the equations they implement. Renaming them would break users and make the mathematics harder to follow.PLR2004(magic values),PLR0912/PLR0913/PLR0915(branches, arguments, statements).Numerical kernels are long, linear routines full of meaningful constants. Splitting them up to satisfy a counter would hurt readability.
PLC0415(imports inside functions) andPLW0603(global statement).The CPU/GPU backend is chosen at import time through a
try/exceptaround the CuPy import and a module-level flag.
sandbox/ is excluded entirely: it holds exploratory scripts that are not packaged or
tested, and gating real work on them would be noise.
Suppressing a rule¶
Prefer fixing the code. When a violation is genuinely intentional, silence it on the specific line with a reason next to it:
# use_float32 is unused here but kept so the GPU and CPU kernels share a signature.
def _rotateWFS(px_gpu, py_gpu, rotAngleInRadians, use_float32=False): # noqa: ARG001
A blanket # noqa with no rule code will itself be flagged. If a rule is wrong for the
whole project rather than one line, change pyproject.toml and explain why in a comment —
future readers need the reasoning more than the rule.
Conventions the linter cannot check¶
See Contributing for the patterns that matter but are not mechanically enforced: validation in property setters, derived values as properties, NumPy-style docstrings, and keeping the CPU and GPU kernels in lockstep.