This repository follows a set of standard development practices for modern Python packages.
For developers who want to get started quickly with uv:
# Clone and setup
git clone https://github.com/talmolab/sleap-io && cd sleap-io
uv sync --all-extras
# Make changes on a new branch
git checkout -b yourname/feature-name
# Before committing - format and lint
uv run ruff format sleap_io tests && uv run ruff check --fix sleap_io tests
# Run tests
uv run pytest tests/
# Check coverage
uv run pytest -q --maxfail=1 --cov --cov-branch && rm .coverage.* && uv run coverage annotate- Clone the repository:
git clone https://github.com/talmolab/sleap-io && cd sleap-io - Install the package in development mode:
With
uv(recommended):Or withuv sync --all-extrasconda:Or with pip:conda env create -f environment.yml && conda activate sleap-iopip install -e .[dev,all] - Test that things are working:
Or if using conda/pip:
uv run pytest testspytest tests
To reinstall the environment from scratch:
With uv:
uv sync --all-extras --refresh
Or with conda:
conda env remove -n sleap-io && conda env create -f environment.yml
We also recommend setting up ruff to run automatically in your IDE.
If using uv, ruff is already included in the dev dependencies and can be run with:
# Auto-format and fix linting issues
uv run ruff format sleap_io tests && uv run ruff check --fix sleap_io tests
# Check without making changes
uv run ruff format --check sleap_io tests
uv run ruff check sleap_io tests
Alternatively, you can install it globally with pipx:
pip install pipx
pipx ensurepath
pipx install ruff
This will make ruff available everywhere (such as VSCode), but will not be dependent on your conda base environment.
Once you're set up, follow these steps to make a change:
- If you don't have push access to the repository, start by making a fork of the repository.
- Switch to the
mainbranch andgit pullto fetch the latest changes. - Create a new branch named
<username>/<feature_name>with a descriptive title for the change. For example:talmo/nwb_supportortalmo/update_dependencies. - Make your changes and ensure code quality:
# Format and lint your code before committing uv run ruff format sleap_io tests && uv run ruff check --fix sleap_io tests # Run tests to ensure nothing broke uv run pytest tests/ # Check test coverage for modified files uv run pytest --cov=sleap_io --cov-report=term
- Push as many commits as you want. Descriptive commit messages and titles are optional but recommended.
- Open a Pull Request of your new branch against
mainwith a description of your changes. Feel free to create a "Draft" pull request to work on it incrementally. - Write a comprehensive PR description including:
- Summary of changes
- Example usage (for new features)
- API changes (if any)
- Testing approach
- Design decisions and rationale
- Once the tests pass, request a review from a core developer and make any changes requested.
- Once approved, perform a squash merge against
mainto incorporate your changes.
This repository employs continuous integration via GitHub Actions to enforce code quality.
See the .github/workflows folder for how our checks are implemented.
This package uses setuptools as a packaging and distribution system.
Our package configuration is defined in pyproject.toml which contains:
- Build system configuration (using setuptools)
- Package metadata and dependencies
- Optional dependencies (extras)
If new dependencies need to be introduced (or if versions need to be fenced), specify these in pyproject.toml in the dependencies section. For development-only dependencies (i.e., packages that are not needed for distribution), add them to the [project.optional-dependencies] → dev section. For optional features like OpenCV support, add them to a dedicated extras group (e.g., opencv).
These dependencies will only be installed when specifying the extras like: pip install -e .[dev,all] or pip install sleap-io[dev,all]. With uv, use uv sync --all-extras to install all optional dependencies.
Available extras:
dev: Development tools (pytest, ruff, etc.)opencv: OpenCV support for video processingav: PyAV support for video processingall: All optional dependencies (opencv + av)
Best practices for adding dependencies include:
- Use permissive version ranges so that the package remains future- and backward-compatible without requiring new releases.
- Don't pin to a single specific versions of dependencies unless absolutely necessary, and consider using platform-specific specifiers.
For more reference see:
- PEP 517 - pyproject.toml-based builds
- PEP 518 - Specifying build dependencies
- PEP 508 - Dependency specification for Python Software Packages
Note: We recommend using uv for fast, reliable Python environment management. For backwards compatibility, a minimal conda environment is defined in environment.yml that simply installs the package via pip.
Testing is done via pytest.
Tests should be created in the tests/ subfolder following the convention test_{MODULE_NAME}.py which mimics the main module organization.
It is highly recommended checking out other existing tests for reference on how these are structured.
All tests must pass before a PR can be merged.
Tests will be run on every commit across multiple operating systems and Python versions (see .github/workflows/ci.yml).
# Run full test suite
uv run pytest tests/
# Run a specific test module
uv run pytest tests/io/test_slp.py -v
# Run a specific test function
uv run pytest tests/io/test_slp.py::test_load_slp -v
# Run with coverage report
uv run pytest --cov=sleap_io --cov-report=term tests/
# Quick coverage check with line-by-line annotations
uv run pytest -q --maxfail=1 --cov --cov-branch && rm .coverage.* && uv run coverage annotate
# This creates .py,cover files next to each module showing line-by-line coverage
# Check which files changed in your PR (useful for targeted testing)
git diff --name-only $(git merge-base origin/main HEAD)We check for coverage by parsing the outputs from pytest and uploading to Codecov.
All changes should aim to increase or maintain test coverage.
To standardize formatting conventions and linting, we use ruff.
It's highly recommended to set this up in your local environment so your code is auto-formatted and linted before pushing commits.
Adherence to the ruff code style and linting rules is automatically checked on push (see .github/workflows/ci.yml).
We require that all non-test code follow the Google Python Style Guide conventions. This is checked via ruff.
For example, a method might be documented as:
def load_tracks(filepath: str) -> np.ndarray:
"""Load the tracks from a SLEAP Analysis HDF5 file.
Args:
filepath: Path to a SLEAP Analysis HDF5 file.
Returns:
The loaded tracks as a `np.ndarray` of shape `(n_tracks, n_frames, n_nodes, 2)`
and dtype `float32`.
Raises:
ValueError: If the file does not contain a `/tracks` dataset.
See also: save_tracks
"""
with h5py.File(filepath, "r") as f:
if "tracks" not in f:
raise ValueError(
"The file does not contain a /tracks dataset. "
"This may not have been generated by SLEAP."
)
tracks = f["tracks"][:]
return tracks.astype("float32")Notes:
- The first line should fit within the 88 character limit, be on the same line as the initial
""", and should use imperative tense (e.g., "Load X..." not "Loads X..."). - Use backticks (`) when possible to enable auto-linking for documentation.
- Always document shapes and data types when describing inputs/outputs that are arrays.
Adherence to the docstring conventions is automatically checked on push (see .github/workflows/ci.yml).
This package follows standard semver version practices, i.e.:
{MAJOR}.{MINOR}.{PATCH}
For alpha/pre-releases, append a{NUM} to the version.
Valid examples:
0.0.1
0.1.10a2
The PyPI-compatible package settings are in pyproject.toml.
The version number is set in sleap_io/version.py in the __version__ variable. This is read automatically by setuptools during installation and build.
To build the package locally:
# Build source distribution and wheel
uv build
# Build artifacts will be placed in dist/
ls dist/For more details, see the uv guide on building packages.
Publishing to PyPI is handled automatically via GitHub Actions using Trusted Publisher. This means no tokens or passwords are needed for releases from GitHub.
For manual/local publishing with uv (rarely needed):
# Build and publish
uv build
uv publishSee the uv publishing documentation for more details.
To trigger an automated build and release (via the .github/workflows/build.yml action), publish a Release. The GitHub Action will automatically authenticate with PyPI using Trusted Publisher and upload the built packages.
- Install
sleap-iowith thedevdependencies:- With
uv:uv sync --all-extras - With pip:
pip install -e ".[dev]" - With conda:
conda env create -f environment.yml
- With
- Build and tag a new version of the docs:
uv run mike deploy --update-aliases 0.1.4 latest - Preview live changes locally with:
uv run mike serve - Manually push a specific version with:
uv run mike deploy --push --update-aliases --allow-empty 0.1.4 latest
If you encounter cv2/OpenCV import errors when running individual tests:
- Try running the entire test module instead:
uv run pytest tests/io/test_video.py - Or run the full test suite:
uv run pytest tests/ - This is a known OpenCV issue with importing submodules
sleap-io has optional video backend dependencies:
- Install all backends:
uv sync --all-extrasorpip install -e .[all] - Install OpenCV only:
pip install -e .[opencv] - Install PyAV only:
pip install -e .[pyav]
If you're having environment problems:
- With
uv:uv sync --all-extras --refresh - With conda:
conda env remove -n sleap-io && conda env create -f environment.yml
To see which files you've modified (useful for targeted testing):
git diff --name-only $(git merge-base origin/main HEAD)