Skip to main content

tree-annotated-plot: annotate the axis of an Altair / Vega-Lite plot with a phylogenetic tree

PyPI version Python versions tests Docs License: MIT Code style: black Ruff

This is a Python package from the Bloom lab that allows you to combine an Altair / Vega-Lite plot with a Nextstrain JSON of a phylogenetic tree so that the tree is aligned to annotate strains on the axis of the plot. See https://jbloomlab.github.io/tree-annotated-plot/ for detailed documentation.

Note: charts must be saved from altair 6+ (Vega-Lite v6). Older specs raise by default; pass --no-strict-version (CLI) or strict_version=False (Python) to override at your own risk. See the docs for details.

Installation

Released on PyPI. Requires Python 3.11+.

pip install tree-annotated-plot

To install the bleeding edge directly from this repository:

pip install git+https://github.com/jbloomlab/tree-annotated-plot.git

For a development checkout, see Installation (development) below.

Changelog

See CHANGELOG.md for the version history.

Notes for developing the package

Installation (development)

python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev,docs]"

pyproject.toml is the canonical source for the supported Python version and runtime dependencies.

Visual verification after code changes

This package has automated tests for chart structure (encodings, sort order, tip reconciliation, scale-bar arithmetic, etc.), but not for chart appearance — there are no image-snapshot regression tests, because rendering is sensitive to fonts, vl-convert-python versions, and platform differences in ways that make pixel-level comparison flaky in practice.

So the discipline is manual: any time you change code that affects rendering (src/tree_annotated_plot/_plot.py, src/tree_annotated_plot/_tree.py, the example scripts under examples/, or anything they touch), run the full check below and eyeball every example before considering the change done.

bash scripts/check.sh        # lint + format + pytest (all 100+ tests)
bash scripts/build_docs.sh   # regenerates docs/images + docs/charts and runs mkdocs --strict

Then open site/examples.html in a browser and, for each example:

  1. Look at the embedded SVG screenshot. Does it match what the feature you changed should produce? Are tip rows still aligned with the chart rows? Is the tree's tip-end facing the chart? Is the scale bar (if enabled) centered and labeled correctly?
  2. Click "Open the interactive chart →". Hover tips to confirm tooltips fire; if there's a cohort selector (Kikawa examples), toggle it; pan/zoom if relevant.
  3. Compare against the deployed site (which still reflects main before your change) for what "good" looks like.

If something visibly regresses, fix it before pushing — there's no CI gate that will catch a visual regression for you.

Continuous integration tests

.github/workflows/tests.yml runs on every push to main and every pull request: it installs the package, fetches the upstream Kikawa Auspice JSONs (so the real-data tests actually exercise rather than skip), builds the Kikawa titer chart specs, and runs ruff check, black --check, and pytest tests/. This mirrors scripts/check.sh — green locally should mean green in CI.

A separate workflow, .github/workflows/docs.yml, builds the docs site on every push to main and every PR (catching broken mkdocstrings refs / missing images / unresolved cross-links before merge), and additionally deploys site/ to GitHub Pages on push to main.

A third workflow, .github/workflows/release.yml, builds the sdist + wheel when a v* tag is pushed, publishes them to PyPI, and creates a matching GitHub Release with auto-generated notes and the wheel/sdist attached (see "Releasing a new version" below).

Releasing a new version

Releases are fully automated by tag push. Trusted publishing (OIDC) means no API token or password is stored in the repo — PyPI authenticates the workflow via its repo + workflow filename + environment.

One-time setup (only needed before the first release):

  1. Reserve tree-annotated-plot on PyPI (first successful publish creates it; or claim it manually via pip install build && python -m build && twine upload dist/* from a maintainer machine).
  2. On PyPI, go to the project → "Publishing" tab → "Add a new pending publisher". Fill in: owner jbloomlab, repository tree-annotated-plot, workflow filename release.yml, environment pypi.
  3. (Optional but recommended) On GitHub, repo Settings → Environments → New environment → name pypi. Add yourself as a required reviewer if you want a manual approval gate before each release lands on PyPI.

Per-release recipe (every time):

# 1. Edit pyproject.toml and bump `version = "X.Y.Z"`.
# 2. Commit and tag (the workflow verifies they match before publishing).
git commit -am "release vX.Y.Z"
git tag vX.Y.Z
git push && git push --tags

The release workflow then builds, verifies tag ⇋ version match, and publishes to PyPI.

Documentation

The docs are at https://jbloomlab.github.io/tree-annotated-plot/. They are auto-deployed by GitHub Actions on every push to main.github/workflows/docs.yml installs the package + docs extras, runs scripts/generate_docs_assets.py to render images and standalone interactive HTML for each example, then mkdocs build --strict, and uploads site/ as a GitHub Pages artifact.

Building documentation locally

For day-to-day editing of the docs, use MkDocs's live-reload server (saves go straight to your browser):

mkdocs serve              # http://localhost:8000

To do this on the Fred Hutch remote server, do:

mkdocs serve -a $(hostname -i | awk '{print $1}'):$(fhfreeport)

For the strict build that matches CI (catches broken mkdocstrings refs, missing images, unresolved cross-links):

bash scripts/build_docs.sh

This first runs scripts/generate_docs_assets.py (so the example SVGs and interactive HTMLs exist before MkDocs reads docs/), then mkdocs build --strict. Output lands in site/.

Adding a new example

The docs follow a single template per example: a short motivation, an embedded SVG screenshot, a link to the fully-interactive standalone HTML, and the commands to reproduce (CLI form first, Python API form second). docs/examples.md shows the existing shape. To add another:

  1. Drop a runnable script into examples/. Keep helpers at module level (callable from outside) so the asset script can import them.
  2. Add a clause to scripts/generate_docs_assets.py that imports the script and calls tree_annotated_plot.plot(...) followed by saving both <name>.svg (into docs/images/) and <name>.html (into docs/charts/). Both directories are gitignored — only the asset script writes there.
  3. Add a section to docs/examples.md matching the template.
  4. Run bash scripts/build_docs.sh and open site/examples.html to verify. Push when happy; the deployed site updates on the next CI run.

Release files for tree-annotated-plot 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tree-annotated-plot 0.4.0
File Size Uploaded
tree_annotated_plot-0.4.0.tar.gz 107.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tree-annotated-plot 0.4.0
File Interpreter ABI Platform
tree_annotated_plot-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 157.1 kB

Release files / tree_annotated_plot-0.4.0.tar.gz

Download URL tree_annotated_plot-0.4.0.tar.gz
Size 107.1 kB
Tags Source
SHA-256 checksum
How to use checksums
2c47f1e74654196e198101e07a6dcbbd4d912361b20f35d8dcca0dc8eaf2ae78
BLAKE2b-256 checksum
How to use checksums
2e72bb32fa834d17bf48ee4173da17405912ff03ee74d8859b942d0e5bec0f7a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 20, 2026.

Transparency log

Release files / tree_annotated_plot-0.4.0-py3-none-any.whl

Download URL tree_annotated_plot-0.4.0-py3-none-any.whl
Size 50.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
25539acfcd3f9d4f49d7509e0aa60ee2adeafdc627d4dccc74d29e1117996af3
BLAKE2b-256 checksum
How to use checksums
5e51ec18e6bc10f487604185f377eb02d8bc92f23f56c4e504e5a1b0e784f51b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 20, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page