plotnado is a lightweight Python package for creating genome browser-style plots from genomic data files (BigWig, BED, NarrowPeak, etc.). It provides two complementary interfaces:
- Python API — fluent builder for programmatic use
- CLI + YAML templates — file-driven workflow for non-programmers
plotnado/
├── figure.py # GenomicFigure — the core plotting API
├── template.py # Pydantic models for YAML templates (Template, TrackSpec, ...)
├── render.py # TemplateCompiler → RenderPlan (template-to-figure bridge)
├── theme.py # Theme / BuiltinTheme
├── tracks/ # Track implementations (BigWigTrack, BedTrack, ...)
│ ├── enums.py # Internal enums incl. TrackType (figure-layer)
│ └── ...
└── cli/
├── cli.py # Typer app entry point
├── init.py # `plotnado init` — infer template from files
├── plot.py # `plotnado plot` — render template for regions
├── validate.py # `plotnado validate` — check template validity
├── inference.py # Heuristics: infer track type/title from filename
└── grouping.py # Grouping strategies for init command
The fluent builder pattern allows chaining:
fig = (
GenomicFigure()
.bigwig("signal.bw", title="H3K27ac")
.narrowpeak("peaks.narrowpeak")
.genes("hg38")
.axis()
.scalebar()
)
fig.save("out.png", region="chr1:1000000-2000000")- Each method (
.bigwig(),.bed(), etc.) appends a track and returnsself from_template(path)builds a figure from a YAML template
YAML templates are human-readable, version-controllable, and editable:
genome: hg38
tracks:
- path: signal.bw
type: bigwig
title: H3K27ac
group: sample1
guides:
genes: true
axis: true
scalebar: trueTemplateCompiler.compile(template) → RenderPlan → GenomicFigure calls.
There are two separate enums. Do not confuse them:
| Enum | Location | Purpose |
|---|---|---|
TrackType |
plotnado/tracks/enums.py |
Internal figure enum; values match GenomicFigure method names |
TemplateTrackType |
plotnado/template.py |
User-facing YAML vocabulary; values appear in template files |
TemplateTrackType has more values (for example annotation and unknown) that map to existing figure methods. The mapping is defined in RenderPlan.get_track_by_method() in render.py.
TrackType = TemplateTrackType alias exists in template.py for backward compatibility.
- Create
plotnado/tracks/mytrack.pywith a class extendingTrack - Add aesthetics class if needed and register field names
- Add a method to
GenomicFigureinfigure.py(for example.mytrack(data, **kwargs)) - Add the alias in
GenomicFigure._alias_map() - Add a
TemplateTrackType.MYTRACK = "mytrack"value intemplate.py - Add the mapping in
RenderPlan.get_track_by_method()inrender.py - Export from
plotnado/tracks/__init__.pyandplotnado/__init__.py - Write tests
RenderPlan.get_track_by_method() maps TemplateTrackType values to GenomicFigure method names:
| TemplateTrackType | GenomicFigure method | Notes |
|---|---|---|
bigwig |
bigwig |
|
bedgraph |
bigwig |
BigWigTrack handles bedgraph natively |
bed |
bed |
|
narrowpeak |
narrowpeak |
|
gene |
genes |
|
links |
links |
|
annotation |
bed |
BED interval track with annotation semantics |
overlay |
overlay |
|
unknown |
bed |
Fallback |
TemplateCompiler.compile()must never mutate theTemplateargument- Resolved group indices go into
RenderPlan.resolved_group_indices, not back into the template - Group references are resolved case-insensitively against track
nameortitlefields
- Width override: always use
width if width is not None else plan.width, neverwidth or plan.width - Bedgraph method: there is no
GenomicFigure.bedgraph(); bedgraph files use.bigwig() TemplateTrackTypevsTrackType: import the right enum for the layer you are working in- CLI shim:
plotnado/cli/render.pyre-exports fromplotnado.render; import fromplotnado.renderin new code
Run: uv run pytest tests/
| Test file | Coverage |
|---|---|
test_template.py |
Template round-trip, YAML serialization |
test_render.py |
TemplateCompiler, no-mutation guarantee, autocolor |
test_inference.py |
Track type/title inference heuristics |
test_grouping.py |
Grouping strategies |
test_cli.py |
CLI integration via typer.testing.CliRunner |
Guidelines:
- Use
tmp_pathpytest fixture for file-based tests - Do not mock
GenomicFigure.plot()or.save()in unit tests - The no-mutation guarantee on
TemplateCompiler.compile()must always have a regression test
uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"
uv run pytest tests/Entry point: plotnado = "plotnado.cli.cli:main" (defined in pyproject.toml)
The main branch is protected; create a feature branch and open a PR for review before merging. Follow the Conventional Commits spec for commit messages.