User Guide


graphviz provides a simple pure-Python interface for the Graphviz graph-drawing software. It runs under Python 2.7 and 3.4+. 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 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  
< 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- and end-node, while edges() takes iterable of name-pairs. Keyword arguments are turned into (node and edge) attributes (see Graphviz docs).

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)  

Passing view=True will automatically open the resulting (PDF, PNG, SVG, etc.) file with your system’s default viewer application for the file type.


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()  

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"?>

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.


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 within nbviewer).

This also allows direct displaying within the Jupyter Qt Console (e.g. the one inside Spyder IDE):



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


To directly add attitbute 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):

>>> 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!"]
    1 -- 2
    2 -- 3
    3 -- 4
    4 -- 5

Quoting and HTML-like labels

The graph-building methods of Graph and Digraph objects automatically take care of quoting/escaping strings where required (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


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.


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)  

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')  

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'))  

>>> g.view(tempfile.mktemp('.gv'))  

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.