mrkeyoor.com_
Sat 08 Aug 21:01 UTC
PyPIDataupdated 08 Aug 2026

pydot

pydot is a pure-Python object model and parser for Graphviz's DOT graph language. It can build directed or undirected graphs from nodes, edges, attributes, subgraphs, and clusters; parse one or more graphs from DOT text or files; edit the result; serialize raw DOT; and ask separately installed Graphviz programs to lay out and render PNG, SVG, PDF, or other formats. It also serves as the DOT conversion bridge used by NetworkX.

Verdict

pydot is a good Python layer for DOT parsing, generation, and NetworkX interchange, especially when a Graphviz subprocess is acceptable. Do not mistake the easy pip install for a self-contained renderer or use it where graph algorithms, interactive UI, strict attribute validation, or predictable low latency are the real requirement.

API stability4/5The central Dot, Graph, Node, Edge, Cluster, add_node, add_edge, graph_from_dot_data, graph_from_dot_file, to_string, create_*, and write_* patterns have existed for years and closely mirror the stable DOT data model. NetworkX also depends on this conversion surface. The dynamic attribute getter and setter design is flexible but weakly checked, parsed output can reflect Graphviz and pyparsing details, and major 4.x cleanup can expose code that relied on accidental quoting or permissive legacy parser behavior.
Docs3/5The README now gives useful end-to-end examples for parsing files and strings, creating nodes and edges, editing attributes, rendering images, returning bytes, raw versus Graphviz DOT, NetworkX conversion, dependency installation, and sensitive DEBUG logging. It is still the main user document, and it explicitly sends readers to help(pydot) and object docstrings for more. There is no polished searchable API site, attribute reference, error guide, layout-engine guide, or deep explanation of quoting and parser edge cases.
Maintenance4/5Version 4.0.1 was published on June 17, 2025, and the repository was pushed on August 1, 2026. The project is neither archived nor disabled, runs continuous integration and coverage, has a named current maintainer, and has modernized packaging, typing checks, tests, and license metadata. Releases are not frequent, but DOT and the public object model do not require weekly churn. Maintenance risk comes mostly from a small maintainer group and compatibility across pyparsing, Graphviz versions, platforms, and historical loose input.
Ecosystem4/5The recorded usage is about 6.0 million downloads per week, the repository has 998 GitHub stars, and pydot is the established DOT bridge documented by NetworkX. It benefits from Graphviz's many layout engines and output formats while remaining pure Python with one installed Python dependency. The surrounding ecosystem is mature, but responsibilities are split: Graphviz supplies rendering, NetworkX supplies algorithms, system packages supply executables and fonts, and pydot itself supplies the Python DOT model and parser.

Use it if

  • You need to generate Graphviz diagrams from Python data without manually quoting and assembling DOT text
  • You need to read, inspect, or rewrite existing DOT files using Python objects
  • You want static Graphviz layouts such as dot, neato, fdp, sfdp, circo, or twopi from a Python workflow
  • You already use NetworkX for graph algorithms and need DOT import, export, or Graphviz rendering
Skip it if

Setup reality

pip install pydot installs pyparsing and no compiled Python extension. Rendering is a separate installation: install Graphviz with the operating system package manager and confirm dot -V works in the same environment, container, service account, and PATH that runs Python. A notebook seeing dot does not prove a systemd service or minimal container will. Raw serialization with to_string or write_raw works without Graphviz; create_png, create_svg, write_png, write_pdf, create_dot, and write_dot invoke a Graphviz program and can fail because the executable is missing, the selected layout program is unavailable, DOT is invalid, fonts are absent, or an output directory is unwritable. Containers need both the graphviz package and any fonts required for consistent labels. pydot 4.0.1 metadata allows Python 3.8+, but the README only promises support for Python 3.9 and newer, so Python 3.8 users are outside the maintainer's guaranteed support even if installation succeeds. DOT parsing returns a list because one input can contain multiple graphs; indexing [0] without checking silently assumes exactly one. Names and attributes are DOT strings, not validated Python enums. Quoting, HTML-like labels, ports, record shapes, strict graphs, and reserved words follow DOT rules, and Graphviz may normalize the serialized representation. The layout engines have different strengths and options: dot is hierarchical, while neato and sfdp are force-directed. A graph that looks good with one can be unreadable with another. Rendering runs an external process and may create temporary files, so add timeouts or job isolation around user-triggered or very large diagrams rather than blocking a web request indefinitely. DEBUG logging can include complete graph contents and sensitive labels, which the README warns about explicitly. Use pydot for graph interchange and rendering, NetworkX for algorithms, and keep Graphviz versions pinned when reproducible layout matters.

Patterns

Build a directed graphcreate-directed-graph

import pydot

graph = pydot.Dot('pipeline', graph_type='digraph', rankdir='LR')
graph.add_node(pydot.Node('fetch', label='Fetch data', shape='box'))
graph.add_node(pydot.Node('store', label='Store result', shape='cylinder'))
graph.add_edge(pydot.Edge('fetch', 'store', label='records'))

Attributes are DOT strings passed through to Graphviz; pydot does not validate most names or allowed values.

Build an undirected graphcreate-undirected-graph

graph = pydot.Dot('network', graph_type='graph')
graph.add_edge(pydot.Edge('alice', 'bob'))
graph.add_edge(pydot.Edge('bob', 'carol'))

Use graph for undirected edges and digraph for directed edges; changing only visual arrow attributes does not change graph semantics.

Apply default node and edge stylesset-default-attributes

graph.set_node_defaults(
    shape='box',
    style='rounded,filled',
    fillcolor='#f3f4f6',
    fontname='Inter',
)
graph.set_edge_defaults(color='#6b7280', arrowsize='0.8')

Graphviz must be able to find the named font on the rendering host or it will substitute another font and change layout.

Group nodes in a Graphviz clustergroup-cluster

backend = pydot.Cluster('backend', label='Backend', color='lightgrey')
backend.add_node(pydot.Node('api'))
backend.add_node(pydot.Node('database'))
graph.add_subgraph(backend)
graph.add_edge(pydot.Edge('api', 'database'))

Cluster names conventionally start with cluster. Clusters influence Graphviz layout but are not a general nested-graph algorithm structure.

Parse graphs from a DOT stringparse-dot-string

dot = 'digraph G { api -> worker; worker -> database; }'
graphs = pydot.graph_from_dot_data(dot)
if len(graphs) != 1:
    raise ValueError(f'expected one graph, got {len(graphs)}')
graph = graphs[0]

The parser returns a list because DOT input may contain multiple top-level graphs.

Load a DOT fileparse-dot-file

graphs = pydot.graph_from_dot_file('architecture.dot', encoding='utf-8')
if not graphs:
    raise ValueError('no graph found')
graph = graphs[0]

Parsing uses pyparsing and does not require Graphviz; rendering the result still does.

Find and edit a named nodeedit-existing-node

matches = graph.get_node('database')
if not matches:
    raise KeyError('database')
node = matches[0]
node.set_shape('cylinder')
node.set_fillcolor('#dbeafe')
node.set_style('filled')

get_node returns a list because DOT can contain repeated declarations of the same node name.

Inspect edge endpoints and attributesinspect-edges

for edge in graph.get_edges():
    source = edge.get_source()
    destination = edge.get_destination()
    label = edge.get_label()
    print(source, destination, label)

Values may retain DOT quoting. Do not assume every getter returns an unquoted application identifier.

Serialize DOT without running Graphvizwrite-raw-dot

dot_text = graph.to_string()
graph.write_raw('pipeline.dot')

Raw serialization is fast and needs no Graphviz executable, but it does not compute layout positions.

Render a PNG with the dot layout enginerender-png

graph.write_png('pipeline.png', prog='dot')

This starts the external Graphviz dot program. Install Graphviz separately and ensure dot is on the service process PATH.

Render SVG into memorycreate-svg-bytes

svg_bytes = graph.create_svg(prog='dot')
with open('pipeline.svg', 'wb') as output:
    output.write(svg_bytes)

create_svg returns bytes and still invokes Graphviz; it is not the same as pydot's raw DOT serialization.

Convert between NetworkX and pydotconvert-networkx

import networkx as nx

nx_graph = nx.DiGraph()
nx_graph.add_edge('api', 'worker', weight=3)
pydot_graph = nx.drawing.nx_pydot.to_pydot(nx_graph)
round_trip = nx.drawing.nx_pydot.from_pydot(pydot_graph)

DOT attributes are commonly strings, so numeric or application-specific types may need conversion after a round trip.

Alternatives

PackageRegistryPick it when
graphvizPyPIYou only need a small official-style Python builder and renderer for DOT and do not need pydot's parser or NetworkX bridge
pygraphvizPyPIYou want tighter access to the Graphviz C library and accept a more difficult native build and system-library dependency
networkxPyPIYour primary need is graph algorithms and analysis, with pydot or pygraphviz used only for interchange and layout