Thanks for your interest in contributing to VisCy!
Please see the following steps for our workflow.
Please read the README for an overview of the project and how you can install and use the package.
We use issues to track bug reports, feature requests, and provide user support.
Before opening a new issue, please first search existing issues (including closed ones) to see if there is an existing discussion about it.
Any change made to the main branch needs to be proposed in a
pull request (PR).
If there is an issue that can be addressed by the PR, please reference it. If there is not a relevant issue, please either open an issue first, or describe the bug fixed or feature implemented in the PR.
This project uses uv for dependency management and is organized as a uv workspace monorepo.
Note
If you are an HPC system, we suggest making a symlink elsewhere first for the uv cache as your home directory will quickly get filled up.
mkdir -p /hpc/mydata/firstname.lastname/.cache/uv && ln -s /hpc/mydata/firstname.lastname/.cache/uv ~/.cache/uv
See uv installation docs.
If you have push permission to the repository:
git clone https://github.com/mehta-lab/VisCy.gitOtherwise, you can follow these instructions to fork the repository.
First, create a virtual environment with a supported Python version (3.11-3.13):
cd VisCy/
uv venv -p 3.13 # or 3.11 or 3.12This makes a virtual environment in .venv/ where the dependencies will be installed.
Then sync dependencies:
uv syncNote:
uv syncinstalls thedevgroup by default, which includes all development dependencies. See dependency groups for more details.
VisCy is organized as a workspace monorepo:
viscy/
├── pyproject.toml # Root workspace configuration
├── packages/
│ └── viscy-transforms/ # Image transforms subpackage
│ ├── pyproject.toml
│ └── src/
│ └── viscy_transforms/
└── src/
└── viscy/ # Umbrella package (re-exports from subpackages)Each package in packages/ is an independent Python package that can be:
- Developed in isolation
- Published to PyPI separately
- Installed independently by users
# Import directly from subpackages
from viscy_transforms import NormalizeSampledThen make the changes and track them with Git.
If you made code changes, make sure that there are also tests for them! Local test runs and coverage check can be invoked by:
# Run all tests
uv run pytest
# Run tests for a specific package
uv run pytest packages/viscy-transforms/
# Run with coverage
uv run pytest --cov=viscy_transformsWe use prek (a faster pre-commit runner) to automatically format and lint code prior to each commit. To minimize test errors when submitting pull requests, install the hooks:
uvx prek install
uvxruns tools in isolated, cached environments—no binaries added to your PATH and no dependencies installed in your project venv.
To run manually:
uvx prek run # run on staged files only
uvx prek run --all-files # run on all filesWe use ruff for linting and formatting:
uvx ruff check . # lint
uvx ruff check --fix . # lint and auto-fix
uvx ruff format . # formatWhen executed within the project root directory, ruff automatically uses the project settings.
Important: All ruff configuration lives in the root
pyproject.tomlonly. Sub-packages must not define their own[tool.ruff.*]sections — ruff does not inherit config, so any[tool.ruff.*]in a sub-package silently overrides the entire root config (includinglint.select,per-file-ignores, etc.).
Docstrings follow the numpy style
(convention = "numpy" in [tool.ruff.lint.pydocstyle]).
Zensical builds one site for the whole monorepo, from
zensical.toml and docs/ at the repo root. Doc tools live in the root doc
dependency group; subpackages carry none.
Preview with live reload:
uv sync --all-packages --group doc # --all-packages: mkdocstrings imports the packages
uv run python docs/_gen_versions.py # refresh the package version table
uv run zensical serve # http://localhost:8000Static build lands in site/ (git-ignored):
uv run zensical build --cleanAuthoring notes:
- Markdown lives in
docs/;navinzensical.tomlsets the order. ::: viscy_datarenders a package's API. A template override (docs/_templates/python/material/module.html.jinja) hides each package's top-level docstring — write overview prose in the Markdown page instead.docs/_gen_versions.pyrewrites the version table indocs/packages/index.md.
CI (.github/workflows/docs.yml) deploys via mike:
main updates dev, a vX.Y.Z tag publishes that version and moves stable.
We use git tags for versioning, and each package has it's own tag. Each package reads
uv-dynamic-versioning; the tag
prefix decides which package gets the version.
| Package | Tag prefix | Example tag |
|---|---|---|
viscy-data |
viscy-data- |
viscy-data-v0.2.1 |
viscy-models |
viscy-models- |
viscy-models-v0.4.0 |
viscy-transforms |
viscy-transforms- |
viscy-transforms-v0.1.3 |
viscy-utils |
viscy-utils- |
viscy-utils-v0.3.0 |
viscy (umbrella) |
none | v0.6.0 |
Per-package, independent. Tag bumps only its own package. Umbrella reads bare tags.
To cut a release:
# 1. clean main, latest tags
git checkout main && git pull && git fetch --tags
# 2. tag (one per package you ship)
git tag viscy-data-v0.2.1
# 3. build — version derived from the tag
uv build --package viscy-data --out-dir dist/
# 4. verify wheel name matches the tag before pushing
ls dist/ # viscy_data-0.2.1-py3-none-any.whl
# 5. push the tag (nothing public until this)
git push origin viscy-data-v0.2.1Note: no version pins between workspace members. A built wheel's
Requires-Distfor a sibling package (e.g.viscy-utils→viscy-data) carries no version constraint —[tool.uv.sources]workspace links resolve locally only, not in published metadata (astral-sh/uv#9811). If you publish to PyPI, add an explicit pin (e.g."viscy-data>=0.1,<0.2") to the dependent package'sdependencies.
- uv Overview
- uv sync - Sync dependencies and packages
- uv Workspaces - Monorepo management
- uv add - Adding dependencies
- uv run - Running commands in the environment
- Dependency groups