summaryrefslogtreecommitdiff
path: root/bin/addons/base/ir/workflow/pydot
diff options
context:
space:
mode:
Diffstat (limited to 'bin/addons/base/ir/workflow/pydot')
-rwxr-xr-xbin/addons/base/ir/workflow/pydot/LICENSE16
-rwxr-xr-xbin/addons/base/ir/workflow/pydot/PKG-INFO33
-rwxr-xr-xbin/addons/base/ir/workflow/pydot/README29
-rwxr-xr-xbin/addons/base/ir/workflow/pydot/__init__.py1
-rwxr-xr-xbin/addons/base/ir/workflow/pydot/dot_parser.py352
-rwxr-xr-xbin/addons/base/ir/workflow/pydot/pydot.py1075
-rwxr-xr-xbin/addons/base/ir/workflow/pydot/setup.py23
7 files changed, 1529 insertions, 0 deletions
diff --git a/bin/addons/base/ir/workflow/pydot/LICENSE b/bin/addons/base/ir/workflow/pydot/LICENSE
new file mode 100755
index 00000000000..3291fa10051
--- /dev/null
+++ b/bin/addons/base/ir/workflow/pydot/LICENSE
@@ -0,0 +1,16 @@
+Copyright (c) 2004 Ero Carrera
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/bin/addons/base/ir/workflow/pydot/PKG-INFO b/bin/addons/base/ir/workflow/pydot/PKG-INFO
new file mode 100755
index 00000000000..098235a0e70
--- /dev/null
+++ b/bin/addons/base/ir/workflow/pydot/PKG-INFO
@@ -0,0 +1,33 @@
+Metadata-Version: 1.0
+Name: pydot
+Version: 0.9.9
+Summary: Python interface to Graphiz's Dot
+Home-page: http://dkbza.org/pydot.html
+Author: Ero Carrera
+Author-email: ero@dkbza.org
+License: MIT
+Description: Graphviz's dot language Python interface.
+
+ This module provides with a full interface to create handle modify
+ and process graphs in Graphviz's dot language.
+
+ References:
+
+ pydot Homepage: http://www.dkbza.org/pydot.html
+ Graphviz: http://www.research.att.com/sw/tools/graphviz/
+ DOT Language: http://www.research.att.com/~erg/graphviz/info/lang.html
+
+ Programmed and tested with Graphviz 1.12 and Python 2.3.3 on GNU/Linux
+ by Ero Carrera (c) 2004 [ero@dkbza.org]
+
+ Distributed under MIT license [http://opensource.org/licenses/mit-license.html].
+
+Platform: any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Natural Language :: English
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Scientific/Engineering :: Visualization
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/bin/addons/base/ir/workflow/pydot/README b/bin/addons/base/ir/workflow/pydot/README
new file mode 100755
index 00000000000..d909fb9a225
--- /dev/null
+++ b/bin/addons/base/ir/workflow/pydot/README
@@ -0,0 +1,29 @@
+pydot - Python interface to Graphviz's Dot language
+Ero Carrera (c) 2004
+ero@dkbza.org
+
+This code is distributed under the MIT license.
+
+Requirements:
+-------------
+
+pyparsing: pydot requires the pyparsing module in order to be
+ able to load DOT files.
+
+GraphViz: is needed in order to render the graphs into any of
+ the plethora of output formats supported.
+
+Installation:
+-------------
+
+Should suffice with doing:
+
+ python setup.py install
+
+Needless to say, no installation is needed just to use the module. A mere:
+
+ import pydot
+
+should do it, provided that the directory containing the modules is on Python
+module search path.
+
diff --git a/bin/addons/base/ir/workflow/pydot/__init__.py b/bin/addons/base/ir/workflow/pydot/__init__.py
new file mode 100755
index 00000000000..6232f794cdd
--- /dev/null
+++ b/bin/addons/base/ir/workflow/pydot/__init__.py
@@ -0,0 +1 @@
+from pydot import *
diff --git a/bin/addons/base/ir/workflow/pydot/dot_parser.py b/bin/addons/base/ir/workflow/pydot/dot_parser.py
new file mode 100755
index 00000000000..30007ed47de
--- /dev/null
+++ b/bin/addons/base/ir/workflow/pydot/dot_parser.py
@@ -0,0 +1,352 @@
+#!/bin/env python
+
+"""
+The dotparser parses graphviz files in dot and dot files and transforms them
+into a class representation defined by pydot.
+
+The module needs pyparsing (tested with version 1.2) and pydot (tested with 0.9.9)
+
+Author: Michael Krause <michael@krause-software.de>
+"""
+
+import sys
+import glob
+import pydot
+
+from pyparsing import __version__ as pyparsing_version
+from pyparsing import Literal, CaselessLiteral, Word, \
+ Upcase, OneOrMore, ZeroOrMore, Forward, NotAny, \
+ delimitedList, oneOf, Group, Optional, Combine, \
+ alphas, nums, restOfLine, cStyleComment, nums, \
+ alphanums, printables, empty, quotedString, \
+ ParseException, ParseResults, CharsNotIn, _noncomma
+
+
+class P_AttrList:
+ def __init__(self, toks):
+ self.attrs = {}
+ i = 0
+ while i < len(toks):
+ attrname = toks[i]
+ attrvalue = toks[i+1]
+ self.attrs[attrname] = attrvalue
+ i += 2
+
+ def __repr__(self):
+ return "%s(%r)" % (self.__class__.__name__, self.attrs)
+
+
+class DefaultStatement(P_AttrList):
+ def __init__(self, default_type, attrs):
+ self.default_type = default_type
+ self.attrs = attrs
+
+ def __repr__(self):
+ return "%s(%s, %r)" % \
+ (self.__class__.__name__, self.default_type, self.attrs)
+
+
+def push_top_graph_stmt(str, loc, toks):
+ attrs = {}
+ g = None
+
+ for element in toks:
+ if isinstance(element, ParseResults) or \
+ isinstance(element, tuple) or \
+ isinstance(element, list):
+
+ element = element[0]
+
+ if element == 'strict':
+ attrs['strict'] = True
+ elif element in ['graph', 'digraph']:
+ attrs['graph_type'] = element
+ elif type(element) == type(''):
+ attrs['graph_name'] = element
+ elif isinstance(element, pydot.Graph):
+ g = pydot.Graph(**attrs)
+ g.__dict__.update(element.__dict__)
+ for e in g.get_edge_list():
+ e.parent_graph = g
+ for e in g.get_node_list():
+ e.parent_graph = g
+ for e in g.get_subgraph_list():
+ e.set_graph_parent(g)
+
+ elif isinstance(element, P_AttrList):
+ attrs.update(element.attrs)
+ else:
+ raise ValueError, "Unknown element statement: %r " % element
+
+ if g is not None:
+ g.__dict__.update(attrs)
+ return g
+
+
+def add_defaults(element, defaults):
+ d = element.__dict__
+ for key, value in defaults.items():
+ if not d.get(key):
+ d[key] = value
+
+
+def add_elements(g, toks, defaults_graph=None, defaults_node=None, defaults_edge=None):
+
+ if defaults_graph is None:
+ defaults_graph = {}
+ if defaults_node is None:
+ defaults_node = {}
+ if defaults_edge is None:
+ defaults_edge = {}
+
+ for element in toks:
+ if isinstance(element, pydot.Graph):
+ add_defaults(element, defaults_graph)
+ g.add_subgraph(element)
+ elif isinstance(element, pydot.Node):
+ add_defaults(element, defaults_node)
+ g.add_node(element)
+ elif isinstance(element, pydot.Edge):
+ add_defaults(element, defaults_edge)
+ g.add_edge(element)
+ elif isinstance(element, ParseResults):
+ for e in element:
+ add_elements(g, [e], defaults_graph, defaults_node, defaults_edge)
+ elif isinstance(element, DefaultStatement):
+ if element.default_type == 'graph':
+ default_graph_attrs = pydot.Node('graph')
+ default_graph_attrs.__dict__.update(element.attrs)
+ g.add_node(default_graph_attrs)
+# defaults_graph.update(element.attrs)
+# g.__dict__.update(element.attrs)
+ elif element.default_type == 'node':
+ default_node_attrs = pydot.Node('node')
+ default_node_attrs.__dict__.update(element.attrs)
+ g.add_node(default_node_attrs)
+ #defaults_node.update(element.attrs)
+ elif element.default_type == 'edge':
+ default_edge_attrs = pydot.Node('edge')
+ default_edge_attrs.__dict__.update(element.attrs)
+ g.add_node(default_edge_attrs)
+ #defaults_edge.update(element.attrs)
+ else:
+ raise ValueError, "Unknown DefaultStatement: %s " % element.default_type
+ elif isinstance(element, P_AttrList):
+ g.__dict__.update(element.attrs)
+ else:
+ raise ValueError, "Unknown element statement: %r " % element
+
+
+def push_graph_stmt(str, loc, toks):
+ g = pydot.Subgraph()
+ add_elements(g, toks)
+ return g
+
+
+def push_subgraph_stmt(str, loc, toks):
+ for e in toks:
+ if len(e)==3:
+ g = e[2]
+ g.set_name(e[1])
+
+ return g
+
+
+def push_default_stmt(str, loc, toks):
+ # The pydot class instances should be marked as
+ # default statements to be inherited by actual
+ # graphs, nodes and edges.
+ # print "push_default_stmt", toks
+ default_type = toks[0][0]
+ if len(toks) > 1:
+ attrs = toks[1].attrs
+ else:
+ attrs = {}
+
+ if default_type in ['graph', 'node', 'edge']:
+ return DefaultStatement(default_type, attrs)
+ else:
+ raise ValueError, "Unknown default statement: %r " % toks
+
+
+def push_attr_list(str, loc, toks):
+ p = P_AttrList(toks)
+ return p
+
+
+def get_port(node):
+
+ if len(node)>1:
+ if isinstance(node[1], ParseResults):
+ if len(node[1][0])==2:
+ if node[1][0][0]==':':
+ return node[1][0][1]
+
+ return None
+
+
+def push_edge_stmt(str, loc, toks):
+
+ tok_attrs = [a for a in toks if isinstance(a, P_AttrList)]
+ attrs = {}
+ for a in tok_attrs:
+ attrs.update(a.attrs)
+
+ n_prev = toks[0]
+ e = []
+ for n_next in tuple(toks)[2::2]:
+ port = get_port(n_prev)
+ if port is not None:
+ n_prev_port = ':'+port
+ else:
+ n_prev_port = ''
+
+ port = get_port(n_next)
+ if port is not None:
+ n_next_port = ':'+port
+ else:
+ n_next_port = ''
+
+ e.append(pydot.Edge(n_prev[0]+n_prev_port, n_next[0]+n_next_port, **attrs))
+ n_prev = n_next
+ return e
+
+
+def push_node_stmt(str, loc, toks):
+
+ if len(toks) == 2:
+ attrs = toks[1].attrs
+ else:
+ attrs = {}
+
+ node_name = toks[0]
+ if isinstance(node_name, list) or isinstance(node_name, tuple):
+ if len(node_name)>0:
+ node_name = node_name[0]
+
+ n = pydot.Node(node_name, **attrs)
+ return n
+
+
+def strip_quotes( s, l, t ):
+ return [ t[0].strip('"') ]
+
+
+graphparser = None
+def GRAPH_DEF():
+ global graphparser
+
+ if not graphparser:
+ # punctuation
+ colon = Literal(":")
+ lbrace = Literal("{")
+ rbrace = Literal("}")
+ lbrack = Literal("[")
+ rbrack = Literal("]")
+ lparen = Literal("(")
+ rparen = Literal(")")
+ equals = Literal("=")
+ comma = Literal(",")
+ dot = Literal(".")
+ slash = Literal("/")
+ bslash = Literal("\\")
+ star = Literal("*")
+ semi = Literal(";")
+ at = Literal("@")
+ minus = Literal("-")
+
+ # keywords
+ strict_ = Literal("strict")
+ graph_ = Literal("graph")
+ digraph_ = Literal("digraph")
+ subgraph_ = Literal("subgraph")
+ node_ = Literal("node")
+ edge_ = Literal("edge")
+
+ identifier = Word(alphanums + "_" ).setName("identifier")
+
+ double_quote = Literal('"')
+ double_quoted_string = \
+ Combine( double_quote + ZeroOrMore(CharsNotIn('"')) + double_quote )
+
+ alphastring_ = OneOrMore(CharsNotIn(_noncomma))
+
+ ID = (identifier | double_quoted_string.setParseAction(strip_quotes) |\
+ alphastring_).setName("ID")
+
+ html_text = Combine(Literal("<<") + OneOrMore(CharsNotIn(",]")))
+
+ float_number = Combine(Optional(minus) + \
+ OneOrMore(Word(nums + "."))).setName("float_number")
+
+ righthand_id = (float_number | ID | html_text).setName("righthand_id")
+
+ port_angle = (at + ID).setName("port_angle")
+
+ port_location = (Group(colon + ID) | \
+ Group(colon + lparen + ID + comma + ID + rparen)).setName("port_location")
+
+ port = (Group(port_location + Optional(port_angle)) | \
+ Group(port_angle + Optional(port_location))).setName("port")
+
+ node_id = (ID + Optional(port))
+ a_list = OneOrMore(ID + Optional(equals.suppress() + righthand_id) + \
+ Optional(comma.suppress())).setName("a_list")
+
+ attr_list = OneOrMore(lbrack.suppress() + Optional(a_list) + \
+ rbrack.suppress()).setName("attr_list")
+
+ attr_stmt = (Group(graph_ | node_ | edge_) + attr_list).setName("attr_stmt")
+
+ edgeop = (Literal("--") | Literal("->")).setName("edgeop")
+
+ stmt_list = Forward()
+ graph_stmt = Group(lbrace.suppress() + Optional(stmt_list) + \
+ rbrace.suppress()).setName("graph_stmt")
+
+ subgraph = (Group(Optional(subgraph_ + Optional(ID)) + graph_stmt) | \
+ Group(subgraph_ + ID)).setName("subgraph")
+
+ edgeRHS = OneOrMore(edgeop + Group(node_id | subgraph))
+
+ edge_stmt = Group(node_id | subgraph) + edgeRHS + Optional(attr_list)
+
+ node_stmt = (node_id + Optional(attr_list) + semi.suppress()).setName("node_stmt")
+
+ assignment = (ID + equals.suppress() + righthand_id).setName("assignment")
+ stmt = (assignment | edge_stmt | attr_stmt | node_stmt | subgraph).setName("stmt")
+ stmt_list << OneOrMore(stmt + Optional(semi.suppress()))
+
+ graphparser = (Optional(strict_) + Group((graph_ | digraph_)) + \
+ Optional(ID) + graph_stmt).setResultsName("graph")
+
+ singleLineComment = "//" + restOfLine
+ graphparser.ignore(singleLineComment)
+ graphparser.ignore(cStyleComment)
+
+ assignment.setParseAction(push_attr_list)
+ a_list.setParseAction(push_attr_list)
+ edge_stmt.setParseAction(push_edge_stmt)
+ node_stmt.setParseAction(push_node_stmt)
+ attr_stmt.setParseAction(push_default_stmt)
+
+ subgraph.setParseAction(push_subgraph_stmt)
+ graph_stmt.setParseAction(push_graph_stmt)
+ graphparser.setParseAction(push_top_graph_stmt)
+
+ return graphparser
+
+
+def parse_dot_data(data):
+ try:
+ graphparser = GRAPH_DEF()
+ if pyparsing_version >= '1.2':
+ graphparser.parseWithTabs()
+ tokens = graphparser.parseString(data)
+ graph = tokens.graph
+ return graph
+ except ParseException, err:
+ print err.line
+ print " "*(err.column-1) + "^"
+ print err
+ return None
diff --git a/bin/addons/base/ir/workflow/pydot/pydot.py b/bin/addons/base/ir/workflow/pydot/pydot.py
new file mode 100755
index 00000000000..947099f3ca0
--- /dev/null
+++ b/bin/addons/base/ir/workflow/pydot/pydot.py
@@ -0,0 +1,1075 @@
+# -*- coding: Latin-1 -*-
+"""Graphviz's dot language Python interface.
+
+This module provides with a full interface to create handle modify
+and process graphs in Graphviz's dot language.
+
+References:
+
+pydot Homepage: http://www.dkbza.org/pydot.html
+Graphviz: http://www.research.att.com/sw/tools/graphviz/
+DOT Language: http://www.research.att.com/~erg/graphviz/info/lang.html
+
+Programmed and tested with Graphviz 1.12 and Python 2.3.3 on GNU/Linux
+by Ero Carrera (c) 2004 [ero@dkbza.org]
+
+Distributed under MIT license [http://opensource.org/licenses/mit-license.html].
+"""
+
+__author__ = 'Ero Carrera'
+__version__ = '0.9.9'
+__license__ = 'MIT'
+
+import os
+import tempfile
+import copy
+import dot_parser
+
+
+def graph_from_dot_data(data):
+ """Load graph as defined by data in DOT format.
+
+ The data is assumed to be in DOT format. It will
+ be parsed and a Dot class will be returned,
+ representing the graph.
+ """
+
+ graph = dot_parser.parse_dot_data(data)
+ if graph is not None:
+ dot = Dot()
+ dot.__dict__.update(graph.__dict__)
+ return dot
+
+ return None
+
+def graph_from_dot_file(path):
+ """Load graph as defined by a DOT file.
+
+ The file is assumed to be in DOT format. It will
+ be loaded, parsed and a Dot class will be returned,
+ representing the graph.
+ """
+
+ fd = file(path, 'rb')
+ data = fd.read()
+ fd.close()
+
+ return graph_from_dot_data(data)
+
+
+def graph_from_edges(edge_list, node_prefix='', directed=False):
+ """Creates a basic graph out of an edge list.
+
+ The edge list has to be a list of tuples representing
+ the nodes connected by the edge.
+ The values can be anything: bool, int, float, str.
+
+ If the graph is undirected by default, it is only
+ calculated from one of the symmetric halves of the matrix.
+ """
+ if directed:
+ graph = Dot(graph_type='digraph')
+ else:
+ graph = Dot(graph_type='graph')
+ for edge in edge_list:
+ e = Edge(node_prefix+str(edge[0]), node_prefix+str(edge[1]))
+ graph.add_edge(e)
+ return graph
+
+
+def graph_from_adjacency_matrix(matrix, node_prefix='', directed=False):
+ """Creates a basic graph out of an adjacency matrix.
+
+ The matrix has to be a list of rows of values
+ representing an adjacency matrix.
+ The values can be anything: bool, int, float, as long
+ as they can evaluate to True or False.
+ """
+ node_orig = 1
+ if directed:
+ graph = Dot(graph_type='digraph')
+ else:
+ graph = Dot(graph_type='graph')
+ for row in matrix:
+ if not directed:
+ skip = matrix.index(row)
+ r = row[skip:]
+ else:
+ skip = 0
+ r = row
+ node_dest = skip+1
+ for e in r:
+ if e:
+ graph.add_edge( \
+ Edge( node_prefix+str(node_orig), \
+ node_prefix+str(node_dest)))
+ node_dest += 1
+ node_orig += 1
+ return graph
+
+def graph_from_incidence_matrix(matrix, node_prefix='', directed=False):
+ """Creates a basic graph out of an incidence matrix.
+
+ The matrix has to be a list of rows of values
+ representing an incidence matrix.
+ The values can be anything: bool, int, float, as long
+ as they can evaluate to True or False.
+ """
+ node_orig = 1
+ if directed:
+ graph = Dot(graph_type='digraph')
+ else:
+ graph = Dot(graph_type='graph')
+ for row in matrix:
+ nodes = []
+ c = 1
+ for node in row:
+ if node:
+ nodes.append(c*node)
+ c += 1
+ nodes.sort()
+ if len(nodes) == 2:
+ graph.add_edge( \
+ Edge(node_prefix+str(abs(nodes[0])), \
+ node_prefix+str(nodes[1]) ))
+ if not directed:
+ graph.set_simplify(True)
+ return graph
+
+
+def find_graphviz():
+ """Locate Graphviz's executables in the system.
+
+ Attempts to locate graphviz's executables in a Unix system.
+ It will look for 'dot', 'twopi' and 'neato' in all the directories
+ specified in the PATH environment variable.
+ It will return a dictionary containing the program names as keys
+ and their paths as values.
+ """
+ progs = {'dot': '', 'twopi': '', 'neato': '', 'circo': '', 'fdp': ''}
+ if not os.environ.has_key('PATH'):
+ return None
+ for path in os.environ['PATH'].split(os.pathsep):
+ for prg in progs.keys():
+ if os.path.exists(path+os.path.sep+prg):
+ progs[prg] = path+os.path.sep+prg
+ elif os.path.exists(path+os.path.sep+prg + '.exe'):
+ progs[prg] = path+os.path.sep+prg + '.exe'
+ return progs
+
+class Common:
+ """Common information to several classes.
+
+ Should not be directly used, several classes are derived from
+ this one.
+ """
+ chars_ID = None
+ parent_graph = None
+
+ def char_range(self, a,b):
+ """Generate a list containing a range of characters.
+
+ Returns a list of characters starting from 'a' up to 'b'
+ both inclusive.
+ """
+ return map(chr, range(ord(a), ord(b)+1))
+
+ def is_ID(self, s):
+ """Checks whether a string is an dot language ID.
+
+ It will check whether the string is solely composed
+ by the characters allowed in an ID or not.
+ """
+ if not self.chars_ID:
+ self.chars_ID = self.char_range('a','z')+self.char_range('A','Z')+ \
+ self.char_range('0','9')+['_']
+ for c in s:
+ if c not in self.chars_ID:
+ return False
+ return True
+
+class Error(Exception):
+ """General error handling class.
+ """
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return self.value
+
+
+class Node(object, Common):
+ """A graph node.
+
+ This class represents a graph's node with all its attributes.
+
+ node(name, attribute=value, ...)
+
+ name: node's name
+
+ All the attributes defined in the Graphviz dot language should
+ be supported.
+ """
+ attributes = ['showboxes', 'URL', 'fontcolor', 'fontsize', 'label', 'fontname', \
+ 'comment', 'root', 'toplabel', 'vertices', 'width', 'z', 'bottomlabel', \
+ 'distortion', 'fixedsize', 'group', 'height', 'orientation', 'pin', \
+ 'rects', 'regular', 'shape', 'shapefile', 'sides', 'skew', 'pos', \
+ 'layer', 'tooltip', 'style', 'target', 'color', 'peripheries',
+ 'fillcolor']
+
+ def __init__(self, name, **attrs):
+
+ if isinstance(name, str) and not name.startswith('"'):
+ idx = name.find(':')
+ if idx>0:
+ name = name[:idx]
+
+ self.name = str(name)
+ for attr in self.attributes:
+ # Set all the attributes to None.
+ self.__setattr__(attr, None)
+ # Generate all the Setter methods.
+ self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x))
+ # Generate all the Getter methods.
+ self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a))
+ for attr, val in attrs.items():
+ self.__setattr__(attr, val)
+
+ def set_name(self, node_name):
+ """Set the node's name."""
+
+ self.name = str(node_name)
+
+ def get_name(self):
+ """Get the node's name."""
+
+ return self.name
+
+ def set(self, name, value):
+ """Set an attribute value by name.
+
+ Given an attribute 'name' it will set its value to 'value'.
+ There's always the possibility of using the methods:
+ set_'name'(value)
+ which are defined for all the existing attributes.
+ """
+ if name in self.attributes:
+ self.__dict__[name] = value
+ return True
+ # Attribute is not known
+ return False
+
+ def to_string(self):
+ """Returns a string representation of the node in dot language.
+ """
+
+ if not isinstance(self.name, str):
+ self.name = str(self.name)
+
+ # RMF: special case defaults for node, edge and graph properties.
+ if self.name in ['node', 'edge', 'graph']:
+ node = self.name
+ else:
+ node = '"'+self.name+'"'
+
+ node_attr = None
+ all_attrs = [a for a in self.attributes]
+ all_attrs += [a for a in Graph.attributes if a not in all_attrs]
+ all_attrs += [a for a in Edge.attributes if a not in all_attrs]
+ for attr in all_attrs:
+ if self.__dict__.has_key(attr) \
+ and self.__getattribute__(attr) is not None:
+ if not node_attr:
+ node_attr = ''
+ else:
+ node_attr += ', '
+ node_attr += attr+'='
+ val = self.__dict__[attr]
+
+ if val.startswith('<') and val.endswith('>'):
+ node_attr += val
+ elif (isinstance(val, str) or isinstance(val, unicode)) and not self.is_ID(val):
+ node_attr += '"'+val+'"'
+ else:
+ node_attr += str(val)
+
+ if node_attr:
+ node += ' ['+node_attr+']'
+ node += ';'
+
+ return node
+
+
+class Edge(object, Common):
+ """A graph edge.
+
+ This class represents a graph's edge with all its attributes.
+
+ edge(src, dst, attribute=value, ...)
+
+ src: source node's name
+ dst: destination node's name
+
+ All the attributes defined in the Graphviz dot language should
+ be supported.
+
+ Attributes can be set through the dynamically generated methods:
+
+ set_[attribute name], i.e. set_label, set_fontname
+
+ or using the instance's attributes:
+
+ Edge.[attribute name], i.e. edge_instance.label, edge_instance.fontname
+ """
+ attributes = ['style', 'target', 'pos', 'layer', 'tooltip', 'color', 'showboxes',\
+ 'URL', 'fontcolor', 'fontsize', 'label', 'fontname', 'comment', 'lp', \
+ 'arrowhead', 'arrowsize', 'arrowtail', 'constraint', 'decorate', 'dir', \
+ 'headURL', 'headclip', 'headhref', 'headlabel', 'headport', \
+ 'headtarget', 'headtooltip', 'href', 'labelangle', 'labeldistance', \
+ 'labelfloat', 'labelfontcolor', 'labelfontname', 'labelfontsize', 'len',\
+ 'lhead', 'ltail', 'minlen', 'samehead', 'sametail', 'weight', 'tailURL',\
+ 'tailclip', 'tailhref', 'taillabel', 'tailport', 'tailtarget', \
+ 'tailtooltip']
+
+ def __init__(self, src, dst, **attrs):
+ self.src = src
+ self.dst = dst
+ for attr in self.attributes:
+ # Set all the attributes to None.
+ self.__setattr__(attr, None)
+ # Generate all the Setter methods.
+ self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x))
+ # Generate all the Getter methods.
+ self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a))
+ for attr, val in attrs.items():
+ self.__setattr__(attr, val)
+
+ def get_source(self):
+ """Get the edges source node name."""
+
+ return self.src
+
+ def get_destination(self):
+ """Get the edge's destination node name."""
+
+ return self.dst
+
+ def __eq__(self, edge):
+ """Compare two edges.
+
+ If the parent graph is directed, arcs linking
+ node A to B are considered equal and A->B != B->A
+
+ If the parent graph is undirected, any edge
+ connecting two nodes is equal to any other
+ edge connecting the same nodes, A->B == B->A
+ """
+
+ if not isinstance(edge, Edge):
+ raise Error, 'Can\'t compare and edge to a non-edge object.'
+ if self.parent_graph.graph_type=='graph':
+ # If the graph is undirected, the edge has neither
+ # source nor destination.
+ if (self.src==edge.src and self.dst==edge.dst) or \
+ (self.src==edge.dst and self.dst==edge.src):
+ return True
+ else:
+ if self.src==edge.src and self.dst==edge.dst:
+ return True
+ return False
+
+
+ def set(self, name, value):
+ """Set an attribute value by name.
+
+ Given an attribute 'name' it will set its value to 'value'.
+ There's always the possibility of using the methods:
+ set_'name'(value)
+ which are defined for all the existing attributes.
+ """
+ if name in self.attributes:
+ self.__dict__[name] = value
+ return True
+ # Attribute is not known
+ return False
+
+
+ def parse_node_ref(self, node_str):
+
+ if not isinstance(node_str, str):
+ node_str = str(node_str)
+
+ if node_str[0]=='"' and node_str[-1]=='"' and node_str[0].count('"')%2!=0:
+ return node_str
+
+ node_port_idx = node_str.rfind(':')
+
+ if node_port_idx>0 and node_str[0]=='"' and node_str[node_port_idx-1]=='"':
+ return node_str
+
+ node_str = node_str.replace('"', '')
+
+ if node_port_idx>0:
+ a = node_str[:node_port_idx]
+ b = node_str[node_port_idx+1:]
+ if self.is_ID(a):
+ node = a
+ else:
+ node = '"'+a+'"'
+ if self.is_ID(b):
+ node += ':'+b
+ else:
+ node+=':"'+b+'"'
+ return node
+
+ return '"'+node_str+'"'
+
+
+ def to_string(self):
+ """Returns a string representation of the edge in dot language.
+ """
+
+ src = self.parse_node_ref(self.src)
+ dst = self.parse_node_ref(self.dst)
+
+ edge = src
+ if self.parent_graph and \
+ self.parent_graph.graph_type and \
+ self.parent_graph.graph_type=='digraph':
+ edge+=' -> '
+ else:
+ edge+=' -- '
+ edge+=dst
+
+ edge_attr = None
+ for attr in self.attributes:
+ if self.__dict__.has_key(attr) \
+ and self.__getattribute__(attr) is not None:
+ if not edge_attr:
+ edge_attr = ''
+ else:
+ edge_attr+=', '
+ edge_attr+=attr+'='
+ val = self.__dict__[attr]
+ if (isinstance(val, str) or isinstance(val, unicode)) and not self.is_ID(val):
+ edge_attr+='"'+val+'"'
+ else:
+ edge_attr+=str(val)
+
+ if edge_attr:
+ edge+=' ['+edge_attr+']'
+ edge+=';'
+
+ return edge
+
+class Graph(object, Common):
+
+ """Class representing a graph in Graphviz's dot language.
+
+ This class implements the methods to work on a representation
+ of a graph in Graphviz's dot language.
+
+ graph(graph_name='G', type='digraph', strict=False, suppress_disconnected=False, attribute=value, ...)
+
+ graph_name:
+ the graph's name
+ type:
+ can be 'graph' or 'digraph'
+ suppress_disconnected:
+ defaults to False, which will remove from the
+ graph any disconnected nodes.
+ simplify:
+ if True it will avoid displaying equal edges, i.e.
+ only one edge between two nodes. removing the
+ duplicated ones.
+
+ All the attributes defined in the Graphviz dot language should
+ be supported.
+
+ Attributes can be set through the dynamically generated methods:
+
+ set_[attribute name], i.e. set_size, set_fontname
+
+ or using the instance's attributes:
+
+ Graph.[attribute name], i.e. graph_instance.label, graph_instance.fontname
+ """
+
+ attributes = ['Damping', 'bb', 'center', 'clusterrank', 'compound', 'concentrate',\
+ 'defaultdist', 'dim', 'fontpath', 'epsilon', 'layers', 'layersep', \
+ 'margin', 'maxiter', 'mclimit', 'mindist', 'pack', 'packmode', 'model', \
+ 'page', 'pagedir', 'nodesep', 'normalize', 'nslimit1', 'ordering', \
+ 'orientation', 'outputorder', 'overlap', 'remincross', 'resolution', \
+ 'rankdir', 'ranksep', 'ratio', 'rotate', 'samplepoints', 'searchsize', \
+ 'sep', 'size', 'splines', 'start', 'stylesheet', 'truecolor', \
+ 'viewport', 'voro_margin', 'quantum', 'bgcolor', 'labeljust', \
+ 'labelloc', 'root', 'showboxes', 'URL', 'fontcolor', 'fontsize', \
+ 'label' ,'fontname', 'comment', 'lp', 'target', 'color', 'style', \
+ 'concentrators']
+
+ def __init__(self, graph_name='G', graph_type='digraph', strict=False, \
+ suppress_disconnected=False, simplify=False, **attrs):
+
+ if graph_type not in ['graph', 'digraph']:
+ raise Error, 'Invalid type. Accepted graph types are: graph, digraph, subgraph'
+ self.graph_type = graph_type
+ self.graph_name = graph_name
+ self.strict = strict
+ self.suppress_disconnected = suppress_disconnected
+ self.simplify = simplify
+ self.node_list = []
+ self.edge_list = []
+ self.edge_src_list = []
+ self.edge_dst_list = []
+ self.subgraph_list = []
+ self.parent_graph = self
+ for attr in self.attributes:
+ # Set all the attributes to None.
+ self.__setattr__(attr, None)
+ # Generate all the Setter methods.
+ self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x))
+ # Generate all the Getter methods.
+ self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a))
+ for attr, val in attrs.items():
+ self.__setattr__(attr, val)
+
+ def set_simplify(self, simplify):
+ """Set whether to simplify or not.
+
+ If True it will avoid displaying equal edges, i.e.
+ only one edge between two nodes. removing the
+ duplicated ones.
+ """
+
+ self.simplify = simplify
+
+ def get_simplify(self):
+ """Get whether to simplify or not.
+
+ Refer to set_simplify for more information.
+ """
+
+ return self.simplify
+
+
+ def set_type(self, graph_type):
+ """Set the graph's type, 'graph' or 'digraph'."""
+ self.graph_type = graph_type
+
+ def get_type(self):
+ """Get the graph's type, 'graph' or 'digraph'."""
+ return self.graph_type
+
+ def set_name(self, graph_name):
+ """Set the graph's name."""
+
+ self.graph_name = graph_name
+
+ def get_name(self):
+ """Get the graph's name."""
+
+ return self.graph_name
+
+ def set_strict(self, val):
+ """Set graph to 'strict' mode.
+
+ This option is only valid for top level graphs.
+ """
+
+ self.strict = val
+
+ def get_strict(self, val):
+ """Get graph's 'strict' mode (True, False).
+
+ This option is only valid for top level graphs.
+ """
+
+ return self.strict
+
+ def set_suppress_disconnected(self, val):
+ """Suppress disconnected nodes in the output graph.
+
+ This option will skip nodes in the graph with no incoming or outgoing
+ edges. This option works also for subgraphs and has effect only in the
+ current graph/subgraph.
+ """
+
+ self.suppress_disconnected = val
+
+ def get_suppress_disconnected(self, val):
+ """Get if suppress disconnected is set.
+
+ Refer to set_suppress_disconnected for more information.
+ """
+
+ self.suppress_disconnected = val
+
+ def set(self, name, value):
+ """Set an attribute value by name.
+
+ Given an attribute 'name' it will set its value to 'value'.
+ There's always the possibility of using the methods:
+
+ set_'name'(value)
+
+ which are defined for all the existing attributes.
+ """
+ if name in self.attributes:
+ self.__dict__[name] = value
+ return True
+ # Attribute is not known
+ return False
+
+ def get(self, name):
+ """Get an attribute value by name.
+
+ Given an attribute 'name' it will get its value.
+ There's always the possibility of using the methods:
+
+ get_'name'()
+
+ which are defined for all the existing attributes.
+ """
+ return self.__dict__[name]
+
+ def add_node(self, graph_node):
+ """Adds a node object to the graph.
+
+ It takes a node object as its only argument and returns
+ None.
+ """
+
+ if not isinstance(graph_node, Node):
+ raise Error, 'add_node received a non node class object'
+
+ if self.get_node(graph_node.get_name()) is None:
+ self.node_list.append(graph_node)
+ graph_node.parent_graph = self.parent_graph
+
+ def get_node(self, name):
+ """Retrieved a node from the graph.
+
+ Given a node's name the corresponding Node
+ instance will be returned.
+
+ If multiple nodes exist with that name, a list of
+ Node instances is returned.
+ If only one node exists, the instance is returned.
+ None is returned otherwise.
+ """
+
+ match = [node for node in self.node_list if node.get_name() == str(name)]
+
+ l = len(match)
+ if l==1:
+ return match[0]
+ elif l>1:
+ return match
+ else:
+ return None
+
+ def get_node_list(self):
+ """Get the list of Node instances.
+
+ This method returns the list of Node instances
+ composing the graph.
+ """
+
+ return self.node_list
+
+ def add_edge(self, graph_edge):
+ """Adds an edge object to the graph.
+
+ It takes a edge object as its only argument and returns
+ None.
+ """
+
+ if not isinstance(graph_edge, Edge):
+ raise Error, 'add_edge received a non edge class object'
+
+ self.edge_list.append(graph_edge)
+
+ src = self.get_node(graph_edge.get_source())
+ if src is None:
+ self.add_node(Node(graph_edge.get_source()))
+
+ dst = self.get_node(graph_edge.get_destination())
+ if dst is None:
+ self.add_node(Node(graph_edge.get_destination()))
+
+ graph_edge.parent_graph = self.parent_graph
+
+ if graph_edge.src not in self.edge_src_list:
+ self.edge_src_list.append(graph_edge.src)
+
+ if graph_edge.dst not in self.edge_dst_list:
+ self.edge_dst_list.append(graph_edge.dst)
+
+ def get_edge(self, src, dst):
+ """Retrieved an edge from the graph.
+
+ Given an edge's source and destination the corresponding
+ Edge instance will be returned.
+
+ If multiple edges exist with that source and destination,
+ a list of Edge instances is returned.
+ If only one edge exists, the instance is returned.
+ None is returned otherwise.
+ """
+
+ match = [edge for edge in self.edge_list if edge.src == src and edge.dst == dst]
+
+ l = len(match)
+ if l==1:
+ return match[0]
+ elif l>1:
+ return match
+ else:
+ return None
+
+ def get_edge_list(self):
+ """Get the list of Edge instances.
+
+ This method returns the list of Edge instances
+ composing the graph.
+ """
+
+ return self.edge_list
+
+ def add_subgraph(self, sgraph):
+ """Adds an edge object to the graph.
+
+ It takes a subgraph object as its only argument and returns
+ None.
+ """
+ if not isinstance(sgraph, Subgraph) and not isinstance(sgraph, Cluster):
+ raise Error, 'add_subgraph received a non subgraph class object'
+
+ self.subgraph_list.append(sgraph)
+
+ sgraph.set_graph_parent(self.parent_graph)
+ return None
+
+ def get_subgraph(self, name):
+ """Retrieved a subgraph from the graph.
+
+ Given a subgraph's name the corresponding
+ Subgraph instance will be returned.
+
+ If multiple subgraphs exist with the same name, a list of
+ Subgraph instances is returned.
+ If only one Subgraph exists, the instance is returned.
+ None is returned otherwise.
+ """
+
+ match = [sgraph for sgraph in self.subgraph_list if sgraph.graph_name == name]
+
+ l = len(match)
+ if l==1:
+ return match[0]
+ elif l>1:
+ return match
+ else:
+ return None
+
+ def get_subgraph_list(self):
+ """Get the list of Subgraph instances.
+
+ This method returns the list of Subgraph instances
+ in the graph.
+ """
+
+ return self.subgraph_list
+
+ def set_graph_parent(self, parent):
+ """Sets a graph and its elements to point the the parent.
+
+ Any subgraph added to a parent graph receives a reference
+ to the parent to access some common data.
+ """
+ self.parent_graph = parent
+
+ for elm in self.edge_list:
+ elm.parent_graph = parent
+
+ for elm in self.node_list:
+ elm.parent_graph = parent
+
+ for elm in self.subgraph_list:
+ elm.parent_graph = parent
+ elm.set_graph_parent(parent)
+
+ def to_string(self, indent=''):
+ """Returns a string representation of the graph in dot language.
+
+ It will return the graph and all its subelements in string from.
+ """
+ graph = indent+''
+ if self.__dict__.has_key('strict'):
+ if self==self.parent_graph and self.strict:
+ graph+='strict '
+
+ graph+=self.graph_type+' '+self.graph_name+' {\n'
+
+ for attr in self.attributes:
+ if self.__dict__.has_key(attr) \
+ and self.__getattribute__(attr) is not None:
+ graph += indent+'\t'+attr+'='
+ val = self.__dict__[attr]
+ if isinstance(val, str) and not self.is_ID(val):
+ graph += '"'+val+'"'
+ else:
+ graph += str(val)
+ graph+=';\n'
+
+ # RMF: for graphviz to handle edge/node defaults correctly,
+ # it seems they must appear before the rest of the nodes/edges
+ # so if they exist, dump them out first.
+ for elm in self.node_list:
+ if elm.name in ['node', 'edge', 'graph']:
+ graph += indent+'\t'+elm.to_string()+'\n'
+
+ for elm in self.subgraph_list:
+ graph += elm.to_string(indent+'\t')+'\n'
+
+ for elm in self.node_list:
+ # RMF: don't repeat the node/edge defaults
+ if elm.name in ['node', 'edge', 'graph']:
+ continue
+ if self.suppress_disconnected:
+ if elm.name not in self.edge_src_list+self.edge_dst_list:
+ continue
+ graph+=indent+'\t'+elm.to_string()+'\n'
+
+ edges_done = []
+ for elm in self.edge_list:
+ if self.simplify and elm in edges_done:
+ continue
+ graph+=indent+'\t'+elm.to_string()+'\n'
+ edges_done.append(elm)
+
+ graph+=indent+'}\n'
+
+ return graph
+
+
+class Subgraph(Graph):
+
+ """Class representing a subgraph in Graphviz's dot language.
+
+ This class implements the methods to work on a representation
+ of a subgraph in Graphviz's dot language.
+
+ subgraph(graph_name='subG', suppress_disconnected=False, attribute=value, ...)
+
+ graph_name:
+ the subgraph's name
+ suppress_disconnected:
+ defaults to false, which will remove from the
+ subgraph any disconnected nodes.
+ All the attributes defined in the Graphviz dot language should
+ be supported.
+
+ Attributes can be set through the dynamically generated methods:
+
+ set_[attribute name], i.e. set_size, set_fontname
+
+ or using the instance's attributes:
+
+ Subgraph.[attribute name], i.e.
+ subgraph_instance.label, subgraph_instance.fontname
+ """
+
+ attributes = Graph.attributes+['rank']
+
+ # RMF: subgraph should have all the attributes of graph so it can be passed
+ # as a graph to all methods
+ def __init__(self, graph_name='subG', suppress_disconnected=False, \
+ simplify=False, **attrs):
+
+ self.graph_type = 'subgraph'
+ self.graph_name = graph_name
+ self.suppress_disconnected = suppress_disconnected
+ self.simplify = simplify
+ self.node_list = []
+ self.edge_list = []
+ self.edge_src_list = []
+ self.edge_dst_list = []
+ self.subgraph_list = []
+ for attr in self.attributes:
+ # Set all the attributes to None.
+ self.__setattr__(attr, None)
+ # Generate all the Setter methods.
+ self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x))
+ # Generate all the Getter methods.
+ self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a))
+ for attr, val in attrs.items():
+ self.__setattr__(attr, val)
+
+
+class Cluster(Graph):
+
+ """Class representing a cluster in Graphviz's dot language.
+
+ This class implements the methods to work on a representation
+ of a cluster in Graphviz's dot language.
+
+ cluster(graph_name='subG', suppress_disconnected=False, attribute=value, ...)
+
+ graph_name:
+ the cluster's name (the string 'cluster' will be always prepended)
+ suppress_disconnected:
+ defaults to false, which will remove from the
+ cluster any disconnected nodes.
+ All the attributes defined in the Graphviz dot language should
+ be supported.
+
+ Attributes can be set through the dynamically generated methods:
+
+ set_[attribute name], i.e. set_color, set_fontname
+
+ or using the instance's attributes:
+
+ Cluster.[attribute name], i.e.
+ cluster_instance.color, cluster_instance.fontname
+ """
+
+ attributes = ['pencolor', 'bgcolor', 'labeljust', 'labelloc', 'URL', 'fontcolor', \
+ 'fontsize', 'label', 'fontname', 'lp', 'style', 'target', 'color', \
+ 'peripheries', 'fillcolor']
+
+ def __init__(self, graph_name='subG', suppress_disconnected=False, \
+ simplify=False, **attrs):
+
+ #if type not in ['subgraph']:
+ # raise Error, 'Invalid type. Accepted graph types are: subgraph'
+ self.graph_type = 'subgraph'
+ self.graph_name = 'cluster_'+graph_name
+ self.suppress_disconnected = suppress_disconnected
+ self.simplify = simplify
+ self.node_list = []
+ self.edge_list = []
+ self.edge_src_list = []
+ self.edge_dst_list = []
+ self.subgraph_list = []
+ for attr in self.attributes:
+ # Set all the attributes to None.
+ self.__setattr__(attr, None)
+ # Generate all the Setter methods.
+ self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x))
+ # Generate all the Getter methods.
+ self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a))
+ for attr, val in attrs.items():
+ self.__setattr__(attr, val)
+
+
+class Dot(Graph):
+ """A container for handling a dot language file.
+
+ This class implements methods to write and process
+ a dot language file. It is a derived class of
+ the base class 'Graph'.
+ """
+
+ progs = None
+
+ formats = ['ps', 'ps2', 'hpgl', 'pcl', 'mif', 'pic', 'gd', 'gd2', 'gif', 'jpg', \
+ 'jpeg', 'png', 'wbmp', 'ismap', 'imap', 'cmap', 'vrml', 'vtx', 'mp', \
+ 'fig', 'svg', 'svgz', 'dia', 'dot', 'canon', 'plain', 'plain-ext', 'xdot']
+
+ def __init__(self, prog='dot', **args):
+ Graph.__init__(self, **args)
+
+ self.prog = prog
+
+ # Automatically creates all the methods enabling the creation
+ # of output in any of the supported formats.
+ for frmt in self.formats:
+ self.__setattr__('create_'+frmt, lambda f=frmt, prog=self.prog : self.create(format=f, prog=prog))
+ f = self.__dict__['create_'+frmt]
+ f.__doc__ = '''Refer to docstring from 'create' for more information.'''
+
+ for frmt in self.formats+['raw']:
+ self.__setattr__('write_'+frmt, lambda path, f=frmt, prog=self.prog : self.write(path, format=f, prog=prog))
+ f = self.__dict__['write_'+frmt]
+ f.__doc__ = '''Refer to docstring from 'write' for more information.'''
+
+ def set_prog(self, prog):
+ """Sets the default program.
+
+ Sets the default program in charge of processing
+ the dot file into a graph.
+ """
+ self.prog = prog
+
+ def write(self, path, prog=None, format='raw'):
+ """Writes a graph to a file.
+
+ Given a filename 'path' it will open/create and truncate
+ such file and write on it a representation of the graph
+ defined by the dot object and in the format specified by
+ 'format'.
+ The format 'raw' is used to dump the string representation
+ of the Dot object, without further processing.
+ The output can be processed by any of graphviz tools, defined
+ in 'prog', which defaults to 'dot'
+ Returns True or False according to the success of the write
+ operation.
+
+ There's also the preferred possibility of using:
+
+ write_'format'(path, prog='program')
+
+ which are automatically defined for all the supported formats.
+ [write_ps(), write_gif(), write_dia(), ...]
+ """
+
+ if prog is None:
+ prog = self.prog
+
+ dot_fd = file(path, "w+b")
+ if format == 'raw':
+ dot_fd.write(self.to_string())
+ else:
+ dot_fd.write(self.create(prog, format))
+ dot_fd.close()
+
+ return True
+
+ def create(self, prog=None, format='ps'):
+ """Creates and returns a Postscript representation of the graph.
+
+ create will write the graph to a temporary dot file and process
+ it with the program given by 'prog' (which defaults to 'twopi'),
+ reading the Postscript output and returning it as a string is the
+ operation is successful.
+ On failure None is returned.
+
+ There's also the preferred possibility of using:
+
+ create_'format'(prog='program')
+
+ which are automatically defined for all the supported formats.
+ [create_ps(), create_gif(), create_dia(), ...]
+ """
+ if prog is None:
+ prog = self.prog
+
+ if self.progs is None:
+ self.progs = find_graphviz()
+ if self.progs is None:
+ return None
+ if not self.progs.has_key(prog):
+ # Program not found ?!?!
+ return None
+
+ tmp_fd, tmp_name = tempfile.mkstemp()
+ os.close(tmp_fd)
+ self.write(tmp_name)
+ stdin, stdout, stderr = os.popen3(self.progs[prog]+' -T'+format+' '+tmp_name, 'b')
+ stdin.close()
+ stderr.close()
+ data = stdout.read()
+ stdout.close()
+ os.unlink(tmp_name)
+ return data
+
diff --git a/bin/addons/base/ir/workflow/pydot/setup.py b/bin/addons/base/ir/workflow/pydot/setup.py
new file mode 100755
index 00000000000..cd8956bebbd
--- /dev/null
+++ b/bin/addons/base/ir/workflow/pydot/setup.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+from distutils.core import setup
+import pydot
+
+setup( name = 'pydot',
+ version = pydot.__version__,
+ description = 'Python interface to Graphiz\'s Dot',
+ author = 'Ero Carrera',
+ author_email = 'ero@dkbza.org',
+ url = 'http://dkbza.org/pydot.html',
+ license = 'MIT',
+ platforms = ["any"],
+ classifiers = ['Development Status :: 5 - Production/Stable', \
+ 'Intended Audience :: Science/Research', \
+ 'License :: OSI Approved :: MIT License',\
+ 'Natural Language :: English', \
+ 'Operating System :: OS Independent', \
+ 'Programming Language :: Python', \
+ 'Topic :: Scientific/Engineering :: Visualization',\
+ 'Topic :: Software Development :: Libraries :: Python Modules'],
+ long_description = "\n".join(pydot.__doc__.split('\n')),
+ py_modules = ['pydot', 'dot_parser'])