Skip to main content

Run Graphviz from Python via WebAssembly — no native binary required

Reason this release was yanked:

incorrect license (whole project is EPL-2.0 to avoid conflicts with graphviz)

Project description

wasi-graphviz

Run Graphviz from Python via WebAssembly — no native Graphviz binary, no Node.js, no browser required.

Why this exists

Rendering DOT graphs from Python sits in an awkward gap.

  • Browser-based WASM renderers like @hpcc-js/wasm-graphviz ship Graphviz as a WebAssembly module — but it's an Emscripten build wrapped in JavaScript glue, designed to run in a browser or Node. Tools like easydot wrap that flow so you can call it from Python and display SVGs in a notebook, but the renderer itself still ultimately needs a JS runtime / browser surface to produce pixels — fine for interactive notebooks, awkward for headless static SVG generation in CI, batch jobs, or server-side pipelines.
  • System-binary Python wrappers like graphviz and pygraphviz shell out to a system-installed dot or link against libcgraph. That means every deployment target — laptops, CI runners, Lambda functions, Docker base images — has to install Graphviz separately, and your code has to spawn subprocesses or manage shared-library loading.

wasi-graphviz plugs the gap: a wasm32-wasi build of upstream Graphviz plus a thin Python wrapper. pip install is the entire install story, the wheel is ~440 KB, and rendering happens entirely in-process — no subprocesses, no system packages, no JS, no browser. Works the same in a notebook, a CI job, a serverless function, or an offline air-gapped environment.

The wheel itself does not include a WASM runtime — that's an explicit choice (see Installation below) so you can pick the runtime that matches your deployment constraints.

Installation

wasi-graphviz needs a runtime to execute the bundled graphviz.wasm. Two are supported, picked via extras:

# Recommended for most users — fast native runtime
pip install wasi-graphviz[wasmtime]

# Pure-Python runtime — slow, but works anywhere Python does
pip install wasi-graphviz[pywasm]

# Install both — `render(..., backend="auto")` will prefer wasmtime
pip install wasi-graphviz[all]

Choosing a backend

wasmtime pywasm
Implementation Native runtime (Rust) with Python bindings Pure-Python WASM interpreter
Speed ~0.3–7 ms per render (see below) ~3–130 s per render — 4–5 orders of magnitude slower
Install size ~15 MB wheel (compiled extensions) ~200 KB wheel
Platforms Linux/macOS/Windows on x86_64 + arm64 Anywhere CPython 3.11+ runs
Cold start Slightly heavier instantiation Fastest to import
Use when… Production, CI, notebooks, anything performance-sensitive Last-resort portability — Pyodide, exotic CPU/OS, no-native-deps environments

backend="auto" (the default) prefers wasmtime when available and silently falls back to pywasm, so most code can ignore the distinction. Force one explicitly when you have a reason — see Backend selection below.

Benchmarks

Median wall time per render on Apple M-series, Python 3.11, measured via pytest-benchmark (run yourself with uv run pytest tests/test_benchmarks.py -m perf --benchmark-only):

Graph wasmtime pywasm wasmtime speedup
10 edges 0.29 ms 3.7 s ~12,800 ×
100 edges 1.74 ms 32.5 s ~18,700 ×
400 edges 6.86 ms 133.3 s ~19,400 ×

pywasm is a pure-Python WASM interpreter, so the ratio is roughly "interpreted Python evaluating WASM bytecode" vs "native compiled code" — expect orders of magnitude, not factors. Use wasmtime unless your environment forbids native code.

Quick start

from wasi_graphviz import render

# Render a simple graph to SVG (uses wasmtime if available, falls back to pywasm)
svg = render("digraph G { a -> b; }")
print(svg.decode("utf-8"))

Usage

Basic rendering

from wasi_graphviz import render

# Render to SVG with default dot engine
svg = render("digraph G { a -> b; }")

# Use a different layout engine
svg = render("graph G { a -- b; }", engine="neato")

# Render to DOT format
output = render("digraph G { a -> b; }", format="dot")

Backend selection

See the trade-off table above for when to pick which.

from wasi_graphviz import render

# Auto-select (prefer wasmtime, fall back to pywasm)
svg = render("digraph G { a -> b; }", backend="auto")

# Force pywasm — pure Python, slow but maximally portable
svg = render("digraph G { a -> b; }", backend="pywasm")

# Force wasmtime — fast native runtime, requires compiled extension
svg = render("digraph G { a -> b; }", backend="wasmtime")

Error handling

from wasi_graphviz import render, RenderError

try:
    svg = render("not valid dot {")
except RenderError as e:
    print(f"Render failed: {e}")

Supported layout engines

All major Graphviz layout engines work:

  • dot — hierarchical layouts (default)
  • neato — spring model
  • circo — circular layout
  • fdp — force-directed placement
  • sfdp — scalable FDP
  • twopi — radial layouts
  • osage — array-based layouts
  • patchwork — treemaps

Supported output formats

The core plugin supports:

  • svg (default)
  • dot
  • json
  • ps
  • map
  • fig
  • tk

Architecture

The project consists of three layers:

  1. WASM artifact (graphviz.wasm)

    • Graphviz 14.1.5 compiled for wasm32-wasi
    • Exposes a plain C ABI: graphviz_render, graphviz_free, graphviz_last_error, graphviz_version
    • No Emscripten, no JS glue
  2. Python backends

    • PywasmBackend — pure-Python interpreter with built-in WASI support
    • WasmtimeBackend — fast native runtime with full WASI support
  3. Public API

    • render(dot, format="svg", engine="dot", backend="auto") -> bytes

Building from source

See BUILD.md for detailed build instructions.

Quick summary:

# Install build tools
pixi install

# Build the WASM artifact
python scripts/prepare_graphviz_wasi.py build/src/graphviz-14.1.5
pixi run cmake -S build/src/graphviz-14.1.5 -B build/graphviz-cmake \
  -DCMAKE_TOOLCHAIN_FILE=$(pwd)/native/wasm32-wasi-toolchain.cmake \
  ...
pixi run cmake --build build/graphviz-cmake --parallel
# ... compile wrapper, link, validate

Development

# Run tests (perf benchmarks are skipped by default)
uv run pytest

# Format and lint
uv run ruff check .
uv run ruff format .

# Run benchmarks comparing wasmtime vs pywasm across graph sizes
uv run pytest tests/test_benchmarks.py -m perf --benchmark-only

License & attribution

The Python wrapper, C wrapper (native/main.c), and build scripts in this repository are licensed under Apache-2.0 (see LICENSE).

The wheel bundles a compiled build of Graphviz, which is licensed under the Eclipse Public License 2.0 (EPL-2.0). The full EPL-2.0 text is shipped inside the wheel at wasi_graphviz/assets/GRAPHVIZ_LICENSE. Source for the bundled Graphviz version is available upstream: https://gitlab.com/graphviz/graphviz/-/tree/14.1.5.

Modifications applied to the Graphviz source before compilation are described in scripts/prepare_graphviz_wasi.py and are themselves licensed under EPL-2.0. See NOTICE for the full attribution.

wasi-graphviz is an unofficial repackaging and is not affiliated with or endorsed by the Graphviz project.


First functional v0.1.0a1 built with Kimi K2.6 in ~1h, single session (81% context used). Total cost: ~$1

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

wasi_graphviz-0.1.0a2.tar.gz (403.1 kB view details)

Uploaded Source

Built Distribution

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

wasi_graphviz-0.1.0a2-py3-none-any.whl (404.4 kB view details)

Uploaded Python 3

File details

Details for the file wasi_graphviz-0.1.0a2.tar.gz.

File metadata

  • Download URL: wasi_graphviz-0.1.0a2.tar.gz
  • Upload date:
  • Size: 403.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for wasi_graphviz-0.1.0a2.tar.gz
Algorithm Hash digest
SHA256 9381d11f40faa99fc4f66b4636b3439ef3a0e6d9b4cab9dd591d83b1eb5f3657
MD5 1b59d43bf91eb16148a42f1009388d82
BLAKE2b-256 646a5dd420e0d5478f70a3aaf2e90dd3466108ffdfd1d4c95d3f0289db4f9822

See more details on using hashes here.

Provenance

The following attestation bundles were made for wasi_graphviz-0.1.0a2.tar.gz:

Publisher: release.yml on pablormier/wasi-graphviz

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

File details

Details for the file wasi_graphviz-0.1.0a2-py3-none-any.whl.

File metadata

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

File hashes

Hashes for wasi_graphviz-0.1.0a2-py3-none-any.whl
Algorithm Hash digest
SHA256 b18e4b117697f081d4d70d23efd0de8c6b5125d5561f4197f6eead72bb7a23b9
MD5 0216e88a4179a98ad028906f7ce4adb1
BLAKE2b-256 184fd62d37b511d85c0192ad7ec2d6fa3fd61ea43d8cad6e71f945c6ffd1503a

See more details on using hashes here.

Provenance

The following attestation bundles were made for wasi_graphviz-0.1.0a2-py3-none-any.whl:

Publisher: release.yml on pablormier/wasi-graphviz

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