Skip to main content

Tiny browser-side Graphviz DOT rendering helpers for notebooks and web views

Project description

easydot

Graphviz rendered in the browser, from one line of Python. 100% client-side.

Python 3.10+ pip install easydot License: BSD-3-Clause No Dependencies marimo

pip install easydot
import easydot

easydot.display("digraph { A -> B -> C }")

Example

easydot 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, no conda, no apt-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 | marimo | 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.

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 graphviz Python package. easydot is 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

easydot-0.1.13.tar.gz (643.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

easydot-0.1.13-py3-none-any.whl (651.8 kB view details)

Uploaded Python 3

File details

Details for the file easydot-0.1.13.tar.gz.

File metadata

  • Download URL: easydot-0.1.13.tar.gz
  • Upload date:
  • Size: 643.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for easydot-0.1.13.tar.gz
Algorithm Hash digest
SHA256 7093372a486ab2de324620e44837885f570a74b4652d5d2a1274aa56025931a8
MD5 2933bbfe5e00928fea2e40ea7a6d259e
BLAKE2b-256 945a4eb7c5d8eaecc8f3d60cbf3c9edca35d6142946566e1a400cd1ba1035fe6

See more details on using hashes here.

Provenance

The following attestation bundles were made for easydot-0.1.13.tar.gz:

Publisher: publish.yml on pablormier/easydot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file easydot-0.1.13-py3-none-any.whl.

File metadata

  • Download URL: easydot-0.1.13-py3-none-any.whl
  • Upload date:
  • Size: 651.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for easydot-0.1.13-py3-none-any.whl
Algorithm Hash digest
SHA256 6bf35b4f5f537865e9638503fe191896afa4843a55c2ababa818bd6b78918713
MD5 818a31fc5d67ca584736436b3b0ead16
BLAKE2b-256 b2b3c4551af00698065fc3766fdb150e452be41b2af8ae1679872882c5b40912

See more details on using hashes here.

Provenance

The following attestation bundles were made for easydot-0.1.13-py3-none-any.whl:

Publisher: publish.yml on pablormier/easydot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page