This is a beta package under rapid iteration. Backward compatibility is not a concern, feel free to make breaking API changes when they improve the design.
Use the /release NEW_VERSION skill (.claude/skills/release/SKILL.md) to perform a
full release. It handles version bumps across all files, lock file sync, pre-commit
checks, commit + tag creation (with annotated tag message), a gated push step, and
generation of the GitHub release message and Discord announcement.
This project uses uv for dependency management and just as a command runner.
uv sync- Install dependencies and sync the virtual environmentuv build- Build the package
just docs(orjust d) - Build documentation using Zensicaljust serve-docs(orjust sd) - Build and serve documentation locally with live reloadjust clean-docs(orjust cd) - Clean documentation build directory and generated API filesjust generate-doc-images(orjust gdi) - Run all documentation image generators locally
Documentation is built entirely on GitHub Actions and deployed to
confusius-tools/confusius-docs
(a separate public repo served via GitHub Pages at https://confusius.tools/).
The workflow (.github/workflows/docs.yml) has three jobs:
build(every push and PR): generates all documentation images, builds the example gallery, runszensical build, and uploadssite/,docs/examples/_built/, anddocs/images/as artifacts.deploy-preview(PRs only): downloads thesite/artifact and deploys it topr-preview/pr-N/in the docs repo. Posts a sticky comment on the PR with the preview URL and maintainspr-preview/versions.jsonfor the mike version switcher.deploy-docs(pushes tomainand version tags): downloads the pre-built artifacts and runsmike deploy.main→dev+latestalias;v*tags →<version>+stablealias.
A companion workflow (.github/workflows/docs-cleanup-preview.yml) removes the preview
directory and its versions.json entry when a PR is closed.
Deployment requires a DOCS_DEPLOY_TOKEN secret: a fine-grained PAT with Contents:
Read and write on confusius-tools/confusius-docs.
Image generators live in docs/images/<topic>/generate.py. Each one is gitignored
(only generate.py is committed; the outputs are not). To add a new generator:
- Create
docs/images/<topic>/generate.pyand a matching.gitignore(copy an existing one as a template). - Add
uv run docs/images/<topic>/generate.pyto thejust generate-doc-imagesrecipe injustfile. - Add the script to the Generate documentation images step in
.github/workflows/docs.yml:xvfb-run -a uv run docs/images/<topic>/generate.py - Cache: add (or update) the matching fetch call in
tools/prefetch_doc_datasets.pyso the new script's data is pre-warmed in CI and the dataset cache key — which hashes onlytools/prefetch_doc_datasets.py— invalidates when the args change. If the script pulls a brand-new dataset, also add a dedicatedactions/cachestep in.github/workflows/docs.ymlkeyed off the same prefetch file (see the existing cache steps as a template).
Examples live in docs/examples/ as Jupytext notebooks (.py files with a
# %% [markdown] header). The gallery builder (tools/build_gallery.py) discovers
and executes them automatically. To add a new example:
- Place the script under the appropriate subdirectory in
docs/examples/. - Add the built output path (
docs/examples/_built/<subdir>/<name>.md) to thenavinzensical.toml. - Cache: the gallery cache key in
.github/workflows/docs.ymlalready usesdocs/examples/**/*.py, so it invalidates automatically when any example script changes. If the example loads data, add the matching fetch call totools/prefetch_doc_datasets.pyso it is pre-warmed in CI — the gallery renderer enforces light/dark output parity and a cold cache there will fail the build.
just pre-commit(orjust pc) - Run all pre-commit hooks (recommended)uv run ruff check . --fix- Run Ruff linter with auto-fixuv run ruff format .- Format code with Ruffuv run ty check src/- Run ty type checkinguv run codespell- Run spell checker
Pre-commit hooks include:
- ruff-check: Linting with auto-fix
- ruff-format: Code formatting
- ty: Type checking (src/ directory only)
- codespell: Spell checking
- numpydoc-validation: Docstring validation
just test(orjust t) - Run all tests with coveragejust test-verbose(orjust tv) - Run all tests with verbose outputjust generate-baselines- Regenerate visual regression test baselines (pytest-mpl)uv run pytest path/to/test_file.py- Run a single test fileuv run pytest path/to/test_file.py::TestClass::test_method- Run a single test
Coverage reports are generated automatically (terminal, HTML in htmlcov/, and XML).
- Use absolute imports:
from confusius.io import AUTCDAT - Group imports: standard library, third-party, local modules
- Use type-only imports when possible:
from typing import TYPE_CHECKING
- Use Ruff for auto-formatting (Black compatible)
- Line length: follow Ruff defaults
- Use double quotes for strings unless single quotes are needed for escaping
- Comments should not duplicate code.
- Good comments do not excuse unclear code.
- If you can't write a clear comment, there may be a problem with the code.
- Comments should dispel confusion, not cause it.
- Explain unidiomatic code in comments.
- Provide links to the original source of copied code.
- Include links to external references where they will be most helpful.
- Add comments when fixing bugs.
- Use
TODO:prefix for comments to mark incomplete implementations. - All comments should end with a period.
- Use comprehensive type hints with
numpy.typingfor arrays - Use
Literalfor string literal types - Use
TypedDictfor structured data dictionaries - Use
TypeAliasfor complex type definitions - Use
npt.NDArrayfor NumPy arrays with specific dtypes - Enable
py.typedmarker for type checking
- Functions/methods:
snake_case - Prefer imperative verb phrases for function names (for example,
get_source_dataarray,reconstruct_layer_dataarray,validate_inputs), not noun phrases. - Classes:
PascalCase - Constants:
UPPER_CASE - Private functions/methods: leading underscore
_function_name - Variables: descriptive
snake_casenames
- Use specific exceptions:
ValueError,TypeError,FileNotFoundError - Use
warnings.warn()for non-critical issues - Validate inputs early with descriptive error messages
- Use try/except blocks for external operations
- Use Zensical for documentation generation
- Use NumPy docstring format for all functions and methods, including private helpers
(prefixed with
_) — they require full Parameters, Returns, and Raises sections just like public ones - Include Parameters, Returns, Raises sections
- Document complex algorithms with references
- Use type hints in docstrings when helpful
- Include default values in the type parameter as
arg : type, default: value, orarg : type, optionalwhen the default isNone— never writearg : type or None, default: None - When describing the fallback behaviour of an optional parameter, write "If not
provided, ..." — not "If
None, ..." - For boolean parameters, start the description with "Whether to ..." — not "If
True/False, ..." - Use single backticks for inline code (Zensical/MarkDocs style, not Sphinx rst)
- Use full package names in docstrings (e.g.,
xarray.DataArraynotxr.DataArray) - In a parameter's
typefield (thename : typeline), writexarray.DataArrayin full. In the prose description below it, sayDataArray(noxarray.prefix, no backticks) — e.g. "a 3D DataArray sharingdata's dims, shape, and coordinates" - Use
list[...],tuple[...]syntax instead of "list of..." descriptions - Document array shapes as
(X, Y, Z) numpy.ndarrayor(X, Y, Z) xarray.DataArray
When a function returns multiple values, document each return value on a separate line in the Returns section:
Returns
-------
first_value : type
Description of the first return value.
second_value : type
Description of the second return value.Do not use tuple[type1, type2] as the return type in the docstring.
- Document module-level constants with triple-quoted docstrings placed immediately after the constant.
- Include a description of the constant's purpose and contents.
- For code adapted from other projects (e.g., nilearn), add a NOTICE file reference at the
module level: "Portions of this file are derived from [Project], which is licensed under
the [License]. See
NOTICEfile for details." - Use mkdocs-style links for cross-references to other functions/classes:
[function_name][confusius.module.path.function_name]or[ClassName][confusius.module.path.ClassName] - Example:
[fit][confusius.glm._models.OLSModel.fit]or[SeedBasedMaps][confusius.connectivity.SeedBasedMaps] - Do NOT use Sphinx-style reference sections (
.. [1])
- Use pathlib.Path for file operations
- Use context managers for file handling
- Prefer functional programming where appropriate
- Use list/dict comprehensions for simple transformations
- Keep functions focused on single responsibilities
- Cross-module shared utilities live in
confusius/_utils/<topic>.pywith public function names (e.g.confusius/_utils/coordinates.pyexportsget_coordinate_spacings, not_get_coordinate_spacings). The leading_on the package conveys "internal API"; names inside it are not prefixed. Group by topic (stack.py,coordinates.py,timing.py,io.py,atlas.py,plotting.py, etc.) — do not pile everything into a single file. - Module-private shared helpers live in
<module>/_utils.py(e.g.registration/_utils.py,plotting/_utils.py). Use this when a helper is shared by 2+ files inside one module but not used outside. Same naming convention: the file is private, the names within are public. - Never import a
_nameacross module boundaries. If you need a private function from another module, that is a signal to either (a) inline it, (b) make it public in the same file, or (c) promote it to_utils/. The underscore is a real boundary, not decoration — respect it.
- Use NumPy operations for array computations
- Use Dask for large array processing
- Prefer vectorized operations over loops
- Use appropriate data types to minimize memory usage
This project follows the Commitizen convention for commit messages.
<type>(<scope>): <short summary>
<body>
- feat: A new feature
- fix: A bug fix
- docs: Documentation only changes
- style: Code style changes (formatting, semicolons, etc.)
- refactor: Code changes that neither fix a bug nor add a feature
- perf: Performance improvements
- test: Adding or correcting tests
- chore: Changes to build process or auxiliary tools
Use a scope that describes the affected component:
io,nifti,autc,zarr- for I/O modulessignal,spatial,iq,reduce,clutter- for signal/IQ processingextract,validation- for signal extraction and input validationatlas,registration- for atlas integration and volume registrationconnectivity,multipose- for connectivity and multi-pose analysisqc- for quality controlxarray,io-accessor,plotting,napari- for UI and xarray extensionsdocs,mkdocs,api- for documentationtests- for test infrastructure
feat(nifti): add support for NIfTI sidecar metadata
docs(mkdocs): update installation instructions
test(nifti): add fixtures for 2D/3D/4D NIfTI files
refactor(iq): simplify power reduction algorithm
The changelog lives at docs/changelog.md. Entries are grouped by version tag, newest
first. When adding an entry, place it under the current development version (the
X.Y.Z.devN heading at the top), never under an already-released version.
Within the development version, entries go under section labels (emoji headings), ordered as below. Add only the labels you need:
### :boom: Breaking changes### :sparkles: Enhancements### :zap: Performance### :bug: Fixes### :books: Documentation### :wrench: Maintenance
Napari plugin related changes may carry an entry with a prefix [Napari plugin].
Write entries that are clear and concise, from the user's perspective: describe the
user-facing effect, not the implementation. End each entry with a link to its pull
request, e.g. ([#123](https://github.com/confusius-tools/confusius/pull/123)).
- No useless tests: Tests must fail if the function returns garbage. Avoid tests that only check shape preservation or that output differs from input.
- Concise test suite: No redundant tests. Each test should verify something unique.
- Test public API only: Do not test private functions (prefixed with
_). They are implementation details covered by testing the public functions that use them. - No
# pragma: no cover: Do not add coverage pragmas to skip lines. - Do not force unreachable tests: If a defensive branch is unreachable because an upstream library invariant prevents constructing that state (e.g., xarray coordinate shape consistency), do not add brittle tests just to satisfy coverage.
- Edge cases: Empty inputs, boundary conditions, special values.
- Error validation: Ensure expected exceptions are raised for invalid inputs.
- Reference implementations: Compare against known-correct implementations (e.g., scipy for wrappers, naive implementations for optimized code).
- Only when no reference implementation exists.
- Examples: mathematical properties (idempotence, commutativity, invariants).
- Prefer reference implementation tests when available.
- Use pytest fixtures for reusable test data. Always check for existing fixtures in
conftest.pyfiles before creating new test data. - Use
numpy.testing.assert_allclosefor floating-point comparisons. - Use
numpy.testing.assert_array_equalfor exact comparisons. - Use
pytest.raisesfor expected exceptions. - Use
pytest.warnsfor expected warnings. - Keep tests fast by using small array sizes.
- Use seeded random number generators for reproducibility.
- Follow napari's plugin testing guidelines: https://napari.org/dev/plugins/testing_and_publishing/test.html.
- Main message: prefer small unit tests over full GUI/integration tests. Trust napari to deliver callbacks/events correctly; test our plugin logic and observable widget/viewer state directly instead of trying to simulate every napari interaction end-to-end.
- Use napari pytest fixtures such as
make_napari_viewer/make_napari_viewer_proxyrather than building custom viewer setup/teardown by hand. - For ConfUSIus napari tests, prefer assertions on public/observable behavior (widget state, layer state, metadata, outputs) over module-private helper return values.
- Use
@pytest.mark.mpl_image_comparefor plot output tests. - Run
just generate-baselinesto regenerate baseline images after intentional plot changes. - Run tests with
uv run pytest --mplto enable image comparison checks.