Skip to main content

Python - Generate flows using pydot

Use Python and the pydot library to easily generate diagrams-as-code like ETL flowcharts, class diagrams, and decision trees. Includes Docker setup and code examples.

· 3 min read
View raw
Christophe
Markdown, WSL and Docker lover ~ Insatiable curious.
Cet article existe aussi en français — lire la version française.

This article is over a year old but was reviewed on July 30, 2026 — the content is still accurate.

Python - Generate flows using Pydot

⚡
TL;DR

This article introduces Pydot, a Python diagram-as-code library that renders diagrams (ETL flows, class diagrams, data flows, decision trees) from Python code into images. Using a small Docker image, a .py script describing the diagram is run with docker run ... python etl.py to produce a PNG, with further examples sourced from the Graphviz documentation.

Pydot is a diagram-as-code generator, i.e. you write lines of code and, thanks to a magic process, you can render the code as an image.

This blog has two other articles in the same family: Docker - Diagrams as code, using the diagrams library and its cloud-provider icons, and Documentation as Code - Transform Your Infrastructure into Beautiful Diagrams with Python and Mermaid, where the diagram is generated from your compose.yaml instead of hand-written.

For guys just like me who are terrible at visuals, this is gold.

Let's say you have to describe an IT process such as ETL (data is loaded, one or other transformation rule is applied and the result is loaded into a database, for example); you can easily imagine drawing some rectangles using a tool like https://app.diagrams.net/ (formerly known as draw.io), or use a smarter way to do it.

Let's see how in this article.

Pydot can generate an image like the one below:

ETL

First, we'll need a Python Docker container. Please create a file called Dockerfile with this content:

FROM python:3.13-slim
# Prevents Python from writing pyc files.
ENV PYTHONDONTWRITEBYTECODE=1
# hadolint ignore=DL3008
RUN set -e -x \
&& apt-get update -yqq \
&& apt-get install -y --no-install-recommends graphviz \
&& apt-get clean \
&& rm -rf /tmp/* /var/lib/apt/lists/*
# hadolint ignore=DL3013
RUN pip install --no-cache-dir pydot

This Dockerfile sets up a lightweight Python 3.13 environment optimized for container use. It disables Python's bytecode generation to reduce disk space, installs the graphviz system package needed for graph visualization, and adds the pydot Python library to interact with those graphs.

And create the image by running docker build --tag pydot ..

We don't need more except ... our flow.

To build an ETL image like the one above, please create a new file called f.i. etl.py with this content:

graph = pydot.Dot(graph_type='digraph', rankdir='TB')
node_csv = pydot.Node('CSV', shape='box', style='filled', fillcolor='lightblue')
node_json = pydot.Node('JSON', shape='box', style='filled', fillcolor='lightblue')
node_extract = pydot.Node('Extract', shape='box')
node_clean = pydot.Node('Clean', shape='box')
node_transform = pydot.Node('Transform', shape='box')
node_load = pydot.Node('Load', shape='box', style='filled', fillcolor='lightgreen')
graph.add_edge(pydot.Edge(node_csv, node_extract))
graph.add_edge(pydot.Edge(node_json, node_extract))
graph.add_edge(pydot.Edge(node_extract, node_clean))
graph.add_edge(pydot.Edge(node_clean, node_transform))
graph.add_edge(pydot.Edge(node_transform, node_load))
node_enrich = pydot.Node('Get more data', shape='box')
graph.add_edge(pydot.Edge(node_transform, node_enrich))
graph.add_edge(pydot.Edge(node_enrich, node_load))
graph.write_png('etl.png')

This code builds a visual diagram of an ETL (Extract-Transform-Load) data pipeline using the pydot library. It creates nodes representing different stages of data processing—starting with CSV and JSON sources, flowing through extraction, cleaning, and transformation steps, with an optional enrichment branch, and ending at a load stage. Finally, it saves this diagram as a PNG image file.

And, now, the easy part, render the Python etl.py file as an image by running docker run --rm -it -v "${PWD}":/diagram -w /diagram pydot python etl.py.

And bingo, you've now an image called etl.png in your directory.

Some other examples​

Search on the Internet for some examples and you'll find f.i. this site: https://graphviz.readthedocs.io/en/stable/examples.html.

You'll find there Python examples and, too, the rendered image.

OOP classes​

graph = pydot.Dot(graph_type='digraph')
# Add nodes
node_person = pydot.Node('Person', shape='box')
node_student = pydot.Node('Student', shape='box')
node_teacher = pydot.Node('Teacher', shape='box')
# Add nodes to the graph
graph.add_node(node_person)
graph.add_node(node_student)
graph.add_node(node_teacher)
# Add edges (inheritance)
graph.add_edge(pydot.Edge(node_person, node_student))
graph.add_edge(pydot.Edge(node_person, node_teacher))
# Render the graph
graph.write_png('class_diagram.png')

This code creates a visual diagram showing a class hierarchy using pydot, which is a Python wrapper around Graphviz. It defines three nodes (Person, Student, and Teacher), adds them to a directed graph, draws arrows showing that Student and Teacher both inherit from Person, and finally saves the diagram as a PNG image file.

Class diagram

Data flow​

graph = pydot.Dot(graph_type='digraph')
# Data sources
source1 = pydot.Node('Source 1', shape='box')
source2 = pydot.Node('Source 2', shape='box')
graph.add_node(source1)
graph.add_node(source2)
# Process nodes
process1 = pydot.Node('Process 1', shape='oval')
process2 = pydot.Node('Process 2', shape='oval')
graph.add_node(process1)
graph.add_node(process2)
# Data store
data_store = pydot.Node('Data Store', shape='cylinder')
graph.add_node(data_store)
# Edges representing data flow
graph.add_edge(pydot.Edge(source1, process1))
graph.add_edge(pydot.Edge(source2, process1))
graph.add_edge(pydot.Edge(process1, process2))
graph.add_edge(pydot.Edge(process1, data_store))
graph.add_edge(pydot.Edge(data_store, process2))
# Render the graph
graph.write_png('data_flow_diagram.png')

This code creates a data flow diagram using pydot, which is a Python library for working with Graphviz. It defines five nodes representing data sources, processes, and a data store, connects them with directional edges to show how data moves between them, and then saves the resulting diagram as a PNG image file.

Data flow

Decision flow​

graph = pydot.Dot(graph_type='digraph')
# Add nodes
node_start = pydot.Node('Start', shape='oval')
node_process1 = pydot.Node('Process 1', shape='box')
node_decision = pydot.Node('Decision', shape='diamond')
node_process2 = pydot.Node('Process 2', shape='box')
node_end = pydot.Node('End', shape='oval')
# Add edges
graph.add_edge(pydot.Edge(node_start, node_process1))
graph.add_edge(pydot.Edge(node_process1, node_decision))
graph.add_edge(pydot.Edge(node_decision, node_process2, label='Yes'))
graph.add_edge(pydot.Edge(node_decision, node_end, label='No'))
graph.add_edge(pydot.Edge(node_process2, node_end))
# Write the graph to a file
graph.write_png('flowchart.png')

This code creates a flowchart diagram using pydot, a Python library for working with Graphviz. It defines five nodes (Start, Process 1, Decision, Process 2, and End) with different shapes, connects them with edges to form a flow, and renders the final diagram as a PNG image file.

Decision flow

Decision tree​

graph = pydot.Dot(graph_type='digraph')
# Add nodes
node_root = pydot.Node('Root', shape='box')
node_left = pydot.Node('Left Child', shape='box')
node_right = pydot.Node('Right Child', shape='box')
node_left_left = pydot.Node('Left-Left Grandchild', shape='ellipse')
node_left_right = pydot.Node('Left-Right Grandchild', shape='ellipse')
node_right_left = pydot.Node('Right-Left Grandchild', shape='ellipse')
node_right_right = pydot.Node('Right-Right Grandchild', shape='ellipse')
# Add nodes to the graph
graph.add_node(node_root)
graph.add_node(node_left)
graph.add_node(node_right)
graph.add_node(node_left_left)
graph.add_node(node_left_right)
graph.add_node(node_right_left)
graph.add_node(node_right_right)
# Add edges
graph.add_edge(pydot.Edge(node_root, node_left, label='Feature 1 < 5'))
graph.add_edge(pydot.Edge(node_root, node_right, label='Feature 1 >= 5'))
graph.add_edge(pydot.Edge(node_left, node_left_left, label='Feature 2 < 3'))
graph.add_edge(pydot.Edge(node_left, node_left_right, label='Feature 2 >= 3'))
graph.add_edge(pydot.Edge(node_right, node_right_left, label='Feature 3 < 10'))
graph.add_edge(pydot.Edge(node_right, node_right_right, label='Feature 3 >= 10'))
# Render the graph
graph.write_png('decision_tree.png')

This code builds a visual representation of a decision tree using pydot, a Python interface to Graphviz. It creates a tree structure with a root node that splits into left and right children based on feature thresholds, and then those children split further into grandchildren. Finally, it renders the entire tree as a PNG image file.

Decision tree


Was this article helpful?
Did you try the steps in this article?