Skip to content

A little python library for generating graphviz process diagrams

Notifications You must be signed in to change notification settings

erikleitch/riak_graphviz

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

96 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Synopsis

riak_graphviz is a python library, built on graphviz.py, intended to simplify generating diagrams for complex processes. The primary functionality it adds to graphviz.py is ease of construction of complicated call stacks, and logical ordering of nodes through the use of subgraphs under the hood.

You'll need graphviz installed to use this code, which you can get, for example, via:

pip install graphviz

or see the official graphviz site for other download options: http://www.graphviz.org.

Extensive (but weirdly awkward to navigate) documentation about graphviz can be found at the official graphviz site: http://www.graphviz.org/Documentation.php

Back to Examples

To create a new directional graph, and output the result as a .png file:

from riak_graphviz import Node, DiGraph
digraph = DiGraph({'format':'png'})

Now we have our main digraph object. Let's add some nodes to it and render it:

node1 = Node({'label':'module1', 'color': 'red'})
digraph.append(node1)

node2 = Node({'label':'module2', 'color': 'blue'})
digraph.append(node2)

digraph.render('img/modules.png')

All top-level nodes appended to a digraph are treated as modules, and given the same rank, so when the plot is rendered, it will look like this:

alt tag

Each node object can have its own hierarchy of nodes. You can construct this hierarchy by appending to the nodes themselves, via the Node.append() function, as detailed below:

Back to Examples

Node.append() takes a single argument with a flexible syntax: a tuple of values (i.e., (val1, val2, val3)) is interpreted as a call-stack, i.e., a set of nodes representing functions that call each other in turn, so the following code:

digraph = DiGraph({'format':'png'})
node = Node({'label':'module1', 'color': 'red'})
node.append(({'label': 'module:fn1'}, {'label': 'module:fn2'}, {'label': 'module:fn3'}))
digraph.append(node)

produces this diagram:

alt tag

Back to Examples

On the other hand, a list of values (i.e., [val1, val2, val3]) is interpreted as a simple list of functions, i.e., a set of nodes representing functions that are called by the same function, one after the other. Thus the following code:

digraph = DiGraph({'format':'png'})
node = Node({'label':'module1', 'color': 'red'})
node.append([{'label': 'module:fn1'}, {'label': 'module:fn2'}, {'label': 'module:fn3'}])
digraph.append(node)

produces this diagram:

alt tag

where the rank of the nodes indicates the order in which the functions are called (assumed to be represented by the order of the function in the list). Setting the rank attribute to same for any node changes this behavior to put every list element at the same rank. Thus the following code

digraph = DiGraph({'format':'png'})
node = Node({'label':'module1', 'color': 'red'})
node.append([{'label': 'module:fn1'}, {'label': 'module:fn2'}, {'label': 'module:fn3'}])
node.setNodeAttr('module1', 'rank', 'same')
digraph.append(node)

produces this diagram instead:

alt tag

Back to Examples

Calls to append can be made sequentially on a node, or any supported syntax may be arbitrarily nested. Thus the following code creates a call-stack of functions, the last of which is itself another call-stack, consisting of a function calling three sub-functions:

    node = Node({'label':'module1', 'color': 'blue'})
    node.append(
      (
        {'label': 'module1:fn1'},
        {'label': 'module1:fn2'},
        ({'label': 'module1:fn3'}, [{'label': 'module1:fn3_1'}, {'label': 'module1:fn3_2'}, {'label': 'module1:fn3_3'}]),
      )
    )
    digraph.append(node)

producing this diagram:

alt tag

Back to Examples

Putting it all together, we can create a digraph with multiple modules by instantiating separate nodes:

node1 = Node({'label':'module1', 'color': 'red'})
node2 = Node({'label':'module2', 'color': 'blue'})

Each one can be configured as desired and appended to the digraph:

node1.append(
  (
    {'label': 'module1:fn1'},
    {'label': 'module1:fn2'},
    ({'label': 'module1:fn3'}, [{'label': 'module1:fn3_1'}, {'label': 'module1:fn3_2'}, {'label': 'module1:fn3_3'}]),
  )
)

node2.append([{'label': 'module2:fn1'}, {'label': 'module2:fn2'}, {'label': 'module2:fn3'}])
node2.setNodeAttr('module1', 'rank', 'same')

digraph.append(node1)
digraph.append(node2)

producing this diagram:

alt tag

Back to Examples

Edges can be added between arbitrary nodes by using the DiGraph.edge() function:

digraph.edge('module2:fn3', 'module1:fn3_3', {'color':'green'})

would produce the following output:

alt tag

Back to Examples

The dictionary passed with a node or edge definition can contain any of the supported graphviz attributes. Thus the following code:

node1 = Node({'label':'module1', 'color': 'red'})
node2 = Node({'label':'module2', 'color': 'blue'})

node1.append(
  (
    {'label': 'module1:fn1', 'color':'darkgreen'},
    {'label': 'module1:fn2'},
    ({'label': 'module1:fn3'}, [{'label': 'module1:fn3_1'}, {'label': 'module1:fn3_2'}, {'label': 'module1:fn3_3'}]),
  )
)

node2.append(
  [
    {'label': 'module2:fn1'},
    {'label': 'module2:fn2',  'style':'filled', 'fillcolor':'mistyrose'},
    {'label': 'module2:fn3'}
  ]
)
node2.setNodeAttr('module1', 'rank', 'same')

digraph.edge('module2:fn3', 'module1:fn3_3', {'color':'green', 'dir':'both', 'label':'gen_server:send'})

digraph.append(node1)
digraph.append(node2)

produces the following diagram:

alt tag

NB: Graphviz attributes are documented at http://soc.if.usp.br/manual/graphviz/html/info/attrs.html.

Back to Examples

Constructing complicated calling sequences with a single call to Node.append() can be challenging to get right. riak_graphviz provides some convenience methods to make this easier.

For example, the following code:

    # Construct a single sequence all at one go:

    node = Node({'label':'module1', 'color': 'blue'})
    node.append(
      (
        {'label': 'module1:fn1'},
        {'label': 'module1:fn2'},
        ({'label': 'module1:fn3'}, [{'label': 'module1:fn3_1'}, {'label': 'module1:fn3_2'}, {'label': 'module1:fn3_3'}]),
      )
    )
    digraph.append(node)

and this code:

    # Construct a single node
    
    node1 = Node({'label':'module1', 'color': 'blue'})
    node1.append({'label': 'module1:fn1'})

    # Construct another node
    
    node2 = Node({'label': 'module1:fn3'})
    node2.append([{'label':'module1:fn3_1'}, {'label':'module1:fn3_3'}])

    # Now append node2 to node1
    
    node1.appendTo('module1:fn1', node2)

    # Now insert on node1
    
    node1.insertBetween('module1:fn1', 'module1:fn3', {'label':'module:fn2'})
    node1.insertBetween('module1:fn3_1', 'module1:fn3_3', {'label':'module:fn3_2'})

    digraph.append(node1)

both produce the same diagram below:

alt tag

About

A little python library for generating graphviz process diagrams

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages