Canopy
Orbital SVG visualizations of codebase health.
Canopy analyses your Python project — complexity, dead code, churn, and module structure — then renders a single SVG diagram you can embed in your README or CI artifacts. Click the diagram below for an interactive view with tooltips, zoom and pan.
What it shows
| Visual element | Meaning |
|---|---|
| Node colour | Health — green (healthy MI), amber (moderate), red (unhealthy) |
| Node size | Lines of code |
| Pulse ring | High git churn (recent changes) |
| Spots | Dead code detected by Vulture |
| Rings | Architectural layers defined in canopy.yml |
| Edges | Import dependencies between modules |
Installation
pip install canopy-code
Canopy shells out to radon and vulture, so install them too:
pip install "canopy-code[tools]"
For development:
git clone https://github.com/bruno-portfolio/canopy-code.git
cd canopy-code
pip install -e ".[dev,tools]"
pre-commit install
Quick Start
# Analyse the current directory
canopy run .
# Specify a project path and output file
canopy run ./my-project --output docs/canopy.svg
# Generate SVG + interactive HTML viewer
canopy run . --output docs/canopy.svg --html docs/canopy.html
# Use a custom config
canopy run . --config path/to/canopy.yml
Configuration
Create a canopy.yml (or canopy.yaml) at the project root. All fields
are optional — sensible defaults apply.
project: myproject # display name (default: directory name)
source: src/myproject # source root relative to project (default: ".")
module_depth: 2 # how many levels to group (default: 2)
ignore: # glob patterns to exclude (future)
- "tests/**"
layers: # architectural ring grouping
core:
modules: ["_core", "domain"]
infra:
modules: ["_cache", "_db"]
label: Infrastructure
vulture:
min_confidence: 60 # Vulture confidence threshold (default: 60)
exclude_types: # Vulture result types to ignore
- attribute
git:
churn_days: 30 # lookback window for churn (default: 30)
thresholds:
mi_healthy: 40 # MI score above this is green (default: 40)
mi_moderate: 20 # MI score above this is amber (default: 20)
churn_high: 20 # commits above this triggers pulse (default: 20)
min_loc: 50 # modules below this LOC get collapsed (default: 50)
output:
path: docs/canopy.svg # output file path (default: canopy.svg)
width: 1000 # SVG width in pixels (default: 1000)
height: 800 # SVG height in pixels (default: 800)
GitHub Action
Add this workflow to .github/workflows/canopy.yml to regenerate the
diagram on every push to main:
name: Canopy
on:
push:
branches: [main]
jobs:
canopy:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history for churn data
- uses: bruno-portfolio/canopy-code@main
By default, the action creates a pull request with the updated diagram. This works with branch protection rules and required status checks.
If your repo has required status checks on PRs, pass a PAT so the PR
triggers your test workflows (the default GITHUB_TOKEN won't):
- uses: bruno-portfolio/canopy-code@main
with:
token: ${{ secrets.CANOPY_PAT }}
Create a fine-grained PAT
with Contents: Read and write + Pull requests: Read and write scoped to
your repo, then add it as a repository secret named CANOPY_PAT.
For repos without branch protection, you can push directly:
- uses: bruno-portfolio/canopy-code@main
with:
strategy: push
Note:
fetch-depth: 0is required for accurate churn data. Without it the clone is shallow and churn will be unavailable (Canopy warns and continues with churn = 0).
Embedding in README
After the SVG is generated, reference it in your README:

GitHub renders inline SVGs natively — no external hosting needed.
To link the static SVG to an interactive HTML viewer on GitHub Pages:
<p align="center">
<a href="https://your-user.github.io/your-repo/canopy.html">
<img src="docs/canopy.svg" width="100%" />
</a>
</p>
The HTML viewer is self-contained (zero external dependencies) and provides hover tooltips, click-to-pin, zoom (scroll) and pan (drag).
Limitations
- Dynamic imports (
importlib.import_module,__import__) are not detected by the static AST parser. TYPE_CHECKINGimports are treated as real imports (no special handling yet).- Shallow clones produce no churn data — use
fetch-depth: 0in CI. exclude_typesin Vulture config is a v1 allowlist; per-module exclusions are not yet supported.ignorepatterns are declared in config but not yet wired through collectors (planned for a future release).
License
Metadata
Release files for canopy-code 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| canopy_code-0.1.0.tar.gz | 59.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| canopy_code-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 92.5 kB
Release files / canopy_code-0.1.0.tar.gz
| Download URL | canopy_code-0.1.0.tar.gz |
|---|---|
| Size | 59.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1c680cac91164a3534b171fe630615564e9866043adc40b99176fcbf550694af
|
|
BLAKE2b-256 checksum How to use checksums |
7c1cc88a28091a644a17d1daf397d83e100e647a159bf94c2881e0828df208d5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
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 Mar 4, 2026.
Transparency logRelease files / canopy_code-0.1.0-py3-none-any.whl
| Download URL | canopy_code-0.1.0-py3-none-any.whl |
|---|---|
| Size | 33.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7c50a9b272f57964fd1a5d58cecdc3160abce54836eb72d944402911d91a7f21
|
|
BLAKE2b-256 checksum How to use checksums |
3d16f68a8f196eeb98f145300c15f7b0158a6e1763bca3dc8ef1f2fc87ae9794
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
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 Mar 4, 2026.
Transparency log