Skip to main content

d2-python

d2-python banner

Build D2 diagrams with Python code.

D2 is a language for writing diagrams as text. d2-python lets you build that text from code, so you can draw diagrams from data, loops and functions. You describe shapes and connections as typed Python objects, and the library writes the D2 and renders it to SVG, PNG, PDF or ASCII.

This project is a maintained hard fork of MrBlenny/py-d2. See Coming from py-d2 if you used it.

Install

Add d2-python to your project with uv. The bin extra also installs the official D2 binary, so d2.render works without installing D2 yourself:

uv add 'd2-python[bin]'

Run a single script without a project:

uv run --with 'd2-python[bin]' diagram.py

Run the D2 command itself without installing anything, or install it once so d2 is on your PATH:

uvx d2-bin --version
uv tool install d2-bin

With pip, use pip install 'd2-python[bin]'. If you already have the d2 command on your PATH, you can leave out the bin extra.

Quick start

import d2

diagram = d2.Diagram(
    shapes=[
        d2.Shape(name="api", label="API"),
        d2.Shape(name="db", label="Postgres", shape=d2.ShapeType.cylinder),
    ],
    connections=[d2.Connection("api", "db", "reads and writes")],
)

print(diagram)  # the D2 text
open("graph.svg", "wb").write(d2.render(diagram))
print(d2.render(diagram, "txt").decode())  # ASCII art

print(diagram) prints this D2 text:

api: API
db: Postgres {
  shape: cylinder
}
api -> db: reads and writes

The ASCII render looks like this:

    ┌──────┐
    │ API  │
    │      │
    └──────┘
        │
reads and writes
        │
        ▼
    .-‾‾‾‾-.
   │╲-____-╱│
   │        │
   │Postgres│
   │        │
    ╲-____-╱

Building diagrams

Every feature below has a runnable script in examples/.

Shapes take a type, an icon, a tooltip and a link:

db = d2.Shape(
    name="db",
    shape=d2.ShapeType.cylinder,
    icon="https://icons.terrastruct.com/essentials%2F117-database.svg",
    tooltip="Primary database",
    link="https://example.com/runbook",
)

A shape can hold other shapes. Connect nested shapes with a dotted path:

backend = d2.Shape(
    name="backend", shapes=[d2.Shape(name="api"), d2.Shape(name="worker")]
)
d2.Connection("backend.api", "backend.worker")

Styles set colors, borders, fonts and effects:

d2.Style(fill="#dbeafe", stroke="navy", stroke_width=2, bold=True, shadow=True)

Connections take a direction, a label, a style and arrowheads, including crow's foot ends for data models:

d2.Connection(
    "orders",
    "users",
    "belongs to",
    direction=d2.Direction.TO,
    style=d2.Style(stroke="gray", animated=True),
    source_arrowhead=d2.Arrowhead(d2.ArrowheadShape.CF_MANY),
    target_arrowhead=d2.Arrowhead(d2.ArrowheadShape.CF_ONE_REQUIRED),
)

A label can be Markdown, LaTeX or code:

d2.Shape(name="notes", label=d2.Text("# Notes\nSome **bold** text", "md"))

Other features:

  • SQL tables with constraints, and UML class shapes with fields and methods.
  • Grids, with grid_rows, grid_columns and gaps.
  • Reusable style classes, set on the diagram and used by name.
  • Layers, scenarios and steps, which are extra boards in one diagram.
  • Diagram settings through d2.Config, such as the layout engine, theme and sketch mode.
  • Variables with ${name} substitution, and legends.
  • Label and icon positions, and fixed top and left positions.
  • Comments at the top of the file.

Rendering

d2.render(diagram, format) runs d2 and returns the result as bytes. It uses the binary from d2-bin when that is installed, and otherwise the d2 command on your PATH. You can also pass D2 text as a string.

Option Values
format "svg" (default), "png", "pdf", "gif", "pptx" or "txt" for ASCII
layout "dagre", "elk" or "tala"
theme, dark_theme a D2 theme id
target the board to render: the root by default, or a path such as "layers.detail"
Others sketch, pad, scale, center, omit_version, no_xml_tag, salt, animate_interval, ascii_mode, timeout

If d2 fails, render raises d2.D2Error with d2's message.

Checking and formatting D2

d2.validate runs d2 validate and raises d2.D2Error when the source is wrong. d2.fmt returns the source formatted by d2 fmt. Both take a Diagram or a string, so you can use them to check D2 text that a person or a language model wrote.

d2.validate("a -> b")
print(d2.fmt("a->b"))

validate checks the syntax and meaning of the source. It does not lay the diagram out, so a few errors only show when you render.

How values are checked

The library checks values when you create an object, so a mistake fails in your code instead of in d2.

  • Names and labels that D2 cannot read as written are quoted for you, e.g. a name with a dot or a D2 keyword such as shape. Connection endpoints are written as given, so they can be dotted paths.
  • A color can be a CSS color name, a 3 or 6 digit hex code with or without #, a gradient or a ${variable}. You can pass a string or a d2.Color. The library writes hex codes in lowercase with # and in quotes, because a bare # starts a comment in D2. Any other value raises ValueError.
  • Numbers that D2 limits are checked, e.g. opacity must be from 0 to 1.

Coming from py-d2

Import d2 instead of py_d2, and drop the D2 prefix from class names, e.g. d2.Shape instead of py_d2.D2Shape. The pull requests that were open on py-d2 are merged here, and most of its open issues are fixed. Issues and pull requests are welcome on this repository.

Development

You need uv.

git clone git@github.com:h0rv/d2-python.git && cd d2-python
uv sync --extra bin

uv run pytest
uv run ruff check
uv run ruff format
uv run ty check

To redraw the banner, run uv run python docs/banner.py.

License

d2-python is MIT licensed. The d2-bin package contains the unmodified D2 binary, which is licensed under the MPL-2.0 by Terrastruct.

Metadata

Release files for d2-python 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for d2-python 2.0.0
File Size Uploaded Provenance
d2_python-2.0.0.tar.gz 16.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for d2-python 2.0.0
File Interpreter ABI Platform
d2_python-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 36.0 kB

Release files / d2_python-2.0.0.tar.gz

Download URL d2_python-2.0.0.tar.gz
Size 16.2 kB
Tags Source
SHA-256 checksum
How to use checksums
7e9ca9faa23e8db93accc202a2c3815e55d272a4204b0ad6ab259520cd4c4fd2
BLAKE2b-256 checksum
How to use checksums
585e63f7c9f8be8aa41716ee2a2e8dd7bc793c4beba4e396f20eb2886b13024e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / d2_python-2.0.0-py3-none-any.whl

Download URL d2_python-2.0.0-py3-none-any.whl
Size 19.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
03ba03bafb66b7152e3ed6398b7a63a7d5c5ac150de433f3698db8a91af1274d
BLAKE2b-256 checksum
How to use checksums
1cad83cd04918b96458dd8bfb201c7260c7d9ec733dce7eec75d5f423709464f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for d2-python 2.0.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page