User Guide¶
Installation¶
graphviz
provides a simple pure-Python interface for the Graphviz
graph-drawing software. It runs under Python 2.7 and 3.5+. To install it
with pip run the following:
$ pip install graphviz
For a system-wide install, this typically requires administrator access. For an
isolated install, you can run the same inside a virtualenv or a
py3:venv
(Python 3 only).
The only dependency is a working installation of Graphviz (download page).
After installing Graphviz, make sure that its bin/
subdirectory containing
the layout commands for rendering graph descriptions (dot
, circo
,
neato
, etc.) is on your systems’ path: On the command-line, dot -V
should print the version of your Graphiz installation.
Basic usage¶
The graphviz
module provides two classes: Graph
and
Digraph
. They create graph descriptions in the DOT language for
undirected and directed graphs respectively. They have the same
API.
Create a graph by instantiating a new Graph
or
Digraph
object:
>>> from graphviz import Digraph
>>> dot = Digraph(comment='The Round Table')
>>> dot
<graphviz.dot.Digraph object at 0x...>
Their constructors allow to set the graph’s name
, the
filename
for the DOT source and the rendered graph, a
comment
for the first source code line, etc.
Add nodes and edges to the graph object using its node()
and
edge()
or edges()
methods:
>>> dot.node('A', 'King Arthur')
>>> dot.node('B', 'Sir Bedevere the Wise')
>>> dot.node('L', 'Sir Lancelot the Brave')
>>> dot.edges(['AB', 'AL'])
>>> dot.edge('B', 'L', constraint='false')
The node()
-method takes a name
identifier as first argument
and an optional label
. The edge()
-method takes the names of
start node and end node, while edges()
takes an iterable of
name pairs. Keyword arguments are turned into (node and edge) attributes (see
Graphviz docs on available attributes).
Check the generated source code:
>>> print(dot.source)
// The Round Table
digraph {
A [label="King Arthur"]
B [label="Sir Bedevere the Wise"]
L [label="Sir Lancelot the Brave"]
A -> B
A -> L
B -> L [constraint=false]
}
Use the render()
-method to save the source code and render it with the
default layout program (dot
, see below for using other layout commands).
>>> dot.render('test-output/round-table.gv', view=True)
'test-output/round-table.gv.pdf'
Passing view=True
will automatically open the resulting (PDF, PNG, SVG,
etc.) file with your system’s default viewer application for the file type.
Formats¶
To use a different output file format than the default PDF, use the
format
argument when creating your Graph
or
Digraph
object:
>>> from graphviz import Graph
>>> g = Graph(format='png')
You can also change the format
attribute on an existing graph
object:
>>> dot.format = 'svg'
>>> dot.render()
'test-output/round-table.gv.svg'
Piped output¶
To directly access the results from the Graphviz rendering command (e.g.
dot
) as binary data string from within Python instead of writing to a file,
use the pipe()
-method of your Graph
or
Digraph
object:
>>> h = Graph('hello', format='svg')
>>> h.edge('Hello', 'World')
>>> print(h.pipe().decode('utf-8'))
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg
...
</svg>
Note that pipe()
returns the raw stdout
from the rendering
command (str
on Python 2, bytes
on Python 3): When piping into
plain-text formats like 'svg'
or 'plain'
, you usually want to decode
the return value as shown above.
Note
The output for pipe()
is buffered in memory, so do not use
this method if the data size is large.
Jupyter notebooks¶
Graph
and Digraph
objects have a
_repr_svg_()
-method so they can be rendered and displayed
directly inside a Jupyter notebook. For an example, check the
examples/notebook.ipynb
file in the
source repository/distribution (or the same rendered
within nbviewer).
This also allows direct displaying within the Jupyter Qt Console (e.g. the one inside Spyder IDE):

Styling¶
Use the graph_attr
, node_attr
, and
edge_attr
arguments to change the default appearance of your
graph, nodes, and edges.
>>> ps = Digraph(name='pet-shop', node_attr={'shape': 'plaintext'})
>>> ps.node('parrot')
>>> ps.node('dead')
>>> ps.edge('parrot', 'dead')
After creation, they can be edited on the graph object:
>>> ps.graph_attr['rankdir'] = 'LR'
>>> ps.edge_attr.update(arrowhead='vee', arrowsize='2')
>>> print(ps.source)
digraph "pet-shop" {
graph [rankdir=LR]
node [shape=plaintext]
edge [arrowhead=vee arrowsize=2]
parrot
dead
parrot -> dead
}
Attributes¶
To directly add attritbute statements (affecting all following graph, node, or
edge items within the same (sub-)graph), use the attr()
-method
with the target as first argument:
>>> ni = Graph('ni')
>>> ni.attr('node', shape='rarrow')
>>> ni.node('1', 'Ni!')
>>> ni.node('2', 'Ni!')
>>> ni.node('3', 'Ni!', shape='egg')
>>> ni.attr('node', shape='star')
>>> ni.node('4', 'Ni!')
>>> ni.node('5', 'Ni!')
By omitting its first argument, you can use it to set arbitrary attributes as
key-value pairs targeting the current (sub-)graph (e.g. for rankdir
,
label
, or setting rank='same'
within a subgraph context,
example):
>>> ni.attr(rankdir='LR')
>>> ni.edges(['12', '23', '34', '45'])
>>> print(ni.source)
graph ni {
node [shape=rarrow]
1 [label="Ni!"]
2 [label="Ni!"]
3 [label="Ni!" shape=egg]
node [shape=star]
4 [label="Ni!"]
5 [label="Ni!"]
rankdir=LR
1 -- 2
2 -- 3
3 -- 4
4 -- 5
}
Backslash escapes¶
The Graphviz layout engine supports a number of
escape sequences such as \n
, \l
, \r
(for multi-line
labels: centered, left-justified, right-justified) and \N
, \G
, \L
(expanded to the current node name, graph name, object label). To be able to
use them from this library (e.g. for labels), strings with backslashes are
passed on as is. This means that literal backslashes need to be escaped
(doubled) by the user. As the backslash is also special in Python string
literals, a second level of doubling is needed (e.g. label='\\\\'
). This
kind of doubling can be avoided by using raw string literals (r'...'
)
instead (same solution as proposed for the stdlib re
module):
>>> e = Digraph()
>>> e.node('backslash', label=r'\\')
>>> e.node('multi_line', label=r'centered\nleft\lright\r')
>>> print(e.source)
digraph {
backslash [label="\\"]
multi_line [label="centered\nleft\lright\r"]
}
To disable any special character meaning in a string (e.g. from user input to
be rendered literally), use the escape()
function (cf. the
re.escape()
function):
>>> from graphviz import escape
>>> bs = Digraph()
>>> bs.node(escape('\\'))
>>> print(bs.source)
digraph {
"\\"
}
Quoting and HTML-like labels¶
The graph-building methods of Graph
and Digraph
objects
automatically take care of quoting (and escaping quotes)
where needed (whitespace, keywords, double quotes, etc.):
>>> q = Digraph()
>>> q.edge('spam', 'eggs eggs')
>>> q.edge('node', '"here\'s a quote"')
>>> print(q.source)
digraph {
spam -> "eggs eggs"
"node" -> "\"here's a quote\""
}
If a string starts with '<'
and ends with '>'
, it is passed on as is,
without quoting/escaping: The content between the angle brackets is treated by
the engine as special HTML string that can be used for HTML-like labels:
>>> h = Graph('html_table')
>>> h.node('tab', label='''<<TABLE>
... <TR>
... <TD>left</TD>
... <TD>right</TD>
... </TR>
... </TABLE>>''')
For strings that should literally begin with '<'
and end with '>'
, use
the nohtml()
function to disable the special meaning of angled
parenthesis and apply normal quoting/escaping (before 0.8.2
, the only
workaround was to add leading or trailing space, e.g. label=' <>'
):
>>> from graphviz import nohtml
>>> d = Digraph(format='svg')
>>> d.node('diamond', label=nohtml('<>'))
>>> print(d.source)
digraph {
diamond [label="<>"]
}
Subgraphs & clusters¶
Graph
and Digraph
objects have a
subgraph()
-method for adding a subgraph to an instance.
There are two ways to use it: Either with a ready-made graph object of the same
kind as the only argument (whose content is added as a subgraph) or omitting
the graph
argument (returning a context manager for defining the subgraph
content more elegantly within a with
-block).
First usage option, with graph
as the only argument:
>>> p = Graph(name='parent')
>>> p.edge('spam', 'eggs')
>>> c = Graph(name='child', node_attr={'shape': 'box'})
>>> c.edge('foo', 'bar')
>>> p.subgraph(c)
Second usage, with a with
-block (omitting the graph
argument):
>>> p = Graph(name='parent')
>>> p.edge('spam', 'eggs')
>>> with p.subgraph(name='child', node_attr={'shape': 'box'}) as c:
... c.edge('foo', 'bar')
Both produce the same result:
>>> print(p.source)
graph parent {
spam -- eggs
subgraph child {
node [shape=box]
foo -- bar
}
}
Note
If the name
of a subgraph begins with 'cluster'
(all lowercase) the
layout engine will treat it as a special cluster subgraph
(example). Also see the Subgraphs and Clusters
section of the DOT language documentation.
Engines¶
To use a different layout command than the default dot
when rendering your
graph, use the engine
argument when creating your graph.
>>> g = Graph(engine='neato')
You can also change the engine
attribute of an existing
instance:
>>> dot.engine = 'circo'
Custom DOT statements¶
To add arbitrary statements to the created DOT source, use the
body
attribute of the Graph
or Digraph
object. It holds the verbatim list of lines to be written to the source file.
Use its append()
or extend()
method:
>>> rt = Digraph(comment='The Round Table')
>>> rt.body.append('\t"King Arthur" -> {\n\t\t"Sir Bedevere", "Sir Lancelot"\n\t}')
>>> rt.edge('Sir Bedevere', 'Sir Lancelot', constraint='false')
>>> print(rt.source)
// The Round Table
digraph {
"King Arthur" -> {
"Sir Bedevere", "Sir Lancelot"
}
"Sir Bedevere" -> "Sir Lancelot" [constraint=false]
}
Note that you might need to correctly quote/escape identifiers and strings containing whitespace or other special characters when using this method.
Using raw DOT¶
To render a ready-made DOT source code string (instead of assembling one with
the higher-level interface of Graph
or Digraph
), create a
Source
object holding your DOT string:
>>> from graphviz import Source
>>> src = Source('digraph "the holy hand grenade" { rankdir=LR; 1 -> 2 -> 3 -> lob }')
>>> src
<graphviz.files.Source object at 0x...>
Use the render()
-method to save and render it:
>>> src.render('test-output/holy-grenade.gv', view=True)
'test-output/holy-grenade.gv.pdf'
Apart from the missing editing methods, Source
objects are the same
as the higher-level graph objects (pipe()
-method,
format
, engine
, Jupyter notebook repr, etc.),
see above.
Existing files¶
To directly render an existing DOT source file (e.g. created with other tools),
you can use the graphviz.render()
function.
>>> from graphviz import render
>>> render('dot', 'png', 'test-output/holy-grenade.gv')
'test-output/holy-grenade.gv.png'
To directly display the graph of an existing DOT source file inside a
Jupyter notebook or Qt Console,
you can use the Source.from_file()
-classmethod (alternate constructor):

Note that if you call render()
or view()
on the
returned Source
object, it will still save()
as usual
(i.e. write the content read into source
back into the file).
You can use graphviz.render()
and graphiz.view()
to directly work
on files in case you need to avoid this round-trip.
Integration with viewers¶
On platforms such as Windows, viewer programs opened by rendering with
view=True
or the view()
-method might lock the (PDF, PNG,
etc.) file for as long as the viewer is open (blocking re-rendering it with a
Permission denied
error). You can use the mktemp()
function
from the stdlib tempfile
module to render to a different file for each
invocation to avoid needing to close the viewer window each time within such an
incremental workflow (and also preserve its intermediate steps):
>>> import tempfile
>>> g = Graph()
>>> g.node('spam')
>>> g.view(tempfile.mktemp('.gv'))
'C:\\Users\\User\\AppData\\Local\\Temp\\tmp3aoie8d0.gv.pdf'
>>> g.view(tempfile.mktemp('.gv'))
'C:\\Users\\User\\AppData\\Local\\Temp\\tmphh4ig7a_.gv.pdf'
Other options are viewers that support live updates or using the Jupyter notebook or Qt Console to display the current version of the rendered graph in repeated add/render/view cycles.