Tiny browser-side Graphviz DOT rendering helpers for notebooks and web views
Project description
pip install easydot
import easydot
easydot.display("digraph { A -> B -> C }")
Example
💡 Why easydot
Graphviz usually requires a native dot binary. That's fine on a laptop, but painful in CI images, slim containers, shared clusters, and browser runtimes like JupyterLite, Pyodide, or marimo. easydot packages browser rendering so pip install easydot is enough for notebooks.
- Pip-installable. No
brew, noconda, noapt-get, no Dockerfile changes. - Browser rendering. Layout runs client-side via Graphviz WASM, so it works inside sandboxed kernels and restricted hosts.
- Tiny notebook outputs. The WASM bundle is vendored and served once over loopback instead of inlined into every cell.
- Works offline. Assets ship in the package, with a local fallback when the CDN isn't reachable.
- Small API.
easydot.display(...)renders via_repr_html_in notebooks.
🔤 Why DOT
DOT is a small text format for graph diagrams. Many Python libraries and build tools can generate it.
- Common output format. NetworkX, pydot, pygraphviz, scikit-learn decision trees, PyTorch and TensorFlow model viz, Dask task graphs, Airflow DAGs, Terraform, Bazel, Ninja,
gprof2dot, and other tools can emit DOT. - LLM-friendly. Models can usually generate DOT for architecture diagrams, state machines, and dependency graphs.
- Plain text. Diffs cleanly, templates easily, pipes nicely.
- Graphviz features. Five layout engines (
dot,neato,fdp,circo,twopi), clusters, HTML-like labels, and styling.
🚀 Usage
pydot
pip install easydot[pydot]
import easydot, pydot
graph = pydot.Dot("example", graph_type="digraph")
graph.add_edge(pydot.Edge("A", "B"))
easydot.display(graph)
NetworkX
import easydot, networkx as nx
from networkx.drawing.nx_pydot import to_pydot
G = nx.DiGraph([("A", "B"), ("B", "C"), ("A", "C")])
easydot.display(to_pydot(G))
CLI
echo 'digraph { A -> B }' | easydot # render DOT to HTML on stdout
easydot --urls # print local asset server URLs
🔀 Source Modes
By default, easydot tries a pinned CDN URL first and falls back to the local server.
| Mode | Local | CDN | Best for |
|---|---|---|---|
auto |
yes | yes | Most setups (default; CDN first, then local fallback) |
local |
yes | no | Offline environments with no internet access |
cdn |
no | yes | Remote hosts where 127.0.0.1 isn't browser-reachable |
easydot.display("digraph { A -> B }", source="cdn")
Environment variables
Set a notebook-wide default without editing every call:
import os
os.environ["EASYDOT_SOURCE"] = "cdn" # auto | local | cdn
Only applies when source="auto". Explicit source= arguments still win.
For hosted marimo environments that protect generated iframe file URLs, force a self-contained iframe:
os.environ["EASYDOT_IFRAME_MODE"] = "srcdoc" # auto | managed | srcdoc | data
PyCharm notebooks are detected automatically and use a data: iframe because
their output recycling can detach and reattach srcdoc iframes while scrolling.
You can force that wrapper explicitly with EASYDOT_IFRAME_MODE="data".
The same modes are available per display call:
easydot.display("digraph { A -> B }", iframe_mode="data")
📓 marimo
Works out of the box. easydot detects marimo and uses its iframe display helper automatically, since marimo doesn't execute inline scripts from plain text/html outputs. All source modes work.
The managed iframe mode uses the installed notebook iframe helper when
available; otherwise it falls back to srcdoc.
uv run marimo edit examples/demo.py # edit the demo
uv run marimo run examples/demo.py --headless --port 2718 --no-token # read-only preview
⏳ Large Graphs
Browser rendering is asynchronous relative to notebook cell execution: a cell
can finish before the browser has loaded Graphviz WASM and produced the SVG.
By default, easydot renders on the output iframe's main thread and shows an
in-progress indicator while the graph is rendering. You can opt into Web Worker
rendering for large graphs.
easydot.display(dot, worker=False) # default: render on the output iframe's main thread
easydot.display(dot, worker="auto") # try a worker, visibly fall back if unavailable
easydot.display(dot, worker=True) # require a worker; no main-thread fallback
If worker rendering is unavailable and worker="auto" is used, easydot shows
a warning before falling back to main-thread rendering. Large graphs may freeze
that output iframe until Graphviz finishes in fallback mode.
🔌 Library Integration
For libraries that generate their own HTML, use the lower-level asset API:
from easydot import asset_urls
js_url = asset_urls()["js"]
const mod = await import(jsUrl);
const graphviz = await mod.Graphviz.load();
const svg = graphviz.layout("digraph { A -> B }", "svg", "dot");
Need server-side rendering to files? Use native Graphviz or the
graphvizPython package.easydotis browser-only by design.
Runtime model
The asset server is intentionally narrow:
- Binds only to
127.0.0.1 - OS-assigned ephemeral port
- Serves only known packaged files (no directory browsing)
- Long-lived cache headers
- Shuts down automatically when the Python process exits
📜 License
| Component | License |
|---|---|
easydot Python code |
BSD-3-Clause |
| Vendored Graphviz WASM | Apache-2.0, from @hpcc-js/wasm-graphviz. Pinned version in src/easydot/_version.py |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file easydot-0.1.15.tar.gz.
File metadata
- Download URL: easydot-0.1.15.tar.gz
- Upload date:
- Size: 644.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
09edb585d2c68ee907558e88f5fe4aeb7a26a069c784934c523aacfe00837f30
|
|
| MD5 |
24ae6f9b2eae6904ee110db4a14c0392
|
|
| BLAKE2b-256 |
7567254bf80976731a4af891553ea4e96e848226dd143d32f0c6374824ceb2bd
|
Provenance
The following attestation bundles were made for easydot-0.1.15.tar.gz:
Publisher:
publish.yml on pablormier/easydot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
easydot-0.1.15.tar.gz -
Subject digest:
09edb585d2c68ee907558e88f5fe4aeb7a26a069c784934c523aacfe00837f30 - Sigstore transparency entry: 1364603524
- Sigstore integration time:
-
Permalink:
pablormier/easydot@3724ab3d9dcbb1645cca97aeb564fc759c213840 -
Branch / Tag:
refs/tags/v0.1.15 - Owner: https://github.com/pablormier
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3724ab3d9dcbb1645cca97aeb564fc759c213840 -
Trigger Event:
push
-
Statement type:
File details
Details for the file easydot-0.1.15-py3-none-any.whl.
File metadata
- Download URL: easydot-0.1.15-py3-none-any.whl
- Upload date:
- Size: 652.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
20ef329c46ac17c7501e13dbc5171f103cc4ef02d57604475fe9a5149cc14807
|
|
| MD5 |
2049fb741344ad736a6aafebf5bafe6b
|
|
| BLAKE2b-256 |
c10101b248ccee36db9ed725ec6792d495c8f0cf58595bbe8515c8ab64151085
|
Provenance
The following attestation bundles were made for easydot-0.1.15-py3-none-any.whl:
Publisher:
publish.yml on pablormier/easydot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
easydot-0.1.15-py3-none-any.whl -
Subject digest:
20ef329c46ac17c7501e13dbc5171f103cc4ef02d57604475fe9a5149cc14807 - Sigstore transparency entry: 1364603545
- Sigstore integration time:
-
Permalink:
pablormier/easydot@3724ab3d9dcbb1645cca97aeb564fc759c213840 -
Branch / Tag:
refs/tags/v0.1.15 - Owner: https://github.com/pablormier
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3724ab3d9dcbb1645cca97aeb564fc759c213840 -
Trigger Event:
push
-
Statement type: