<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Diego A. Carrasco Gubernatis | Personal Website (Posts about development)</title><link>https://diegocarrasco.com/</link><description></description><atom:link href="https://diegocarrasco.com/categories/development.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:hi@diegocarrasco.com"&gt;Diego Carrasco G.&lt;/a&gt; </copyright><lastBuildDate>Sat, 09 May 2026 08:04:39 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>D2 (d2lang) Python Wrapper: Use D2Lang in Python Projects</title><link>https://diegocarrasco.com/d2-python-wrapper-introduction/</link><dc:creator>Diego Carrasco G.</dc:creator><description>&lt;figure&gt;&lt;img src="https://diegocarrasco.com/images/social-images/d2-python-wrapper-introduction.jpg"&gt;&lt;/figure&gt; &lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: I created a Python wrapper for D2 diagram language that bundles D2's binaries, allowing you to render D2 diagrams in your Python projects without manual binary installation or subprocess management. You can install it with &lt;code&gt;pip&lt;/code&gt;.&lt;/p&gt;
&lt;div class="toc"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://diegocarrasco.com/d2-python-wrapper-introduction/#context"&gt;Context&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://diegocarrasco.com/d2-python-wrapper-introduction/#what-i-built"&gt;What I Built&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://diegocarrasco.com/d2-python-wrapper-introduction/#what-you-get"&gt;What You Get&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://diegocarrasco.com/d2-python-wrapper-introduction/#real-world-usage"&gt;Real-World Usage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://diegocarrasco.com/d2-python-wrapper-introduction/#how-to-get-started"&gt;How to Get Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://diegocarrasco.com/d2-python-wrapper-introduction/#testing-approach"&gt;Testing Approach&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://diegocarrasco.com/d2-python-wrapper-introduction/#build-automation"&gt;Build Automation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://diegocarrasco.com/d2-python-wrapper-introduction/#whats-next"&gt;What's Next&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://diegocarrasco.com/d2-python-wrapper-introduction/#when-to-use-this"&gt;When to Use This&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h2 id="context"&gt;Context&lt;/h2&gt;
&lt;p&gt;I needed a way to render D2 diagrams programmatically in Python, specifically for this Nikola static site and other Python projects. While D2 is a powerful diagram scripting language (which I now love) with a CLI tool written in Go, installing the binary manually in each environment wasn't practical (and also not possible afaik in Cloudflare Pages). After searching for "D2 lang Python" and "D2 diagram Python wrapper" without finding a solution that bundled the binaries or that could render d2 code, I created &lt;a href="https://github.com/dacog/d2-python-wrapper/"&gt;d2-python-wrapper&lt;/a&gt;. It's a thin Python layer around D2's official binary, distributed as a Python package that includes the necessary executables for each platform.&lt;/p&gt;
&lt;p&gt;I found &lt;a href="https://github.com/terrastruct/d2"&gt;D2&lt;/a&gt; while exploring options for adding diagrams to my &lt;a href="https://diegocarrasco.com/digital-marketers-playbook-launch/"&gt;digital marketing book&lt;/a&gt; and I wanted to use it to add a diagram in this &lt;a href="https://diegocarrasco.com/access-multiple-lxc-containers-single-tailscale"&gt;article about Tailscale and LXC containers&lt;/a&gt;. If you're familiar with Mermaid or PlantUML, D2 is similar but with a more intuitive syntax and (in my opinion) better-looking output.&lt;/p&gt;
&lt;p&gt;Here's where it got interesting: I couldn't install D2's CLI tools directly in &lt;a href="https://diegocarrasco.com/deploy-nikola-site-cloudflare-pages/"&gt;Cloudflare Pages&lt;/a&gt;. Rather than depend on third-party services (I tried &lt;a href="https://kroki.io/"&gt;kroki&lt;/a&gt;, I decided to create a wrapper that bundles the binaries. That's how &lt;a href="https://github.com/dacog/d2-python-wrapper/"&gt;d2-python-wrapper&lt;/a&gt; was born, and now you can install it directly from PyPI.&lt;/p&gt;
&lt;h2 id="what-i-built"&gt;What I Built&lt;/h2&gt;
&lt;p&gt;I wanted something simple - no complex subprocess management or command-line hassles. Just clean Python code like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;d2_python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;D2&lt;/span&gt;

&lt;span class="n"&gt;d2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;D2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"x -&amp;gt; y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"output.svg"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Instead of installing D2 separately, I can just use pip to install the package and have everything ready to go. I can also fix specific version of the package with specific versions of the d2 binary.&lt;/p&gt;
&lt;h2 id="what-you-get"&gt;What You Get&lt;/h2&gt;
&lt;p&gt;When you install &lt;code&gt;d2-python-wrapper&lt;/code&gt;, you get:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A simple Python API for D2 diagram rendering&lt;/li&gt;
&lt;li&gt;The D2 binaries included in the package&lt;/li&gt;
&lt;li&gt;Automatic platform detection (Linux, Windows, MacOS)&lt;/li&gt;
&lt;li&gt;Multiple output formats (SVG, PNG, PDF)&lt;/li&gt;
&lt;li&gt;Theme customization options&lt;/li&gt;
&lt;li&gt;Layout engine selection&lt;/li&gt;
&lt;li&gt;Proper cleanup of temporary files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Basically, an interface in Python for d2 bin 😁&lt;/p&gt;
&lt;h2 id="real-world-usage"&gt;Real-World Usage&lt;/h2&gt;
&lt;p&gt;I'm currently working on a Nikola plugin for D2 diagrams (not yet released). Here's how I use it in my site with a shortcode:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;pre class="code literal-block"&gt;&lt;code class="language-d2"&gt;direction: down
Internet -&amp;gt; Server
Server -&amp;gt; Database&lt;/code&gt;&lt;/pre&gt;

&lt;div style="max-width: 150px"&gt;&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2version="v0.6.8" preserveaspectratio="xMinYMin meet" viewbox="0 0 312 600"&gt;&lt;svg id="d2-svg" class="d2-1442863242" width="312" height="600" viewbox="-101 -101 312 600"&gt;&lt;rect x="-101.000000" y="-101.000000" width="312.000000" height="600.000000" rx="0.000000" fill="#FFFFFF" class=" fill-N7" stroke-width="0"&gt;&lt;/rect&gt;&lt;style type="text/css"&gt;&lt;![CDATA[
.d2-1442863242 .text-bold {
	font-family: "d2-1442863242-font-bold";
}
@font-face {
	font-family: d2-1442863242-font-bold;
	src: url("data:application/font-woff;base64,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");
}]]&gt;&lt;/style&gt;&lt;style type="text/css"&gt;&lt;![CDATA[.shape {
  shape-rendering: geometricPrecision;
  stroke-linejoin: round;
}
.connection {
  stroke-linecap: round;
  stroke-linejoin: round;
}
.blend {
  mix-blend-mode: multiply;
  opacity: 0.5;
}

		.d2-1442863242 .fill-N1{fill:#0A0F25;}
		.d2-1442863242 .fill-N2{fill:#676C7E;}
		.d2-1442863242 .fill-N3{fill:#9499AB;}
		.d2-1442863242 .fill-N4{fill:#CFD2DD;}
		.d2-1442863242 .fill-N5{fill:#DEE1EB;}
		.d2-1442863242 .fill-N6{fill:#EEF1F8;}
		.d2-1442863242 .fill-N7{fill:#FFFFFF;}
		.d2-1442863242 .fill-B1{fill:#0D32B2;}
		.d2-1442863242 .fill-B2{fill:#0D32B2;}
		.d2-1442863242 .fill-B3{fill:#E3E9FD;}
		.d2-1442863242 .fill-B4{fill:#E3E9FD;}
		.d2-1442863242 .fill-B5{fill:#EDF0FD;}
		.d2-1442863242 .fill-B6{fill:#F7F8FE;}
		.d2-1442863242 .fill-AA2{fill:#4A6FF3;}
		.d2-1442863242 .fill-AA4{fill:#EDF0FD;}
		.d2-1442863242 .fill-AA5{fill:#F7F8FE;}
		.d2-1442863242 .fill-AB4{fill:#EDF0FD;}
		.d2-1442863242 .fill-AB5{fill:#F7F8FE;}
		.d2-1442863242 .stroke-N1{stroke:#0A0F25;}
		.d2-1442863242 .stroke-N2{stroke:#676C7E;}
		.d2-1442863242 .stroke-N3{stroke:#9499AB;}
		.d2-1442863242 .stroke-N4{stroke:#CFD2DD;}
		.d2-1442863242 .stroke-N5{stroke:#DEE1EB;}
		.d2-1442863242 .stroke-N6{stroke:#EEF1F8;}
		.d2-1442863242 .stroke-N7{stroke:#FFFFFF;}
		.d2-1442863242 .stroke-B1{stroke:#0D32B2;}
		.d2-1442863242 .stroke-B2{stroke:#0D32B2;}
		.d2-1442863242 .stroke-B3{stroke:#E3E9FD;}
		.d2-1442863242 .stroke-B4{stroke:#E3E9FD;}
		.d2-1442863242 .stroke-B5{stroke:#EDF0FD;}
		.d2-1442863242 .stroke-B6{stroke:#F7F8FE;}
		.d2-1442863242 .stroke-AA2{stroke:#4A6FF3;}
		.d2-1442863242 .stroke-AA4{stroke:#EDF0FD;}
		.d2-1442863242 .stroke-AA5{stroke:#F7F8FE;}
		.d2-1442863242 .stroke-AB4{stroke:#EDF0FD;}
		.d2-1442863242 .stroke-AB5{stroke:#F7F8FE;}
		.d2-1442863242 .background-color-N1{background-color:#0A0F25;}
		.d2-1442863242 .background-color-N2{background-color:#676C7E;}
		.d2-1442863242 .background-color-N3{background-color:#9499AB;}
		.d2-1442863242 .background-color-N4{background-color:#CFD2DD;}
		.d2-1442863242 .background-color-N5{background-color:#DEE1EB;}
		.d2-1442863242 .background-color-N6{background-color:#EEF1F8;}
		.d2-1442863242 .background-color-N7{background-color:#FFFFFF;}
		.d2-1442863242 .background-color-B1{background-color:#0D32B2;}
		.d2-1442863242 .background-color-B2{background-color:#0D32B2;}
		.d2-1442863242 .background-color-B3{background-color:#E3E9FD;}
		.d2-1442863242 .background-color-B4{background-color:#E3E9FD;}
		.d2-1442863242 .background-color-B5{background-color:#EDF0FD;}
		.d2-1442863242 .background-color-B6{background-color:#F7F8FE;}
		.d2-1442863242 .background-color-AA2{background-color:#4A6FF3;}
		.d2-1442863242 .background-color-AA4{background-color:#EDF0FD;}
		.d2-1442863242 .background-color-AA5{background-color:#F7F8FE;}
		.d2-1442863242 .background-color-AB4{background-color:#EDF0FD;}
		.d2-1442863242 .background-color-AB5{background-color:#F7F8FE;}
		.d2-1442863242 .color-N1{color:#0A0F25;}
		.d2-1442863242 .color-N2{color:#676C7E;}
		.d2-1442863242 .color-N3{color:#9499AB;}
		.d2-1442863242 .color-N4{color:#CFD2DD;}
		.d2-1442863242 .color-N5{color:#DEE1EB;}
		.d2-1442863242 .color-N6{color:#EEF1F8;}
		.d2-1442863242 .color-N7{color:#FFFFFF;}
		.d2-1442863242 .color-B1{color:#0D32B2;}
		.d2-1442863242 .color-B2{color:#0D32B2;}
		.d2-1442863242 .color-B3{color:#E3E9FD;}
		.d2-1442863242 .color-B4{color:#E3E9FD;}
		.d2-1442863242 .color-B5{color:#EDF0FD;}
		.d2-1442863242 .color-B6{color:#F7F8FE;}
		.d2-1442863242 .color-AA2{color:#4A6FF3;}
		.d2-1442863242 .color-AA4{color:#EDF0FD;}
		.d2-1442863242 .color-AA5{color:#F7F8FE;}
		.d2-1442863242 .color-AB4{color:#EDF0FD;}
		.d2-1442863242 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(https://diegocarrasco.com/d2-python-wrapper-introduction/#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]&gt;&lt;/style&gt;&lt;g id="Internet"&gt;&lt;g class="shape"&gt;&lt;rect x="4.000000" y="0.000000" width="103.000000" height="66.000000" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-B6" style="stroke-width:2;"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;text x="55.500000" y="38.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"&gt;Internet&lt;/text&gt;&lt;/g&gt;&lt;g id="Server"&gt;&lt;g class="shape"&gt;&lt;rect x="9.000000" y="166.000000" width="93.000000" height="66.000000" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-B6" style="stroke-width:2;"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;text x="55.500000" y="204.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"&gt;Server&lt;/text&gt;&lt;/g&gt;&lt;g id="Database"&gt;&lt;g class="shape"&gt;&lt;rect x="0.000000" y="332.000000" width="110.000000" height="66.000000" stroke="#0D32B2" fill="#F7F8FE" class=" stroke-B1 fill-B6" style="stroke-width:2;"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;text x="55.000000" y="370.500000" fill="#0A0F25" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px"&gt;Database&lt;/text&gt;&lt;/g&gt;&lt;g id="(Internet -&amp;gt; Server)[0]"&gt;&lt;marker id="mk-3488378134" markerwidth="10.000000" markerheight="12.000000" refx="7.000000" refy="6.000000" viewbox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerunits="userSpaceOnUse"&gt; &lt;polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" fill="#0D32B2" class="connection fill-B1" stroke-width="2"&gt;&lt;/polygon&gt; &lt;/marker&gt;&lt;path d="M 55.000000 68.000000 C 55.000000 106.000000 55.000000 126.000000 55.000000 162.000000" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-1442863242)"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g id="(Server -&amp;gt; Database)[0]"&gt;&lt;path d="M 55.000000 234.000000 C 55.000000 272.000000 55.000000 292.000000 55.000000 328.000000" stroke="#0D32B2" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-1442863242)"&gt;&lt;/path&gt;&lt;/g&gt;&lt;mask id="d2-1442863242" maskunits="userSpaceOnUse" x="-101" y="-101" width="312" height="600"&gt;
&lt;rect x="-101" y="-101" width="312" height="600" fill="white"&gt;&lt;/rect&gt;
&lt;rect x="26.500000" y="22.500000" width="58" height="21" fill="rgba(0,0,0,0.75)"&gt;&lt;/rect&gt;
&lt;rect x="31.500000" y="188.500000" width="48" height="21" fill="rgba(0,0,0,0.75)"&gt;&lt;/rect&gt;
&lt;rect x="22.500000" y="354.500000" width="65" height="21" fill="rgba(0,0,0,0.75)"&gt;&lt;/rect&gt;
&lt;/mask&gt;&lt;/svg&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;p&gt;If you're interested in using D2 with Nikola, keep an eye out for the plugin release or just let me know in &lt;a href="https://mas.to/@daco/"&gt;Mastodon&lt;/a&gt; or &lt;a href="https://www.linkedin.com/in/diegocarrasco/"&gt;LinkedIn&lt;/a&gt;. In the meantime, you can still use d2-python-wrapper directly in your Python projects.&lt;/p&gt;
&lt;h2 id="how-to-get-started"&gt;How to Get Started&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;First, install via pip:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;d2-python-wrapper
&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Then use it in your code:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;d2_python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;D2&lt;/span&gt;

&lt;span class="n"&gt;d2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;D2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# From string input&lt;/span&gt;
&lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"x -&amp;gt; y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"output.svg"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Or with more options&lt;/span&gt;
&lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"diagram.d2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"output.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
          &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"svg"&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="testing-approach"&gt;Testing Approach&lt;/h2&gt;
&lt;p&gt;I wanted to make sure the wrapper produces exactly the same output as the original D2 binary. Here's one of the tests that verifies this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_wrapper_vs_binary_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tmp_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;diagram&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"x -&amp;gt; y"&lt;/span&gt;
    &lt;span class="n"&gt;wrapper_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tmp_path&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;"wrapper.svg"&lt;/span&gt;
    &lt;span class="n"&gt;binary_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tmp_path&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s2"&gt;"binary.svg"&lt;/span&gt;

    &lt;span class="c1"&gt;# Test both outputs are identical&lt;/span&gt;
    &lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diagram&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapper_output&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;d2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;binary_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_file&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binary_output&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;wrapper_output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_bytes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;binary_output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_bytes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id="build-automation"&gt;Build Automation&lt;/h2&gt;
&lt;p&gt;I'm using GitHub Actions to automatically bundle D2 binaries for Linux, Windows, and MacOS. While I've primarily tested this on Linux, the setup should support all major platforms:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;Download and extract D2 releases&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="no"&gt;mkdir -p d2_python/bin/{linux,win32,darwin}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="no"&gt;# Platform-specific binary downloads and setup...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you're using Windows or MacOS and want to help test, feel free to give it a try and let me know how it goes!&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What's Next&lt;/h2&gt;
&lt;p&gt;I built this primarily for my own use with &lt;a href="https://diegocarrasco.com/nikola-blog-setup"&gt;Nikola&lt;/a&gt; and other Python projects, but I'm open to expanding it. If you have ideas for new features or find any bugs, please open an issue on &lt;a href="https://github.com/dacog/d2-python-wrapper/"&gt;GitHub&lt;/a&gt;. &lt;/p&gt;
&lt;h2 id="when-to-use-this"&gt;When to Use This&lt;/h2&gt;
&lt;p&gt;You might find d2-python-wrapper useful if you're:
- Generating static sites
- Automating documentation
- Creating reports
- Working on any Python project where you need programmatic diagram rendering&lt;/p&gt;
&lt;p&gt;I'm releasing this under the Mozilla Public License Version 2.0 (same as D2 itself), so feel free to use and modify it for your projects.&lt;/p&gt;</description><category>automation</category><category>d2</category><category>d2-python-wrapper</category><category>d2lang. projects</category><category>development</category><category>diagrams</category><category>packages</category><category>python</category><guid>https://diegocarrasco.com/d2-python-wrapper-introduction/</guid><pubDate>Fri, 22 Nov 2024 08:00:00 GMT</pubDate></item></channel></rss>