1c522f
From 050e291cbf63fd190f1ec14d65d1154a23ce8756 Mon Sep 17 00:00:00 2001
1c522f
From: Sandro Tosi <morph@debian.org>
1c522f
Date: Sun, 10 Jan 2016 00:25:45 +0000
1c522f
Subject: support python3
1c522f
1c522f
Origin: https://github.com/nlhepler/pydot/tree/adf18a858a63b321b7e4ffd964a24d73add1bf4f
1c522f
Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=739858
1c522f
Reviewed-by: Sandro Tosi <morph@debian.org>
1c522f
Last-Update: 2016-01-10
1c522f
1c522f
---
1c522f
 dot_parser.py |  429 ++++++++--------
1c522f
 pydot.py      | 1553 +++++++++++++++++++++++++++------------------------------
1c522f
 setup.py      |    6 +-
1c522f
 3 files changed, 954 insertions(+), 1034 deletions(-)
1c522f
1c522f
diff --git a/dot_parser.py b/dot_parser.py
1c522f
index dedd61a..4cdd482 100644
1c522f
--- a/dot_parser.py
1c522f
+++ b/dot_parser.py
1c522f
@@ -1,4 +1,3 @@
1c522f
-# -*- coding: Latin-1 -*-
1c522f
 """Graphviz's dot language parser.
1c522f
 
1c522f
 The dotparser parses graphviz files in dot and dot files and transforms them
1c522f
@@ -10,236 +9,215 @@ Author: Michael Krause <michael@krause-software.de>
1c522f
 Fixes by: Ero Carrera <ero@dkbza.org>
1c522f
 """
1c522f
 
1c522f
+from __future__ import division, print_function
1c522f
+
1c522f
 __author__ = ['Michael Krause', 'Ero Carrera']
1c522f
 __license__ = 'MIT'
1c522f
 
1c522f
-
1c522f
 import sys
1c522f
-import glob
1c522f
 import pydot
1c522f
-import re
1c522f
 import codecs
1c522f
 
1c522f
 from pyparsing import __version__ as pyparsing_version
1c522f
 
1c522f
-from pyparsing import ( nestedExpr, Literal, CaselessLiteral, Word, Upcase, OneOrMore, ZeroOrMore,
1c522f
-    Forward, NotAny, delimitedList, oneOf, Group, Optional, Combine, alphas, nums,
1c522f
-    restOfLine, cStyleComment, nums, alphanums, printables, empty, quotedString,
1c522f
-    ParseException, ParseResults, CharsNotIn, _noncomma, dblQuotedString, QuotedString, ParserElement )
1c522f
+from pyparsing import (
1c522f
+    nestedExpr, Literal, CaselessLiteral, Word, OneOrMore,
1c522f
+    Forward, Group, Optional, Combine, nums, restOfLine,
1c522f
+    cStyleComment, alphanums, printables, ParseException,
1c522f
+    ParseResults, CharsNotIn, QuotedString
1c522f
+    )
1c522f
+
1c522f
+
1c522f
+PY3 = not sys.version_info < (3, 0, 0)
1c522f
+
1c522f
+if PY3:
1c522f
+    basestring = str
1c522f
 
1c522f
 
1c522f
 class P_AttrList:
1c522f
 
1c522f
     def __init__(self, toks):
1c522f
-
1c522f
         self.attrs = {}
1c522f
         i = 0
1c522f
-        
1c522f
+
1c522f
         while i < len(toks):
1c522f
             attrname = toks[i]
1c522f
-            if i+2 < len(toks) and toks[i+1] == '=':
1c522f
-                attrvalue = toks[i+2]
1c522f
+            if i + 2 < len(toks) and toks[i + 1] == '=':
1c522f
+                attrvalue = toks[i + 2]
1c522f
                 i += 3
1c522f
             else:
1c522f
                 attrvalue = None
1c522f
                 i += 1
1c522f
-                
1c522f
+
1c522f
             self.attrs[attrname] = attrvalue
1c522f
-            
1c522f
-            
1c522f
-    def __repr__(self):
1c522f
 
1c522f
+    def __repr__(self):
1c522f
         return "%s(%r)" % (self.__class__.__name__, self.attrs)
1c522f
 
1c522f
 
1c522f
-
1c522f
 class DefaultStatement(P_AttrList):
1c522f
 
1c522f
     def __init__(self, default_type, attrs):
1c522f
-
1c522f
         self.default_type = default_type
1c522f
         self.attrs = attrs
1c522f
 
1c522f
     def __repr__(self):
1c522f
-
1c522f
-        return "%s(%s, %r)" % (self.__class__.__name__,
1c522f
-            self.default_type, self.attrs)
1c522f
+        return "%s(%s, %r)" % (
1c522f
+            self.__class__.__name__,
1c522f
+            self.default_type, self.attrs
1c522f
+            )
1c522f
 
1c522f
 
1c522f
 top_graphs = list()
1c522f
 
1c522f
-def push_top_graph_stmt(str, loc, toks):
1c522f
 
1c522f
+def push_top_graph_stmt(str, loc, toks):
1c522f
     attrs = {}
1c522f
     g = None
1c522f
-    
1c522f
+
1c522f
     for element in toks:
1c522f
-    
1c522f
-        if( isinstance(element, (ParseResults, tuple, list)) and
1c522f
-            len(element) == 1 and isinstance(element[0], basestring) ):
1c522f
-            
1c522f
+        if (isinstance(element, (ParseResults, tuple, list)) and
1c522f
+                len(element) == 1 and isinstance(element[0], basestring)):
1c522f
             element = element[0]
1c522f
-            
1c522f
+
1c522f
         if element == 'strict':
1c522f
             attrs['strict'] = True
1c522f
-            
1c522f
-        elif element in ['graph', 'digraph']:
1c522f
 
1c522f
+        elif element in ['graph', 'digraph']:
1c522f
             attrs = {}
1c522f
-            
1c522f
+
1c522f
             g = pydot.Dot(graph_type=element, **attrs)
1c522f
             attrs['type'] = element
1c522f
-            
1c522f
-            top_graphs.append( g )
1c522f
-            
1c522f
-        elif isinstance( element, basestring):
1c522f
-            g.set_name( element )
1c522f
-            
1c522f
+
1c522f
+            top_graphs.append(g)
1c522f
+
1c522f
+        elif isinstance(element, basestring):
1c522f
+            g.set_name(element)
1c522f
+
1c522f
         elif isinstance(element, pydot.Subgraph):
1c522f
-        
1c522f
-            g.obj_dict['attributes'].update( element.obj_dict['attributes'] )
1c522f
-            g.obj_dict['edges'].update( element.obj_dict['edges'] )
1c522f
-            g.obj_dict['nodes'].update( element.obj_dict['nodes'] )
1c522f
-            g.obj_dict['subgraphs'].update( element.obj_dict['subgraphs'] )
1c522f
-            
1c522f
+            g.obj_dict['attributes'].update(element.obj_dict['attributes'])
1c522f
+            g.obj_dict['edges'].update(element.obj_dict['edges'])
1c522f
+            g.obj_dict['nodes'].update(element.obj_dict['nodes'])
1c522f
+            g.obj_dict['subgraphs'].update(element.obj_dict['subgraphs'])
1c522f
             g.set_parent_graph(g)
1c522f
-            
1c522f
+
1c522f
         elif isinstance(element, P_AttrList):
1c522f
             attrs.update(element.attrs)
1c522f
 
1c522f
         elif isinstance(element, (ParseResults, list)):
1c522f
             add_elements(g, element)
1c522f
-            
1c522f
+
1c522f
         else:
1c522f
-            raise ValueError, "Unknown element statement: %r " % element
1c522f
-    
1c522f
-    
1c522f
+            raise ValueError("Unknown element statement: %r " % element)
1c522f
+
1c522f
     for g in top_graphs:
1c522f
         update_parent_graph_hierarchy(g)
1c522f
-    
1c522f
-    if len( top_graphs ) == 1:
1c522f
+
1c522f
+    if len(top_graphs) == 1:
1c522f
         return top_graphs[0]
1c522f
-        
1c522f
+
1c522f
     return top_graphs
1c522f
 
1c522f
 
1c522f
 def update_parent_graph_hierarchy(g, parent_graph=None, level=0):
1c522f
-
1c522f
-
1c522f
     if parent_graph is None:
1c522f
         parent_graph = g
1c522f
-        
1c522f
-    for key_name in ('edges',):
1c522f
 
1c522f
+    for key_name in ('edges',):
1c522f
         if isinstance(g, pydot.frozendict):
1c522f
             item_dict = g
1c522f
         else:
1c522f
             item_dict = g.obj_dict
1c522f
-            
1c522f
-        if not item_dict.has_key( key_name ):
1c522f
+
1c522f
+        if key_name not in item_dict:
1c522f
             continue
1c522f
 
1c522f
         for key, objs in item_dict[key_name].items():
1c522f
             for obj in objs:
1c522f
-                if 'parent_graph' in obj and obj['parent_graph'].get_parent_graph()==g:
1c522f
+                if 'parent_graph' in obj and obj['parent_graph'].get_parent_graph() == g:
1c522f
                     if obj['parent_graph'] is g:
1c522f
                         pass
1c522f
                     else:
1c522f
                         obj['parent_graph'].set_parent_graph(parent_graph)
1c522f
 
1c522f
                 if key_name == 'edges' and len(key) == 2:
1c522f
-                    for idx, vertex in enumerate( obj['points'] ):
1c522f
-                        if isinstance( vertex, (pydot.Graph, pydot.Subgraph, pydot.Cluster)):
1c522f
+                    for idx, vertex in enumerate(obj['points']):
1c522f
+                        if isinstance(vertex, (pydot.Graph, pydot.Subgraph, pydot.Cluster)):
1c522f
                             vertex.set_parent_graph(parent_graph)
1c522f
-                        if isinstance( vertex, pydot.frozendict):
1c522f
+                        if isinstance(vertex, pydot.frozendict):
1c522f
                             if vertex['parent_graph'] is g:
1c522f
                                 pass
1c522f
                             else:
1c522f
                                 vertex['parent_graph'].set_parent_graph(parent_graph)
1c522f
 
1c522f
 
1c522f
-
1c522f
 def add_defaults(element, defaults):
1c522f
-
1c522f
     d = element.__dict__
1c522f
     for key, value in defaults.items():
1c522f
         if not d.get(key):
1c522f
             d[key] = value
1c522f
 
1c522f
 
1c522f
-
1c522f
 def add_elements(g, toks, defaults_graph=None, defaults_node=None, defaults_edge=None):
1c522f
-    
1c522f
     if defaults_graph is None:
1c522f
         defaults_graph = {}
1c522f
     if defaults_node is None:
1c522f
         defaults_node = {}
1c522f
     if defaults_edge is None:
1c522f
         defaults_edge = {}
1c522f
-        
1c522f
+
1c522f
     for elm_idx, element in enumerate(toks):
1c522f
-        
1c522f
         if isinstance(element, (pydot.Subgraph, pydot.Cluster)):
1c522f
-        
1c522f
             add_defaults(element, defaults_graph)
1c522f
             g.add_subgraph(element)
1c522f
-            
1c522f
+
1c522f
         elif isinstance(element, pydot.Node):
1c522f
-        
1c522f
             add_defaults(element, defaults_node)
1c522f
             g.add_node(element)
1c522f
-            
1c522f
+
1c522f
         elif isinstance(element, pydot.Edge):
1c522f
-        
1c522f
             add_defaults(element, defaults_edge)
1c522f
             g.add_edge(element)
1c522f
-            
1c522f
+
1c522f
         elif isinstance(element, ParseResults):
1c522f
-        
1c522f
             for e in element:
1c522f
                 add_elements(g, [e], defaults_graph, defaults_node, defaults_edge)
1c522f
-                
1c522f
+
1c522f
         elif isinstance(element, DefaultStatement):
1c522f
-        
1c522f
             if element.default_type == 'graph':
1c522f
-            
1c522f
                 default_graph_attrs = pydot.Node('graph', **element.attrs)
1c522f
                 g.add_node(default_graph_attrs)
1c522f
 
1c522f
             elif element.default_type == 'node':
1c522f
-            
1c522f
                 default_node_attrs = pydot.Node('node', **element.attrs)
1c522f
                 g.add_node(default_node_attrs)
1c522f
 
1c522f
             elif element.default_type == 'edge':
1c522f
-            
1c522f
                 default_edge_attrs = pydot.Node('edge', **element.attrs)
1c522f
                 g.add_node(default_edge_attrs)
1c522f
                 defaults_edge.update(element.attrs)
1c522f
 
1c522f
             else:
1c522f
-                raise ValueError, "Unknown DefaultStatement: %s " % element.default_type
1c522f
-                
1c522f
+                raise ValueError("Unknown DefaultStatement: %s " % element.default_type)
1c522f
+
1c522f
         elif isinstance(element, P_AttrList):
1c522f
-        
1c522f
             g.obj_dict['attributes'].update(element.attrs)
1c522f
 
1c522f
         else:
1c522f
-            raise ValueError, "Unknown element statement: %r" % element
1c522f
+            raise ValueError("Unknown element statement: %r" % element)
1c522f
 
1c522f
 
1c522f
-def push_graph_stmt(str, loc, toks):            
1c522f
-                       
1c522f
+def push_graph_stmt(str, loc, toks):
1c522f
     g = pydot.Subgraph('')
1c522f
     add_elements(g, toks)
1c522f
     return g
1c522f
 
1c522f
 
1c522f
 def push_subgraph_stmt(str, loc, toks):
1c522f
-
1c522f
     g = pydot.Subgraph('')
1c522f
+
1c522f
     for e in toks:
1c522f
-        if len(e)==3:
1c522f
+        if len(e) == 3:
1c522f
             e[2].set_name(e[1])
1c522f
             if e[0] == 'subgraph':
1c522f
                 e[2].obj_dict['show_keyword'] = True
1c522f
@@ -253,11 +231,9 @@ def push_subgraph_stmt(str, loc, toks):
1c522f
 
1c522f
 
1c522f
 def push_default_stmt(str, loc, toks):
1c522f
-
1c522f
     # The pydot class instances should be marked as
1c522f
     # default statements to be inherited by actual
1c522f
     # graphs, nodes and edges.
1c522f
-    #
1c522f
     default_type = toks[0][0]
1c522f
     if len(toks) > 1:
1c522f
         attrs = toks[1].attrs
1c522f
@@ -267,90 +243,80 @@ def push_default_stmt(str, loc, toks):
1c522f
     if default_type in ['graph', 'node', 'edge']:
1c522f
         return DefaultStatement(default_type, attrs)
1c522f
     else:
1c522f
-        raise ValueError, "Unknown default statement: %r " % toks
1c522f
+        raise ValueError("Unknown default statement: %r " % toks)
1c522f
 
1c522f
 
1c522f
 def push_attr_list(str, loc, toks):
1c522f
-
1c522f
     p = P_AttrList(toks)
1c522f
     return p
1c522f
 
1c522f
 
1c522f
 def get_port(node):
1c522f
-
1c522f
-    if len(node)>1:
1c522f
+    if len(node) > 1:
1c522f
         if isinstance(node[1], ParseResults):
1c522f
-            if len(node[1][0])==2:
1c522f
-                if node[1][0][0]==':':
1c522f
+            if len(node[1][0]) == 2:
1c522f
+                if node[1][0][0] == ':':
1c522f
                     return node[1][0][1]
1c522f
-                    
1c522f
     return None
1c522f
 
1c522f
-    
1c522f
-def do_node_ports(node):
1c522f
 
1c522f
+def do_node_ports(node):
1c522f
     node_port = ''
1c522f
+
1c522f
     if len(node) > 1:
1c522f
-        node_port = ''.join( [str(a)+str(b) for a,b in node[1] ] )
1c522f
+        node_port = ''.join([str(a) + str(b) for a, b in node[1]])
1c522f
 
1c522f
     return node_port
1c522f
 
1c522f
-    
1c522f
+
1c522f
 def push_edge_stmt(str, loc, toks):
1c522f
-    
1c522f
     tok_attrs = [a for a in toks if isinstance(a, P_AttrList)]
1c522f
     attrs = {}
1c522f
+
1c522f
     for a in tok_attrs:
1c522f
         attrs.update(a.attrs)
1c522f
 
1c522f
     e = []
1c522f
 
1c522f
     if isinstance(toks[0][0], pydot.Graph):
1c522f
-    
1c522f
         n_prev = pydot.frozendict(toks[0][0].obj_dict)
1c522f
-    else:        
1c522f
-        n_prev = toks[0][0] + do_node_ports( toks[0] )
1c522f
+    else:
1c522f
+        n_prev = toks[0][0] + do_node_ports(toks[0])
1c522f
 
1c522f
     if isinstance(toks[2][0], ParseResults):
1c522f
-    
1c522f
-        n_next_list = [[n.get_name(),] for n in toks[2][0] ]
1c522f
+        n_next_list = [[n.get_name()] for n in toks[2][0]]
1c522f
         for n_next in [n for n in n_next_list]:
1c522f
             n_next_port = do_node_ports(n_next)
1c522f
-            e.append(pydot.Edge(n_prev, n_next[0]+n_next_port, **attrs))
1c522f
+            e.append(pydot.Edge(n_prev, n_next[0] + n_next_port, **attrs))
1c522f
 
1c522f
     elif isinstance(toks[2][0], pydot.Graph):
1c522f
-    
1c522f
         e.append(pydot.Edge(n_prev, pydot.frozendict(toks[2][0].obj_dict), **attrs))
1c522f
 
1c522f
     elif isinstance(toks[2][0], pydot.Node):
1c522f
-    
1c522f
         node = toks[2][0]
1c522f
-        
1c522f
+
1c522f
         if node.get_port() is not None:
1c522f
             name_port = node.get_name() + ":" + node.get_port()
1c522f
         else:
1c522f
             name_port = node.get_name()
1c522f
-        
1c522f
+
1c522f
         e.append(pydot.Edge(n_prev, name_port, **attrs))
1c522f
 
1c522f
     elif isinstance(toks[2][0], type('')):
1c522f
-    
1c522f
         for n_next in [n for n in tuple(toks)[2::2]]:
1c522f
-
1c522f
             if isinstance(n_next, P_AttrList) or not isinstance(n_next[0], type('')):
1c522f
                 continue
1c522f
 
1c522f
-            n_next_port = do_node_ports( n_next )
1c522f
-            e.append(pydot.Edge(n_prev, n_next[0]+n_next_port, **attrs))
1c522f
-                
1c522f
-            n_prev = n_next[0]+n_next_port
1c522f
-            
1c522f
+            n_next_port = do_node_ports(n_next)
1c522f
+            e.append(pydot.Edge(n_prev, n_next[0] + n_next_port, **attrs))
1c522f
+
1c522f
+            n_prev = n_next[0] + n_next_port
1c522f
+
1c522f
     else:
1c522f
         # UNEXPECTED EDGE TYPE
1c522f
         pass
1c522f
-        
1c522f
-    return e
1c522f
 
1c522f
+    return e
1c522f
 
1c522f
 
1c522f
 def push_node_stmt(s, loc, toks):
1c522f
@@ -359,30 +325,25 @@ def push_node_stmt(s, loc, toks):
1c522f
         attrs = toks[1].attrs
1c522f
     else:
1c522f
         attrs = {}
1c522f
-        
1c522f
+
1c522f
     node_name = toks[0]
1c522f
     if isinstance(node_name, list) or isinstance(node_name, tuple):
1c522f
-        if len(node_name)>0:
1c522f
+        if len(node_name) > 0:
1c522f
             node_name = node_name[0]
1c522f
-    
1c522f
+
1c522f
     n = pydot.Node(str(node_name), **attrs)
1c522f
     return n
1c522f
 
1c522f
 
1c522f
-
1c522f
-    
1c522f
-    
1c522f
-
1c522f
 graphparser = None
1c522f
 
1c522f
-def graph_definition():
1c522f
 
1c522f
+def graph_definition():
1c522f
     global graphparser
1c522f
-    
1c522f
+
1c522f
     if not graphparser:
1c522f
-    
1c522f
         # punctuation
1c522f
-        colon  = Literal(":")
1c522f
+        colon = Literal(":")
1c522f
         lbrace = Literal("{")
1c522f
         rbrace = Literal("}")
1c522f
         lbrack = Literal("[")
1c522f
@@ -390,142 +351,170 @@ def graph_definition():
1c522f
         lparen = Literal("(")
1c522f
         rparen = Literal(")")
1c522f
         equals = Literal("=")
1c522f
-        comma  = Literal(",")
1c522f
-        dot    = Literal(".")
1c522f
-        slash  = Literal("/")
1c522f
-        bslash = Literal("\\")
1c522f
-        star   = Literal("*")
1c522f
-        semi   = Literal(";")
1c522f
-        at     = Literal("@")
1c522f
-        minus  = Literal("-")
1c522f
-        
1c522f
+        comma = Literal(",")
1c522f
+        # dot = Literal(".")
1c522f
+        # slash = Literal("/")
1c522f
+        # bslash = Literal("\\")
1c522f
+        # star = Literal("*")
1c522f
+        semi = Literal(";")
1c522f
+        at = Literal("@")
1c522f
+        minus = Literal("-")
1c522f
+
1c522f
         # keywords
1c522f
-        strict_    = CaselessLiteral("strict")
1c522f
-        graph_     = CaselessLiteral("graph")
1c522f
-        digraph_   = CaselessLiteral("digraph")
1c522f
-        subgraph_  = CaselessLiteral("subgraph")
1c522f
-        node_      = CaselessLiteral("node")
1c522f
-        edge_      = CaselessLiteral("edge")
1c522f
-        
1c522f
-        
1c522f
+        strict_ = CaselessLiteral("strict")
1c522f
+        graph_ = CaselessLiteral("graph")
1c522f
+        digraph_ = CaselessLiteral("digraph")
1c522f
+        subgraph_ = CaselessLiteral("subgraph")
1c522f
+        node_ = CaselessLiteral("node")
1c522f
+        edge_ = CaselessLiteral("edge")
1c522f
+
1c522f
         # token definitions
1c522f
-        
1c522f
-        identifier = Word(alphanums + "_." ).setName("identifier")
1c522f
-        
1c522f
-        double_quoted_string = QuotedString('"', multiline=True, unquoteResults=False) # dblQuotedString
1c522f
+        identifier = Word(alphanums + "_.").setName("identifier")
1c522f
+
1c522f
+        # dblQuotedString
1c522f
+        double_quoted_string = QuotedString('"', multiline=True, unquoteResults=False)
1c522f
 
1c522f
-        alphastring_ = OneOrMore(CharsNotIn(_noncomma + ' '))
1c522f
+        noncomma_ = "".join([c for c in printables if c != ","])
1c522f
+        alphastring_ = OneOrMore(CharsNotIn(noncomma_ + ' '))
1c522f
 
1c522f
         def parse_html(s, loc, toks):
1c522f
             return '<%s>' % ''.join(toks[0])
1c522f
-            
1c522f
-        
1c522f
+
1c522f
         opener = '<'
1c522f
         closer = '>'
1c522f
-        html_text = nestedExpr( opener, closer, 
1c522f
-            ( CharsNotIn( opener + closer )  ) 
1c522f
-                ).setParseAction(parse_html).leaveWhitespace()
1c522f
-
1c522f
-        ID = ( identifier | html_text | 
1c522f
-            double_quoted_string | #.setParseAction(strip_quotes) |
1c522f
-            alphastring_ ).setName("ID")
1c522f
-            
1c522f
-        
1c522f
-        float_number = Combine(Optional(minus) +	
1c522f
-            OneOrMore(Word(nums + "."))).setName("float_number")
1c522f
-            
1c522f
-        righthand_id =  (float_number | ID ).setName("righthand_id")
1c522f
+        html_text = nestedExpr(
1c522f
+            opener, closer,
1c522f
+            (CharsNotIn(opener + closer))
1c522f
+            ).setParseAction(parse_html).leaveWhitespace()
1c522f
+
1c522f
+        ID = (
1c522f
+            identifier | html_text |
1c522f
+            double_quoted_string |  # .setParseAction(strip_quotes) |
1c522f
+            alphastring_
1c522f
+            ).setName("ID")
1c522f
+
1c522f
+        float_number = Combine(
1c522f
+            Optional(minus) +
1c522f
+            OneOrMore(Word(nums + "."))
1c522f
+            ).setName("float_number")
1c522f
+
1c522f
+        righthand_id = (float_number | ID).setName("righthand_id")
1c522f
 
1c522f
         port_angle = (at + ID).setName("port_angle")
1c522f
-        
1c522f
-        port_location = (OneOrMore(Group(colon + ID)) |	
1c522f
-            Group(colon + lparen + ID + comma + ID + rparen)).setName("port_location")
1c522f
-            
1c522f
-        port = (Group(port_location + Optional(port_angle)) |	
1c522f
-            Group(port_angle + Optional(port_location))).setName("port")
1c522f
-            
1c522f
+
1c522f
+        port_location = (
1c522f
+            OneOrMore(Group(colon + ID)) |
1c522f
+            Group(colon + lparen + ID + comma + ID + rparen)
1c522f
+            ).setName("port_location")
1c522f
+
1c522f
+        port = (
1c522f
+            Group(port_location + Optional(port_angle)) |
1c522f
+            Group(port_angle + Optional(port_location))
1c522f
+            ).setName("port")
1c522f
+
1c522f
         node_id = (ID + Optional(port))
1c522f
-        a_list = OneOrMore(ID + Optional(equals + righthand_id) +
1c522f
-            Optional(comma.suppress())).setName("a_list")
1c522f
-        
1c522f
-        attr_list = OneOrMore(lbrack.suppress() + Optional(a_list) +	
1c522f
-            rbrack.suppress()).setName("attr_list")
1c522f
-        
1c522f
+        a_list = OneOrMore(
1c522f
+            ID + Optional(equals + righthand_id) + Optional(comma.suppress())
1c522f
+            ).setName("a_list")
1c522f
+
1c522f
+        attr_list = OneOrMore(
1c522f
+            lbrack.suppress() + Optional(a_list) + rbrack.suppress()
1c522f
+            ).setName("attr_list")
1c522f
+
1c522f
         attr_stmt = (Group(graph_ | node_ | edge_) + attr_list).setName("attr_stmt")
1c522f
-        
1c522f
+
1c522f
         edgeop = (Literal("--") | Literal("->")).setName("edgeop")
1c522f
-        
1c522f
+
1c522f
         stmt_list = Forward()
1c522f
-        graph_stmt = Group(lbrace.suppress() + Optional(stmt_list) +	
1c522f
-            rbrace.suppress() + Optional(semi.suppress()) ).setName("graph_stmt")
1c522f
-            
1c522f
-            
1c522f
+        graph_stmt = Group(
1c522f
+            lbrace.suppress() + Optional(stmt_list) +
1c522f
+            rbrace.suppress() + Optional(semi.suppress())
1c522f
+            ).setName("graph_stmt")
1c522f
+
1c522f
         edge_point = Forward()
1c522f
-        
1c522f
+
1c522f
         edgeRHS = OneOrMore(edgeop + edge_point)
1c522f
         edge_stmt = edge_point + edgeRHS + Optional(attr_list)
1c522f
-        
1c522f
+
1c522f
         subgraph = Group(subgraph_ + Optional(ID) + graph_stmt).setName("subgraph")
1c522f
-        
1c522f
-        edge_point << Group( subgraph | graph_stmt | node_id ).setName('edge_point')
1c522f
-        
1c522f
-        node_stmt = (node_id + Optional(attr_list) + Optional(semi.suppress())).setName("node_stmt")
1c522f
-        
1c522f
+
1c522f
+        edge_point << Group(subgraph | graph_stmt | node_id).setName('edge_point')
1c522f
+
1c522f
+        node_stmt = (
1c522f
+            node_id + Optional(attr_list) + Optional(semi.suppress())
1c522f
+            ).setName("node_stmt")
1c522f
+
1c522f
         assignment = (ID + equals + righthand_id).setName("assignment")
1c522f
-        stmt =  (assignment | edge_stmt | attr_stmt | subgraph | graph_stmt | node_stmt).setName("stmt")
1c522f
+        stmt = (
1c522f
+            assignment | edge_stmt | attr_stmt |
1c522f
+            subgraph | graph_stmt | node_stmt
1c522f
+            ).setName("stmt")
1c522f
         stmt_list << OneOrMore(stmt + Optional(semi.suppress()))
1c522f
-        
1c522f
-        graphparser = OneOrMore( (Optional(strict_) + Group((graph_ | digraph_)) +
1c522f
-            Optional(ID) + graph_stmt).setResultsName("graph") )
1c522f
-        
1c522f
+
1c522f
+        graphparser = OneOrMore((
1c522f
+            Optional(strict_) + Group((graph_ | digraph_)) +
1c522f
+            Optional(ID) + graph_stmt
1c522f
+            ).setResultsName("graph"))
1c522f
+
1c522f
         singleLineComment = Group("//" + restOfLine) | Group("#" + restOfLine)
1c522f
-        
1c522f
-        
1c522f
+
1c522f
         # actions
1c522f
-        
1c522f
         graphparser.ignore(singleLineComment)
1c522f
         graphparser.ignore(cStyleComment)
1c522f
-        
1c522f
+
1c522f
         assignment.setParseAction(push_attr_list)
1c522f
         a_list.setParseAction(push_attr_list)
1c522f
         edge_stmt.setParseAction(push_edge_stmt)
1c522f
         node_stmt.setParseAction(push_node_stmt)
1c522f
         attr_stmt.setParseAction(push_default_stmt)
1c522f
-        
1c522f
+
1c522f
         subgraph.setParseAction(push_subgraph_stmt)
1c522f
         graph_stmt.setParseAction(push_graph_stmt)
1c522f
         graphparser.setParseAction(push_top_graph_stmt)
1c522f
-        
1c522f
-    
1c522f
+
1c522f
     return graphparser
1c522f
 
1c522f
 
1c522f
 def parse_dot_data(data):
1c522f
-
1c522f
     global top_graphs
1c522f
-    
1c522f
+
1c522f
     top_graphs = list()
1c522f
 
1c522f
-    if data.startswith(codecs.BOM_UTF8):
1c522f
-        data = data.decode( 'utf-8' )
1c522f
-        
1c522f
+    if PY3:
1c522f
+        if isinstance(data, bytes):
1c522f
+            # this is extremely hackish
1c522f
+            try:
1c522f
+                idx = data.index(b'charset') + 7
1c522f
+                while data[idx] in b' \t\n\r=':
1c522f
+                    idx += 1
1c522f
+                fst = idx
1c522f
+                while data[idx] not in b' \t\n\r];,':
1c522f
+                    idx += 1
1c522f
+                charset = data[fst:idx].strip(b'"\'').decode('ascii')
1c522f
+                data = data.decode(charset)
1c522f
+            except:
1c522f
+                data = data.decode('utf-8')
1c522f
+    else:
1c522f
+        if data.startswith(codecs.BOM_UTF8):
1c522f
+            data = data.decode('utf-8')
1c522f
+
1c522f
     try:
1c522f
-    
1c522f
+
1c522f
         graphparser = graph_definition()
1c522f
-        
1c522f
+
1c522f
         if pyparsing_version >= '1.2':
1c522f
             graphparser.parseWithTabs()
1c522f
-            
1c522f
+
1c522f
         tokens = graphparser.parseString(data)
1c522f
 
1c522f
         if len(tokens) == 1:
1c522f
             return tokens[0]
1c522f
         else:
1c522f
             return [g for g in tokens]
1c522f
-        
1c522f
-    except ParseException, err:
1c522f
-    
1c522f
-        print err.line
1c522f
-        print " "*(err.column-1) + "^"
1c522f
-        print err
1c522f
+
1c522f
+    except ParseException:
1c522f
+        err = sys.exc_info()[1]
1c522f
+        print(err.line)
1c522f
+        print(" " * (err.column - 1) + "^")
1c522f
+        print(err)
1c522f
         return None
1c522f
diff --git a/pydot.py b/pydot.py
1c522f
index e9bd2a1..c20db18 100644
1c522f
--- a/pydot.py
1c522f
+++ b/pydot.py
1c522f
@@ -17,24 +17,40 @@ Copyright (c) 2005-2011 Ero Carrera <ero.carrera@gmail.com>
1c522f
 Distributed under MIT license [http://opensource.org/licenses/mit-license.html].
1c522f
 """
1c522f
 
1c522f
-__revision__ = "$LastChangedRevision: 28 $"
1c522f
+from __future__ import division, print_function
1c522f
+
1c522f
 __author__ = 'Ero Carrera'
1c522f
-__version__ = '1.0.%d' % int( __revision__[21:-2] )
1c522f
+__version__ = '1.0.29'
1c522f
 __license__ = 'MIT'
1c522f
 
1c522f
 import os
1c522f
 import re
1c522f
 import subprocess
1c522f
+import sys
1c522f
 import tempfile
1c522f
 import copy
1c522f
+
1c522f
+from operator import itemgetter
1c522f
+
1c522f
 try:
1c522f
     import dot_parser
1c522f
-except Exception, e:
1c522f
-    print "Couldn't import dot_parser, loading of dot files will not be possible."
1c522f
-    
1c522f
+except Exception:
1c522f
+    print("Couldn't import dot_parser, loading of dot files will not be possible.")
1c522f
+
1c522f
 
1c522f
+PY3 = not sys.version_info < (3, 0, 0)
1c522f
 
1c522f
-GRAPH_ATTRIBUTES = set( ['Damping', 'K', 'URL', 'aspect', 'bb', 'bgcolor',
1c522f
+if PY3:
1c522f
+    NULL_SEP = b''
1c522f
+    basestring = str
1c522f
+    long = int
1c522f
+    unicode = str
1c522f
+else:
1c522f
+    NULL_SEP = ''
1c522f
+
1c522f
+
1c522f
+GRAPH_ATTRIBUTES = set([
1c522f
+    'Damping', 'K', 'URL', 'aspect', 'bb', 'bgcolor',
1c522f
     'center', 'charset', 'clusterrank', 'colorscheme', 'comment', 'compound',
1c522f
     'concentrate', 'defaultdist', 'dim', 'dimen', 'diredgeconstraints',
1c522f
     'dpi', 'epsilon', 'esep', 'fontcolor', 'fontname', 'fontnames',
1c522f
@@ -46,13 +62,15 @@ GRAPH_ATTRIBUTES = set( ['Damping', 'K', 'URL', 'aspect', 'bb', 'bgcolor',
1c522f
     'overlap_scaling', 'pack', 'packmode', 'pad', 'page', 'pagedir',
1c522f
     'quadtree', 'quantum', 'rankdir', 'ranksep', 'ratio', 'remincross',
1c522f
     'repulsiveforce', 'resolution', 'root', 'rotate', 'searchsize', 'sep',
1c522f
-    'showboxes', 'size', 'smoothing', 'sortv', 'splines', 'start', 
1c522f
+    'showboxes', 'size', 'smoothing', 'sortv', 'splines', 'start',
1c522f
     'stylesheet', 'target', 'truecolor', 'viewport', 'voro_margin',
1c522f
-    # for subgraphs 
1c522f
-    'rank' ] )
1c522f
+    # for subgraphs
1c522f
+    'rank'
1c522f
+   ])
1c522f
 
1c522f
 
1c522f
-EDGE_ATTRIBUTES = set( ['URL', 'arrowhead', 'arrowsize', 'arrowtail',
1c522f
+EDGE_ATTRIBUTES = set([
1c522f
+    'URL', 'arrowhead', 'arrowsize', 'arrowtail',
1c522f
     'color', 'colorscheme', 'comment', 'constraint', 'decorate', 'dir',
1c522f
     'edgeURL', 'edgehref', 'edgetarget', 'edgetooltip', 'fontcolor',
1c522f
     'fontname', 'fontsize', 'headURL', 'headclip', 'headhref', 'headlabel',
1c522f
@@ -63,10 +81,12 @@ EDGE_ATTRIBUTES = set( ['URL', 'arrowhead', 'arrowsize', 'arrowtail',
1c522f
     'nojustify', 'penwidth', 'pos', 'samehead', 'sametail', 'showboxes',
1c522f
     'style', 'tailURL', 'tailclip', 'tailhref', 'taillabel', 'tailport',
1c522f
     'tailtarget', 'tailtooltip', 'target', 'tooltip', 'weight',
1c522f
-    'rank' ] )
1c522f
+    'rank'
1c522f
+   ])
1c522f
 
1c522f
 
1c522f
-NODE_ATTRIBUTES = set( ['URL', 'color', 'colorscheme', 'comment',
1c522f
+NODE_ATTRIBUTES = set([
1c522f
+    'URL', 'color', 'colorscheme', 'comment',
1c522f
     'distortion', 'fillcolor', 'fixedsize', 'fontcolor', 'fontname',
1c522f
     'fontsize', 'group', 'height', 'id', 'image', 'imagescale', 'label',
1c522f
     'labelloc', 'layer', 'margin', 'nojustify', 'orientation', 'penwidth',
1c522f
@@ -74,14 +94,62 @@ NODE_ATTRIBUTES = set( ['URL', 'color', 'colorscheme', 'comment',
1c522f
     'shape', 'shapefile', 'showboxes', 'sides', 'skew', 'sortv', 'style',
1c522f
     'target', 'tooltip', 'vertices', 'width', 'z',
1c522f
     # The following are attributes dot2tex
1c522f
-    'texlbl',  'texmode' ] )
1c522f
+    'texlbl', 'texmode'
1c522f
+   ])
1c522f
 
1c522f
 
1c522f
-CLUSTER_ATTRIBUTES = set( ['K', 'URL', 'bgcolor', 'color', 'colorscheme',
1c522f
+CLUSTER_ATTRIBUTES = set([
1c522f
+    'K', 'URL', 'bgcolor', 'color', 'colorscheme',
1c522f
     'fillcolor', 'fontcolor', 'fontname', 'fontsize', 'label', 'labeljust',
1c522f
     'labelloc', 'lheight', 'lp', 'lwidth', 'nojustify', 'pencolor',
1c522f
-    'penwidth', 'peripheries', 'sortv', 'style', 'target', 'tooltip'] )
1c522f
-            
1c522f
+    'penwidth', 'peripheries', 'sortv', 'style', 'target', 'tooltip'
1c522f
+   ])
1c522f
+
1c522f
+
1c522f
+def is_string_like(obj): # from John Hunter, types-free version
1c522f
+    """Check if obj is string."""
1c522f
+    try:
1c522f
+        obj + ''
1c522f
+    except (TypeError, ValueError):
1c522f
+        return False
1c522f
+    return True
1c522f
+
1c522f
+def get_fobj(fname, mode='w+'):
1c522f
+    """Obtain a proper file object.
1c522f
+
1c522f
+    Parameters
1c522f
+    ----------
1c522f
+    fname : string, file object, file descriptor
1c522f
+        If a string or file descriptor, then we create a file object. If *fname*
1c522f
+        is a file object, then we do nothing and ignore the specified *mode*
1c522f
+        parameter.
1c522f
+    mode : str
1c522f
+        The mode of the file to be opened.
1c522f
+
1c522f
+    Returns
1c522f
+    -------
1c522f
+    fobj : file object
1c522f
+        The file object.
1c522f
+    close : bool
1c522f
+        If *fname* was a string, then *close* will be *True* to signify that
1c522f
+        the file object should be closed after writing to it. Otherwise, *close*
1c522f
+        will be *False* signifying that the user, in essence, created the file
1c522f
+        object already and that subsequent operations should not close it.
1c522f
+
1c522f
+    """
1c522f
+    if is_string_like(fname):
1c522f
+        fobj = open(fname, mode)
1c522f
+        close = True
1c522f
+    elif hasattr(fname, 'write'):
1c522f
+        # fname is a file-like object, perhaps a StringIO (for example)
1c522f
+        fobj = fname
1c522f
+        close = False
1c522f
+    else:
1c522f
+        # assume it is a file descriptor
1c522f
+        fobj = os.fdopen(fname, mode)
1c522f
+        close = False
1c522f
+    return fobj, close
1c522f
+
1c522f
 
1c522f
 #
1c522f
 # Extented version of ASPN's Python Cookbook Recipe:
1c522f
@@ -92,7 +160,7 @@ CLUSTER_ATTRIBUTES = set( ['K', 'URL', 'bgcolor', 'color', 'colorscheme',
1c522f
 #
1c522f
 class frozendict(dict):
1c522f
     def _blocked_attribute(obj):
1c522f
-        raise AttributeError, "A frozendict cannot be modified."
1c522f
+        raise AttributeError("A frozendict cannot be modified.")
1c522f
     _blocked_attribute = property(_blocked_attribute)
1c522f
 
1c522f
     __delitem__ = __setitem__ = clear = _blocked_attribute
1c522f
@@ -105,7 +173,7 @@ class frozendict(dict):
1c522f
         for arg in args:
1c522f
             if isinstance(arg, dict):
1c522f
                 arg = copy.copy(arg)
1c522f
-                for k, v in arg.iteritems():
1c522f
+                for k, v in arg.items():
1c522f
                     if isinstance(v, frozendict):
1c522f
                         arg[k] = v
1c522f
                     elif isinstance(v, dict):
1c522f
@@ -114,13 +182,13 @@ class frozendict(dict):
1c522f
                         v_ = list()
1c522f
                         for elm in v:
1c522f
                             if isinstance(elm, dict):
1c522f
-                                v_.append( frozendict(elm) )
1c522f
+                                v_.append(frozendict(elm))
1c522f
                             else:
1c522f
-                                v_.append( elm )
1c522f
+                                v_.append(elm)
1c522f
                         arg[k] = tuple(v_)
1c522f
-                args_.append( arg )
1c522f
+                args_.append(arg)
1c522f
             else:
1c522f
-                args_.append( arg )
1c522f
+                args_.append(arg)
1c522f
 
1c522f
         dict.__init__(new, *args_, **kw)
1c522f
         return new
1c522f
@@ -132,7 +200,7 @@ class frozendict(dict):
1c522f
         try:
1c522f
             return self._cached_hash
1c522f
         except AttributeError:
1c522f
-            h = self._cached_hash = hash(tuple(sorted(self.iteritems())))
1c522f
+            h = self._cached_hash = hash(tuple(sorted(self.items())))
1c522f
             return h
1c522f
 
1c522f
     def __repr__(self):
1c522f
@@ -142,23 +210,25 @@ class frozendict(dict):
1c522f
 dot_keywords = ['graph', 'subgraph', 'digraph', 'node', 'edge', 'strict']
1c522f
 
1c522f
 id_re_alpha_nums = re.compile('^[_a-zA-Z][a-zA-Z0-9_,]*$', re.UNICODE)
1c522f
-id_re_alpha_nums_with_ports = re.compile('^[_a-zA-Z][a-zA-Z0-9_,:\"]*[a-zA-Z0-9_,\"]+$', re.UNICODE)
1c522f
+id_re_alpha_nums_with_ports = re.compile(
1c522f
+    '^[_a-zA-Z][a-zA-Z0-9_,:\"]*[a-zA-Z0-9_,\"]+$', re.UNICODE
1c522f
+    )
1c522f
 id_re_num = re.compile('^[0-9,]+$', re.UNICODE)
1c522f
 id_re_with_port = re.compile('^([^:]*):([^:]*)$', re.UNICODE)
1c522f
-id_re_dbl_quoted = re.compile('^\".*\"$', re.S|re.UNICODE)
1c522f
-id_re_html = re.compile('^<.*>$', re.S|re.UNICODE)
1c522f
+id_re_dbl_quoted = re.compile('^\".*\"$', re.S | re.UNICODE)
1c522f
+id_re_html = re.compile('^<.*>$', re.S | re.UNICODE)
1c522f
 
1c522f
 
1c522f
-def needs_quotes( s ):
1c522f
+def needs_quotes(s):
1c522f
     """Checks whether a string is a dot language ID.
1c522f
-    
1c522f
+
1c522f
     It will check whether the string is solely composed
1c522f
     by the characters allowed in an ID or not.
1c522f
     If the string is one of the reserved keywords it will
1c522f
     need quotes too but the user will need to add them
1c522f
     manually.
1c522f
     """
1c522f
-    
1c522f
+
1c522f
     # If the name is a reserved keyword it will need quotes but pydot
1c522f
     # can't tell when it's being used as a keyword or when it's simply
1c522f
     # a name. Hence the user needs to supply the quotes when an element
1c522f
@@ -168,11 +238,14 @@ def needs_quotes( s ):
1c522f
     if s in dot_keywords:
1c522f
         return False
1c522f
 
1c522f
-    chars = [ord(c) for c in s if ord(c)>0x7f or ord(c)==0]
1c522f
+    chars = [ord(c) for c in s if ord(c) > 0x7f or ord(c) == 0]
1c522f
     if chars and not id_re_dbl_quoted.match(s) and not id_re_html.match(s):
1c522f
         return True
1c522f
-        
1c522f
-    for test_re in [id_re_alpha_nums, id_re_num, id_re_dbl_quoted, id_re_html, id_re_alpha_nums_with_ports]:
1c522f
+
1c522f
+    for test_re in [
1c522f
+            id_re_alpha_nums, id_re_num, id_re_dbl_quoted,
1c522f
+            id_re_html, id_re_alpha_nums_with_ports
1c522f
+           ]:
1c522f
         if test_re.match(s):
1c522f
             return False
1c522f
 
1c522f
@@ -184,109 +257,109 @@ def needs_quotes( s ):
1c522f
 
1c522f
 
1c522f
 def quote_if_necessary(s):
1c522f
+    # Older versions of graphviz throws a syntax error for empty values without
1c522f
+    # quotes, e.g. [label=]
1c522f
+    if s == '':
1c522f
+        return '""'
1c522f
 
1c522f
     if isinstance(s, bool):
1c522f
         if s is True:
1c522f
             return 'True'
1c522f
         return 'False'
1c522f
 
1c522f
-    if not isinstance( s, basestring ):
1c522f
+    if not isinstance(s, basestring):
1c522f
         return s
1c522f
 
1c522f
     if not s:
1c522f
         return s
1c522f
-        
1c522f
+
1c522f
     if needs_quotes(s):
1c522f
-        replace = {'"'  : r'\"',
1c522f
-                   "\n" : r'\n',
1c522f
-                   "\r" : r'\r'}
1c522f
-        for (a,b) in replace.items():
1c522f
+        replace = {'"': r'\"', "\n": r'\n', "\r": r'\r'}
1c522f
+        for (a, b) in replace.items():
1c522f
             s = s.replace(a, b)
1c522f
 
1c522f
         return '"' + s + '"'
1c522f
-     
1c522f
-    return s   
1c522f
 
1c522f
+    return s
1c522f
 
1c522f
 
1c522f
 def graph_from_dot_data(data):
1c522f
     """Load graph as defined by data in DOT format.
1c522f
-    
1c522f
+
1c522f
     The data is assumed to be in DOT format. It will
1c522f
-    be parsed and a Dot class will be returned, 
1c522f
+    be parsed and a Dot class will be returned,
1c522f
     representing the graph.
1c522f
     """
1c522f
-    
1c522f
+
1c522f
     return dot_parser.parse_dot_data(data)
1c522f
 
1c522f
 
1c522f
 def graph_from_dot_file(path):
1c522f
     """Load graph as defined by a DOT file.
1c522f
-    
1c522f
+
1c522f
     The file is assumed to be in DOT format. It will
1c522f
-    be loaded, parsed and a Dot class will be returned, 
1c522f
+    be loaded, parsed and a Dot class will be returned,
1c522f
     representing the graph.
1c522f
     """
1c522f
-    
1c522f
-    fd = file(path, 'rb')
1c522f
+
1c522f
+    fd = open(path, 'rb')
1c522f
     data = fd.read()
1c522f
     fd.close()
1c522f
-    
1c522f
-    return graph_from_dot_data(data)
1c522f
 
1c522f
+    return graph_from_dot_data(data)
1c522f
 
1c522f
 
1c522f
 def graph_from_edges(edge_list, node_prefix='', directed=False):
1c522f
     """Creates a basic graph out of an edge list.
1c522f
-    
1c522f
+
1c522f
     The edge list has to be a list of tuples representing
1c522f
     the nodes connected by the edge.
1c522f
     The values can be anything: bool, int, float, str.
1c522f
-    
1c522f
+
1c522f
     If the graph is undirected by default, it is only
1c522f
     calculated from one of the symmetric halves of the matrix.
1c522f
     """
1c522f
-    
1c522f
+
1c522f
     if directed:
1c522f
         graph = Dot(graph_type='digraph')
1c522f
-        
1c522f
+
1c522f
     else:
1c522f
         graph = Dot(graph_type='graph')
1c522f
-        
1c522f
+
1c522f
     for edge in edge_list:
1c522f
-        
1c522f
+
1c522f
         if isinstance(edge[0], str):
1c522f
             src = node_prefix + edge[0]
1c522f
         else:
1c522f
             src = node_prefix + str(edge[0])
1c522f
-            
1c522f
+
1c522f
         if isinstance(edge[1], str):
1c522f
             dst = node_prefix + edge[1]
1c522f
         else:
1c522f
             dst = node_prefix + str(edge[1])
1c522f
 
1c522f
-        e = Edge( src, dst )
1c522f
+        e = Edge(src, dst)
1c522f
         graph.add_edge(e)
1c522f
-        
1c522f
+
1c522f
     return graph
1c522f
 
1c522f
 
1c522f
-def graph_from_adjacency_matrix(matrix, node_prefix= u'', directed=False):
1c522f
+def graph_from_adjacency_matrix(matrix, node_prefix='', directed=False):
1c522f
     """Creates a basic graph out of an adjacency matrix.
1c522f
-    
1c522f
+
1c522f
     The matrix has to be a list of rows of values
1c522f
     representing an adjacency matrix.
1c522f
     The values can be anything: bool, int, float, as long
1c522f
     as they can evaluate to True or False.
1c522f
     """
1c522f
-    
1c522f
+
1c522f
     node_orig = 1
1c522f
-    
1c522f
+
1c522f
     if directed:
1c522f
         graph = Dot(graph_type='digraph')
1c522f
     else:
1c522f
         graph = Dot(graph_type='graph')
1c522f
-        
1c522f
+
1c522f
     for row in matrix:
1c522f
         if not directed:
1c522f
             skip = matrix.index(row)
1c522f
@@ -294,320 +367,297 @@ def graph_from_adjacency_matrix(matrix, node_prefix= u'', directed=False):
1c522f
         else:
1c522f
             skip = 0
1c522f
             r = row
1c522f
-        node_dest = skip+1
1c522f
-        
1c522f
+        node_dest = skip + 1
1c522f
+
1c522f
         for e in r:
1c522f
             if e:
1c522f
                 graph.add_edge(
1c522f
-                    Edge( node_prefix + node_orig, 
1c522f
-                        node_prefix + node_dest) )
1c522f
+                    Edge(
1c522f
+                        node_prefix + node_orig,
1c522f
+                        node_prefix + node_dest
1c522f
+                        )
1c522f
+                    )
1c522f
             node_dest += 1
1c522f
         node_orig += 1
1c522f
-        
1c522f
-    return graph
1c522f
 
1c522f
+    return graph
1c522f
 
1c522f
 
1c522f
 def graph_from_incidence_matrix(matrix, node_prefix='', directed=False):
1c522f
     """Creates a basic graph out of an incidence matrix.
1c522f
-    
1c522f
+
1c522f
     The matrix has to be a list of rows of values
1c522f
     representing an incidence matrix.
1c522f
     The values can be anything: bool, int, float, as long
1c522f
     as they can evaluate to True or False.
1c522f
     """
1c522f
-    
1c522f
-    node_orig = 1
1c522f
-    
1c522f
+
1c522f
     if directed:
1c522f
         graph = Dot(graph_type='digraph')
1c522f
     else:
1c522f
         graph = Dot(graph_type='graph')
1c522f
-        
1c522f
+
1c522f
     for row in matrix:
1c522f
         nodes = []
1c522f
         c = 1
1c522f
-        
1c522f
+
1c522f
         for node in row:
1c522f
             if node:
1c522f
-                nodes.append(c*node)
1c522f
+                nodes.append(c * node)
1c522f
             c += 1
1c522f
-            nodes.sort()
1c522f
-            
1c522f
+
1c522f
+        nodes.sort()
1c522f
+
1c522f
         if len(nodes) == 2:
1c522f
-            graph.add_edge(	
1c522f
-                Edge( node_prefix + abs(nodes[0]),	
1c522f
-                    node_prefix + nodes[1] ))
1c522f
+            graph.add_edge(
1c522f
+                Edge(
1c522f
+                    node_prefix + abs(nodes[0]),
1c522f
+                    node_prefix + nodes[1]
1c522f
+                    )
1c522f
+                )
1c522f
 
1c522f
     if not directed:
1c522f
         graph.set_simplify(True)
1c522f
 
1c522f
     return graph
1c522f
 
1c522f
-            
1c522f
-
1c522f
 
1c522f
 def __find_executables(path):
1c522f
     """Used by find_graphviz
1c522f
-    
1c522f
+
1c522f
     path - single directory as a string
1c522f
-    
1c522f
+
1c522f
     If any of the executables are found, it will return a dictionary
1c522f
     containing the program names as keys and their paths as values.
1c522f
-    
1c522f
+
1c522f
     Otherwise returns None
1c522f
     """
1c522f
-    
1c522f
+
1c522f
     success = False
1c522f
     progs = {'dot': '', 'twopi': '', 'neato': '', 'circo': '', 'fdp': '', 'sfdp': ''}
1c522f
-    
1c522f
+
1c522f
     was_quoted = False
1c522f
     path = path.strip()
1c522f
     if path.startswith('"') and path.endswith('"'):
1c522f
         path = path[1:-1]
1c522f
-        was_quoted =  True
1c522f
-    
1c522f
-    if os.path.isdir(path) : 
1c522f
-    
1c522f
-        for prg in progs.iterkeys():
1c522f
-    
1c522f
+        was_quoted = True
1c522f
+
1c522f
+    if os.path.isdir(path):
1c522f
+        for prg in progs.keys():
1c522f
             if progs[prg]:
1c522f
                 continue
1c522f
-               
1c522f
-            if os.path.exists( os.path.join(path, prg) ):
1c522f
-                
1c522f
+
1c522f
+            if os.path.exists(os.path.join(path, prg)):
1c522f
                 if was_quoted:
1c522f
                     progs[prg] = '"' + os.path.join(path, prg) + '"'
1c522f
                 else:
1c522f
                     progs[prg] = os.path.join(path, prg)
1c522f
-                    
1c522f
+
1c522f
                 success = True
1c522f
-               
1c522f
-            elif os.path.exists( os.path.join(path, prg + '.exe') ):
1c522f
 
1c522f
+            elif os.path.exists(os.path.join(path, prg + '.exe')):
1c522f
                 if was_quoted:
1c522f
                     progs[prg] = '"' + os.path.join(path, prg + '.exe') + '"'
1c522f
                 else:
1c522f
                     progs[prg] = os.path.join(path, prg + '.exe')
1c522f
-                    
1c522f
+
1c522f
                 success = True
1c522f
-    
1c522f
+
1c522f
     if success:
1c522f
-    
1c522f
         return progs
1c522f
-        
1c522f
     else:
1c522f
-    
1c522f
         return None
1c522f
 
1c522f
 
1c522f
-
1c522f
 # The multi-platform version of this 'find_graphviz' function was
1c522f
 # contributed by Peter Cock
1c522f
-#
1c522f
 def find_graphviz():
1c522f
     """Locate Graphviz's executables in the system.
1c522f
-    
1c522f
+
1c522f
     Tries three methods:
1c522f
-    
1c522f
+
1c522f
     First: Windows Registry (Windows only)
1c522f
     This requires Mark Hammond's pywin32 is installed.
1c522f
-    
1c522f
+
1c522f
     Secondly: Search the path
1c522f
     It will look for 'dot', 'twopi' and 'neato' in all the directories
1c522f
     specified in the PATH environment variable.
1c522f
-    
1c522f
+
1c522f
     Thirdly: Default install location (Windows only)
1c522f
     It will look for 'dot', 'twopi' and 'neato' in the default install
1c522f
     location under the "Program Files" directory.
1c522f
-    
1c522f
+
1c522f
     It will return a dictionary containing the program names as keys
1c522f
     and their paths as values.
1c522f
-    
1c522f
+
1c522f
     If this fails, it returns None.
1c522f
     """
1c522f
-    
1c522f
+
1c522f
     # Method 1 (Windows only)
1c522f
-    #
1c522f
     if os.sys.platform == 'win32':
1c522f
-        
1c522f
-        HKEY_LOCAL_MACHINE =    0x80000002
1c522f
-        KEY_QUERY_VALUE =       0x0001
1c522f
+
1c522f
+        HKEY_LOCAL_MACHINE = 0x80000002
1c522f
+        KEY_QUERY_VALUE = 0x0001
1c522f
 
1c522f
         RegOpenKeyEx = None
1c522f
         RegQueryValueEx = None
1c522f
         RegCloseKey = None
1c522f
-        
1c522f
+
1c522f
         try:
1c522f
-            import win32api, win32con
1c522f
+            import win32api
1c522f
             RegOpenKeyEx = win32api.RegOpenKeyEx
1c522f
             RegQueryValueEx = win32api.RegQueryValueEx
1c522f
             RegCloseKey = win32api.RegCloseKey
1c522f
 
1c522f
         except ImportError:
1c522f
             # Print a messaged suggesting they install these?
1c522f
-            #
1c522f
             pass
1c522f
 
1c522f
         try:
1c522f
             import ctypes
1c522f
-            
1c522f
+
1c522f
             def RegOpenKeyEx(key, subkey, opt, sam):
1c522f
                 result = ctypes.c_uint(0)
1c522f
                 ctypes.windll.advapi32.RegOpenKeyExA(key, subkey, opt, sam, ctypes.byref(result))
1c522f
                 return result.value
1c522f
-                
1c522f
-            def RegQueryValueEx( hkey, valuename ):
1c522f
+
1c522f
+            def RegQueryValueEx(hkey, valuename):
1c522f
                 data_type = ctypes.c_uint(0)
1c522f
                 data_len = ctypes.c_uint(1024)
1c522f
-                data = ctypes.create_string_buffer( 1024 )
1c522f
-                
1c522f
-                res = ctypes.windll.advapi32.RegQueryValueExA(hkey, valuename, 0, 
1c522f
-                    ctypes.byref(data_type), data, ctypes.byref(data_len))
1c522f
-                    
1c522f
+                data = ctypes.create_string_buffer(1024)
1c522f
+
1c522f
+                # this has a return value, which we should probably check
1c522f
+                ctypes.windll.advapi32.RegQueryValueExA(
1c522f
+                    hkey, valuename, 0, ctypes.byref(data_type),
1c522f
+                    data, ctypes.byref(data_len)
1c522f
+                    )
1c522f
+
1c522f
                 return data.value
1c522f
-            
1c522f
+
1c522f
             RegCloseKey = ctypes.windll.advapi32.RegCloseKey
1c522f
-            
1c522f
+
1c522f
         except ImportError:
1c522f
             # Print a messaged suggesting they install these?
1c522f
-            #
1c522f
             pass
1c522f
 
1c522f
         if RegOpenKeyEx is not None:
1c522f
-            
1c522f
             # Get the GraphViz install path from the registry
1c522f
-            #
1c522f
             hkey = None
1c522f
             potentialKeys = [
1c522f
                 "SOFTWARE\\ATT\\Graphviz",
1c522f
-                "SOFTWARE\\AT&T Research Labs\\Graphviz",
1c522f
-            ]
1c522f
+                "SOFTWARE\\AT&T Research Labs\\Graphviz"
1c522f
+                ]
1c522f
             for potentialKey in potentialKeys:
1c522f
-                
1c522f
+
1c522f
                 try:
1c522f
-                    hkey = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
1c522f
-                        potentialKey, 0, KEY_QUERY_VALUE )
1c522f
-                    
1c522f
+                    hkey = RegOpenKeyEx(
1c522f
+                        HKEY_LOCAL_MACHINE,
1c522f
+                        potentialKey, 0, KEY_QUERY_VALUE
1c522f
+                        )
1c522f
+
1c522f
                     if hkey is not None:
1c522f
-                        path = RegQueryValueEx( hkey, "InstallPath" )
1c522f
-                        RegCloseKey( hkey )
1c522f
-                        
1c522f
+                        path = RegQueryValueEx(hkey, "InstallPath")
1c522f
+                        RegCloseKey(hkey)
1c522f
+
1c522f
                         # The regitry variable might exist, left by old installations
1c522f
                         # but with no value, in those cases we keep searching...
1c522f
                         if not path:
1c522f
                             continue
1c522f
-                        
1c522f
+
1c522f
                         # Now append the "bin" subdirectory:
1c522f
-                        #
1c522f
                         path = os.path.join(path, "bin")
1c522f
                         progs = __find_executables(path)
1c522f
-                        if progs is not None :
1c522f
-                            #print "Used Windows registry"
1c522f
+                        if progs is not None:
1c522f
+                            #print("Used Windows registry")
1c522f
                             return progs
1c522f
-                
1c522f
-                except Exception, excp:
1c522f
-                    #raise excp
1c522f
+
1c522f
+                except Exception:
1c522f
+                    #raise
1c522f
                     pass
1c522f
                 else:
1c522f
                     break
1c522f
-                    
1c522f
-
1c522f
 
1c522f
     # Method 2 (Linux, Windows etc)
1c522f
-    #
1c522f
-    if os.environ.has_key('PATH'):
1c522f
-    
1c522f
+    if 'PATH' in os.environ:
1c522f
         for path in os.environ['PATH'].split(os.pathsep):
1c522f
             progs = __find_executables(path)
1c522f
-            if progs is not None :
1c522f
-                #print "Used path"
1c522f
+            if progs is not None:
1c522f
+                #print("Used path")
1c522f
                 return progs
1c522f
 
1c522f
     # Method 3 (Windows only)
1c522f
-    #
1c522f
     if os.sys.platform == 'win32':
1c522f
-    
1c522f
+
1c522f
         # Try and work out the equivalent of "C:\Program Files" on this
1c522f
         # machine (might be on drive D:, or in a different language)
1c522f
-        #
1c522f
-        
1c522f
-        if os.environ.has_key('PROGRAMFILES'):
1c522f
-        
1c522f
+        if 'PROGRAMFILES' in os.environ:
1c522f
             # Note, we could also use the win32api to get this
1c522f
             # information, but win32api may not be installed.
1c522f
-            
1c522f
             path = os.path.join(os.environ['PROGRAMFILES'], 'ATT', 'GraphViz', 'bin')
1c522f
-            
1c522f
         else:
1c522f
-        
1c522f
             #Just in case, try the default...
1c522f
             path = r"C:\Program Files\att\Graphviz\bin"
1c522f
-            
1c522f
+
1c522f
         progs = __find_executables(path)
1c522f
-        
1c522f
-        if progs is not None :
1c522f
-        
1c522f
-            #print "Used default install location"
1c522f
-            return progs
1c522f
 
1c522f
+        if progs is not None:
1c522f
+
1c522f
+            #print("Used default install location")
1c522f
+            return progs
1c522f
 
1c522f
     for path in (
1c522f
-        '/usr/bin', '/usr/local/bin',
1c522f
-        '/opt/local/bin',
1c522f
-        '/opt/bin', '/sw/bin', '/usr/share',
1c522f
-        '/Applications/Graphviz.app/Contents/MacOS/' ):
1c522f
-        
1c522f
+            '/usr/bin', '/usr/local/bin',
1c522f
+            '/opt/local/bin',
1c522f
+            '/opt/bin', '/sw/bin', '/usr/share',
1c522f
+            '/Applications/Graphviz.app/Contents/MacOS/'
1c522f
+            ):
1c522f
+
1c522f
         progs = __find_executables(path)
1c522f
-        if progs is not None :
1c522f
-            #print "Used path"
1c522f
+        if progs is not None:
1c522f
+            #print("Used path")
1c522f
             return progs
1c522f
 
1c522f
     # Failed to find GraphViz
1c522f
-    #
1c522f
     return None
1c522f
-    
1c522f
 
1c522f
-class Common:
1c522f
+
1c522f
+class Common(object):
1c522f
     """Common information to several classes.
1c522f
-    
1c522f
+
1c522f
     Should not be directly used, several classes are derived from
1c522f
     this one.
1c522f
     """
1c522f
-    
1c522f
 
1c522f
     def __getstate__(self):
1c522f
 
1c522f
         dict = copy.copy(self.obj_dict)
1c522f
-   
1c522f
+
1c522f
         return dict
1c522f
 
1c522f
-    
1c522f
     def __setstate__(self, state):
1c522f
-    
1c522f
-        self.obj_dict = state
1c522f
 
1c522f
+        self.obj_dict = state
1c522f
 
1c522f
     def __get_attribute__(self, attr):
1c522f
         """Look for default attributes for this node"""
1c522f
-        
1c522f
+
1c522f
         attr_val = self.obj_dict['attributes'].get(attr, None)
1c522f
-        
1c522f
+
1c522f
         if attr_val is None:
1c522f
             # get the defaults for nodes/edges
1c522f
-            
1c522f
+
1c522f
             default_node_name = self.obj_dict['type']
1c522f
-            
1c522f
+
1c522f
             # The defaults for graphs are set on a node named 'graph'
1c522f
             if default_node_name in ('subgraph', 'digraph', 'cluster'):
1c522f
                 default_node_name = 'graph'
1c522f
-                
1c522f
+
1c522f
             g = self.get_parent_graph()
1c522f
             if g is not None:
1c522f
-                defaults = g.get_node( default_node_name )
1c522f
+                defaults = g.get_node(default_node_name)
1c522f
             else:
1c522f
                 return None
1c522f
-            
1c522f
+
1c522f
             # Multiple defaults could be set by having repeated 'graph [...]'
1c522f
             # 'node [...]', 'edge [...]' statements. In such case, if the
1c522f
             # same attribute is set in different statements, only the first
1c522f
@@ -618,84 +668,78 @@ class Common:
1c522f
             #
1c522f
             if not isinstance(defaults, (list, tuple)):
1c522f
                 defaults = [defaults]
1c522f
-                
1c522f
+
1c522f
             for default in defaults:
1c522f
                 attr_val = default.obj_dict['attributes'].get(attr, None)
1c522f
                 if attr_val:
1c522f
                     return attr_val
1c522f
         else:
1c522f
             return attr_val
1c522f
-            
1c522f
+
1c522f
         return None
1c522f
-    
1c522f
 
1c522f
     def set_parent_graph(self, parent_graph):
1c522f
-    
1c522f
+
1c522f
         self.obj_dict['parent_graph'] = parent_graph
1c522f
-        
1c522f
 
1c522f
     def get_parent_graph(self):
1c522f
-    
1c522f
-        return self.obj_dict.get('parent_graph', None)
1c522f
 
1c522f
+        return self.obj_dict.get('parent_graph', None)
1c522f
 
1c522f
     def set(self, name, value):
1c522f
         """Set an attribute value by name.
1c522f
-        
1c522f
+
1c522f
         Given an attribute 'name' it will set its value to 'value'.
1c522f
         There's always the possibility of using the methods:
1c522f
-        
1c522f
+
1c522f
             set_'name'(value)
1c522f
-            
1c522f
+
1c522f
         which are defined for all the existing attributes.
1c522f
         """
1c522f
 
1c522f
         self.obj_dict['attributes'][name] = value
1c522f
 
1c522f
-
1c522f
     def get(self, name):
1c522f
         """Get an attribute value by name.
1c522f
-        
1c522f
+
1c522f
         Given an attribute 'name' it will get its value.
1c522f
         There's always the possibility of using the methods:
1c522f
-        
1c522f
+
1c522f
             get_'name'()
1c522f
-            
1c522f
+
1c522f
         which are defined for all the existing attributes.
1c522f
         """
1c522f
 
1c522f
         return self.obj_dict['attributes'].get(name, None)
1c522f
-        
1c522f
 
1c522f
     def get_attributes(self):
1c522f
         """"""
1c522f
-        
1c522f
+
1c522f
         return self.obj_dict['attributes']
1c522f
 
1c522f
-        
1c522f
     def set_sequence(self, seq):
1c522f
-    
1c522f
-        self.obj_dict['sequence'] = seq
1c522f
 
1c522f
+        self.obj_dict['sequence'] = seq
1c522f
 
1c522f
     def get_sequence(self):
1c522f
-    
1c522f
+
1c522f
         return self.obj_dict['sequence']
1c522f
-        
1c522f
-        
1c522f
+
1c522f
     def create_attribute_methods(self, obj_attributes):
1c522f
-    
1c522f
+
1c522f
         #for attr in self.obj_dict['attributes']:
1c522f
         for attr in obj_attributes:
1c522f
-        
1c522f
+
1c522f
             # Generate all the Setter methods.
1c522f
             #
1c522f
-            self.__setattr__( 'set_'+attr, lambda x, a=attr : self.obj_dict['attributes'].__setitem__(a, x) )
1c522f
-            
1c522f
+            self.__setattr__(
1c522f
+                'set_' + attr,
1c522f
+                lambda x, a=attr: self.obj_dict['attributes'].__setitem__(a, x)
1c522f
+                )
1c522f
+
1c522f
             # Generate all the Getter methods.
1c522f
             #
1c522f
-            self.__setattr__('get_'+attr, lambda a=attr : self.__get_attribute__(a))
1c522f
-
1c522f
+            self.__setattr__('get_' + attr, lambda a=attr: self.__get_attribute__(a))
1c522f
 
1c522f
 
1c522f
 class Error(Exception):
1c522f
@@ -703,6 +747,7 @@ class Error(Exception):
1c522f
     """
1c522f
     def __init__(self, value):
1c522f
         self.value = value
1c522f
+
1c522f
     def __str__(self):
1c522f
         return self.value
1c522f
 
1c522f
@@ -712,119 +757,108 @@ class InvocationException(Exception):
1c522f
     """
1c522f
     def __init__(self, value):
1c522f
         self.value = value
1c522f
+
1c522f
     def __str__(self):
1c522f
         return self.value
1c522f
 
1c522f
 
1c522f
-
1c522f
-class Node(object, Common):
1c522f
+class Node(Common):
1c522f
     """A graph node.
1c522f
-    
1c522f
+
1c522f
     This class represents a graph's node with all its attributes.
1c522f
-    
1c522f
+
1c522f
     node(name, attribute=value, ...)
1c522f
-    
1c522f
+
1c522f
     name: node's name
1c522f
-    
1c522f
+
1c522f
     All the attributes defined in the Graphviz dot language should
1c522f
     be supported.
1c522f
     """
1c522f
 
1c522f
-    def __init__(self, name = '', obj_dict = None, **attrs):
1c522f
-    
1c522f
+    def __init__(self, name='', obj_dict=None, **attrs):
1c522f
+
1c522f
         #
1c522f
         # Nodes will take attributes of all other types because the defaults
1c522f
         # for any GraphViz object are dealt with as if they were Node definitions
1c522f
         #
1c522f
-        
1c522f
+
1c522f
         if obj_dict is not None:
1c522f
-        
1c522f
             self.obj_dict = obj_dict
1c522f
-            
1c522f
         else:
1c522f
-        
1c522f
             self.obj_dict = dict()
1c522f
-            
1c522f
+
1c522f
             # Copy the attributes
1c522f
             #
1c522f
-            self.obj_dict[ 'attributes' ] = dict( attrs )
1c522f
-            self.obj_dict[ 'type' ] = 'node'
1c522f
-            self.obj_dict[ 'parent_graph' ] = None
1c522f
-            self.obj_dict[ 'parent_node_list' ] = None
1c522f
-            self.obj_dict[ 'sequence' ] = None
1c522f
-    
1c522f
+            self.obj_dict['attributes'] = dict(attrs)
1c522f
+            self.obj_dict['type'] = 'node'
1c522f
+            self.obj_dict['parent_graph'] = None
1c522f
+            self.obj_dict['parent_node_list'] = None
1c522f
+            self.obj_dict['sequence'] = None
1c522f
+
1c522f
             # Remove the compass point
1c522f
             #
1c522f
             port = None
1c522f
             if isinstance(name, basestring) and not name.startswith('"'):
1c522f
                 idx = name.find(':')
1c522f
-                if idx > 0 and idx+1 < len(name):
1c522f
+                if idx > 0 and idx + 1 < len(name):
1c522f
                     name, port = name[:idx], name[idx:]
1c522f
 
1c522f
             if isinstance(name, (long, int)):
1c522f
                 name = str(name)
1c522f
-            
1c522f
-            self.obj_dict['name'] = quote_if_necessary( name )
1c522f
+
1c522f
+            self.obj_dict['name'] = quote_if_necessary(name)
1c522f
             self.obj_dict['port'] = port
1c522f
-        
1c522f
+
1c522f
         self.create_attribute_methods(NODE_ATTRIBUTES)
1c522f
-        
1c522f
-    
1c522f
-    
1c522f
+
1c522f
     def set_name(self, node_name):
1c522f
         """Set the node's name."""
1c522f
-        
1c522f
+
1c522f
         self.obj_dict['name'] = node_name
1c522f
-        
1c522f
-        
1c522f
+
1c522f
     def get_name(self):
1c522f
         """Get the node's name."""
1c522f
-        
1c522f
+
1c522f
         return self.obj_dict['name']
1c522f
 
1c522f
-    
1c522f
     def get_port(self):
1c522f
         """Get the node's port."""
1c522f
-        
1c522f
-        return self.obj_dict['port']
1c522f
 
1c522f
+        return self.obj_dict['port']
1c522f
 
1c522f
     def add_style(self, style):
1c522f
-    
1c522f
+
1c522f
         styles = self.obj_dict['attributes'].get('style', None)
1c522f
         if not styles and style:
1c522f
-            styles = [ style ]
1c522f
+            styles = [style]
1c522f
         else:
1c522f
             styles = styles.split(',')
1c522f
-            styles.append( style )
1c522f
-        
1c522f
-        self.obj_dict['attributes']['style'] = ','.join( styles )
1c522f
-        
1c522f
+            styles.append(style)
1c522f
+
1c522f
+        self.obj_dict['attributes']['style'] = ','.join(styles)
1c522f
 
1c522f
     def to_string(self):
1c522f
         """Returns a string representation of the node in dot language.
1c522f
         """
1c522f
-        
1c522f
-        
1c522f
+
1c522f
         # RMF: special case defaults for node, edge and graph properties.
1c522f
         #
1c522f
         node = quote_if_necessary(self.obj_dict['name'])
1c522f
 
1c522f
         node_attr = list()
1c522f
 
1c522f
-        for attr, value in self.obj_dict['attributes'].iteritems():
1c522f
+        for attr, value in sorted(self.obj_dict['attributes'].items(), key=itemgetter(0)):
1c522f
             if value is not None:
1c522f
-                node_attr.append( '%s=%s' % (attr, quote_if_necessary(value) ) )
1c522f
+                node_attr.append('%s=%s' % (attr, quote_if_necessary(value)))
1c522f
             else:
1c522f
-                node_attr.append( attr )
1c522f
-                
1c522f
-                
1c522f
+                node_attr.append(attr)
1c522f
+
1c522f
         # No point in having nodes setting any defaults if the don't set
1c522f
         # any attributes...
1c522f
         #
1c522f
         if node in ('graph', 'node', 'edge') and len(node_attr) == 0:
1c522f
             return ''
1c522f
-            
1c522f
+
1c522f
         node_attr = ', '.join(node_attr)
1c522f
 
1c522f
         if node_attr:
1c522f
@@ -833,207 +867,191 @@ class Node(object, Common):
1c522f
         return node + ';'
1c522f
 
1c522f
 
1c522f
-
1c522f
-class Edge(object,  Common ):
1c522f
+class Edge(Common):
1c522f
     """A graph edge.
1c522f
-    
1c522f
+
1c522f
     This class represents a graph's edge with all its attributes.
1c522f
-    
1c522f
+
1c522f
     edge(src, dst, attribute=value, ...)
1c522f
-    
1c522f
+
1c522f
     src: source node's name
1c522f
     dst: destination node's name
1c522f
-    
1c522f
+
1c522f
     All the attributes defined in the Graphviz dot language should
1c522f
     be supported.
1c522f
-    
1c522f
- 	Attributes can be set through the dynamically generated methods:
1c522f
-    
1c522f
+
1c522f
+    Attributes can be set through the dynamically generated methods:
1c522f
+
1c522f
      set_[attribute name], i.e. set_label, set_fontname
1c522f
-     
1c522f
+
1c522f
     or directly by using the instance's special dictionary:
1c522f
-    
1c522f
-     Edge.obj_dict['attributes'][attribute name], i.e. 
1c522f
-     
1c522f
+
1c522f
+     Edge.obj_dict['attributes'][attribute name], i.e.
1c522f
+
1c522f
         edge_instance.obj_dict['attributes']['label']
1c522f
         edge_instance.obj_dict['attributes']['fontname']
1c522f
-        
1c522f
-    """
1c522f
-    
1c522f
 
1c522f
+    """
1c522f
 
1c522f
     def __init__(self, src='', dst='', obj_dict=None, **attrs):
1c522f
-    
1c522f
+
1c522f
         if isinstance(src, (list, tuple)) and dst == '':
1c522f
             src, dst = src
1c522f
-            
1c522f
+
1c522f
         if obj_dict is not None:
1c522f
-        
1c522f
+
1c522f
             self.obj_dict = obj_dict
1c522f
-            
1c522f
+
1c522f
         else:
1c522f
-        
1c522f
+
1c522f
             self.obj_dict = dict()
1c522f
-            
1c522f
+
1c522f
             # Copy the attributes
1c522f
             #
1c522f
-            self.obj_dict[ 'attributes' ] = dict( attrs )
1c522f
-            self.obj_dict[ 'type' ] = 'edge'
1c522f
-            self.obj_dict[ 'parent_graph' ] = None
1c522f
-            self.obj_dict[ 'parent_edge_list' ] = None
1c522f
-            self.obj_dict[ 'sequence' ] = None
1c522f
+            self.obj_dict['attributes'] = dict(attrs)
1c522f
+            self.obj_dict['type'] = 'edge'
1c522f
+            self.obj_dict['parent_graph'] = None
1c522f
+            self.obj_dict['parent_edge_list'] = None
1c522f
+            self.obj_dict['sequence'] = None
1c522f
 
1c522f
             if isinstance(src, Node):
1c522f
                 src = src.get_name()
1c522f
-                
1c522f
+
1c522f
             if isinstance(dst, Node):
1c522f
                 dst = dst.get_name()
1c522f
-    
1c522f
-            points = ( quote_if_necessary( src) , quote_if_necessary( dst) )
1c522f
-            
1c522f
+
1c522f
+            points = (quote_if_necessary(src), quote_if_necessary(dst))
1c522f
+
1c522f
             self.obj_dict['points'] = points
1c522f
-            
1c522f
-        self.create_attribute_methods(EDGE_ATTRIBUTES)
1c522f
 
1c522f
+        self.create_attribute_methods(EDGE_ATTRIBUTES)
1c522f
 
1c522f
     def get_source(self):
1c522f
         """Get the edges source node name."""
1c522f
-    
1c522f
+
1c522f
         return self.obj_dict['points'][0]
1c522f
-        
1c522f
-        
1c522f
+
1c522f
     def get_destination(self):
1c522f
         """Get the edge's destination node name."""
1c522f
-        
1c522f
+
1c522f
         return self.obj_dict['points'][1]
1c522f
-            
1c522f
-            
1c522f
+
1c522f
     def __hash__(self):
1c522f
-        
1c522f
-         return hash( hash(self.get_source()) + hash(self.get_destination()) )
1c522f
-        
1c522f
-        
1c522f
+        return hash(hash(self.get_source()) + hash(self.get_destination()))
1c522f
+
1c522f
     def __eq__(self, edge):
1c522f
         """Compare two edges.
1c522f
-        
1c522f
+
1c522f
         If the parent graph is directed, arcs linking
1c522f
         node A to B are considered equal and A->B != B->A
1c522f
-        
1c522f
+
1c522f
         If the parent graph is undirected, any edge
1c522f
         connecting two nodes is equal to any other
1c522f
         edge connecting the same nodes, A->B == B->A
1c522f
         """
1c522f
-        
1c522f
+
1c522f
         if not isinstance(edge, Edge):
1c522f
-            raise Error, "Can't compare and edge to a non-edge object."
1c522f
-            
1c522f
+            raise Error("Can't compare and edge to a non-edge object.")
1c522f
+
1c522f
         if self.get_parent_graph().get_top_graph_type() == 'graph':
1c522f
-        
1c522f
+
1c522f
             # If the graph is undirected, the edge has neither
1c522f
             # source nor destination.
1c522f
             #
1c522f
-            if	( ( self.get_source() == edge.get_source() and self.get_destination() == edge.get_destination() ) or
1c522f
-                ( edge.get_source() == self.get_destination() and edge.get_destination() == self.get_source() ) ):
1c522f
+            if ((self.get_source() == edge.get_source() and
1c522f
+                    self.get_destination() == edge.get_destination()) or
1c522f
+                (edge.get_source() == self.get_destination() and
1c522f
+                    edge.get_destination() == self.get_source())):
1c522f
                 return True
1c522f
-                
1c522f
+
1c522f
         else:
1c522f
-        
1c522f
-            if self.get_source()==edge.get_source() and self.get_destination()==edge.get_destination() :
1c522f
+            if (self.get_source() == edge.get_source() and
1c522f
+                    self.get_destination() == edge.get_destination()):
1c522f
                 return True
1c522f
-                
1c522f
+
1c522f
         return False
1c522f
 
1c522f
-        
1c522f
-    
1c522f
     def parse_node_ref(self, node_str):
1c522f
-    
1c522f
+
1c522f
         if not isinstance(node_str, str):
1c522f
             return node_str
1c522f
-    
1c522f
+
1c522f
         if node_str.startswith('"') and node_str.endswith('"'):
1c522f
-        
1c522f
             return node_str
1c522f
-        
1c522f
+
1c522f
         node_port_idx = node_str.rfind(':')
1c522f
-        
1c522f
-        if node_port_idx>0 and node_str[0]=='"' and node_str[node_port_idx-1]=='"':
1c522f
-        
1c522f
+
1c522f
+        if (node_port_idx > 0 and node_str[0] == '"' and
1c522f
+                node_str[node_port_idx - 1] == '"'):
1c522f
             return node_str
1c522f
-                
1c522f
-        if node_port_idx>0:
1c522f
-        
1c522f
+
1c522f
+        if node_port_idx > 0:
1c522f
             a = node_str[:node_port_idx]
1c522f
-            b = node_str[node_port_idx+1:]
1c522f
+            b = node_str[node_port_idx + 1:]
1c522f
 
1c522f
             node = quote_if_necessary(a)
1c522f
 
1c522f
-            node += ':'+quote_if_necessary(b)
1c522f
+            node += ':' + quote_if_necessary(b)
1c522f
 
1c522f
             return node
1c522f
-            
1c522f
+
1c522f
         return node_str
1c522f
-        
1c522f
-    
1c522f
+
1c522f
     def to_string(self):
1c522f
         """Returns a string representation of the edge in dot language.
1c522f
         """
1c522f
 
1c522f
-        src = self.parse_node_ref( self.get_source() )
1c522f
-        dst = self.parse_node_ref( self.get_destination() )
1c522f
-        
1c522f
+        src = self.parse_node_ref(self.get_source())
1c522f
+        dst = self.parse_node_ref(self.get_destination())
1c522f
+
1c522f
         if isinstance(src, frozendict):
1c522f
-            edge = [ Subgraph(obj_dict=src).to_string() ]
1c522f
+            edge = [Subgraph(obj_dict=src).to_string()]
1c522f
         elif isinstance(src, (int, long)):
1c522f
-            edge = [ str(src) ]
1c522f
+            edge = [str(src)]
1c522f
         else:
1c522f
-            edge = [ src ]
1c522f
-        
1c522f
-        if	(self.get_parent_graph() and
1c522f
-            self.get_parent_graph().get_top_graph_type() and
1c522f
-            self.get_parent_graph().get_top_graph_type() == 'digraph' ):
1c522f
-
1c522f
-            edge.append( '->' )
1c522f
-            
1c522f
+            edge = [src]
1c522f
+
1c522f
+        if (self.get_parent_graph() and
1c522f
+                self.get_parent_graph().get_top_graph_type() and
1c522f
+                self.get_parent_graph().get_top_graph_type() == 'digraph'):
1c522f
+
1c522f
+            edge.append('->')
1c522f
+
1c522f
         else:
1c522f
-            edge.append( '--' )
1c522f
-            
1c522f
+            edge.append('--')
1c522f
+
1c522f
         if isinstance(dst, frozendict):
1c522f
-            edge.append( Subgraph(obj_dict=dst).to_string() )
1c522f
+            edge.append(Subgraph(obj_dict=dst).to_string())
1c522f
         elif isinstance(dst, (int, long)):
1c522f
-            edge.append( str(dst) )
1c522f
+            edge.append(str(dst))
1c522f
         else:
1c522f
-            edge.append( dst )
1c522f
-
1c522f
+            edge.append(dst)
1c522f
 
1c522f
         edge_attr = list()
1c522f
-        
1c522f
-        for attr, value in self.obj_dict['attributes'].iteritems():
1c522f
-        
1c522f
+
1c522f
+        for attr, value in sorted(self.obj_dict['attributes'].items(), key=itemgetter(0)):
1c522f
             if value is not None:
1c522f
-                edge_attr.append( '%s=%s' % (attr, quote_if_necessary(value) ) )
1c522f
+                edge_attr.append('%s=%s' % (attr, quote_if_necessary(value)))
1c522f
             else:
1c522f
-                edge_attr.append( attr )
1c522f
+                edge_attr.append(attr)
1c522f
 
1c522f
         edge_attr = ', '.join(edge_attr)
1c522f
-        
1c522f
+
1c522f
         if edge_attr:
1c522f
-            edge.append( ' [' + edge_attr + ']' )
1c522f
+            edge.append(' [' + edge_attr + ']')
1c522f
 
1c522f
         return ' '.join(edge) + ';'
1c522f
-    
1c522f
-    
1c522f
-    
1c522f
-    
1c522f
-    
1c522f
-class Graph(object, Common):
1c522f
+
1c522f
+
1c522f
+class Graph(Common):
1c522f
     """Class representing a graph in Graphviz's dot language.
1c522f
 
1c522f
     This class implements the methods to work on a representation
1c522f
     of a graph in Graphviz's dot language.
1c522f
-    
1c522f
-    graph(  graph_name='G', graph_type='digraph',
1c522f
+
1c522f
+    graph(graph_name='G', graph_type='digraph',
1c522f
         strict=False, suppress_disconnected=False, attribute=value, ...)
1c522f
-    
1c522f
+
1c522f
     graph_name:
1c522f
         the graph's name
1c522f
     graph_type:
1c522f
@@ -1045,222 +1063,178 @@ class Graph(object, Common):
1c522f
         if True it will avoid displaying equal edges, i.e.
1c522f
         only one edge between two nodes. removing the
1c522f
         duplicated ones.
1c522f
-        
1c522f
+
1c522f
     All the attributes defined in the Graphviz dot language should
1c522f
     be supported.
1c522f
-    
1c522f
+
1c522f
     Attributes can be set through the dynamically generated methods:
1c522f
-    
1c522f
+
1c522f
      set_[attribute name], i.e. set_size, set_fontname
1c522f
-     
1c522f
+
1c522f
     or using the instance's attributes:
1c522f
-    
1c522f
-     Graph.obj_dict['attributes'][attribute name], i.e. 
1c522f
-     
1c522f
+
1c522f
+     Graph.obj_dict['attributes'][attribute name], i.e.
1c522f
+
1c522f
         graph_instance.obj_dict['attributes']['label']
1c522f
         graph_instance.obj_dict['attributes']['fontname']
1c522f
     """
1c522f
-    
1c522f
 
1c522f
-    def __init__(self, graph_name='G', obj_dict=None, graph_type='digraph', strict=False,
1c522f
-        suppress_disconnected=False, simplify=False, **attrs):
1c522f
+    def __init__(
1c522f
+            self, graph_name='G', obj_dict=None, graph_type='digraph', strict=False,
1c522f
+            suppress_disconnected=False, simplify=False, **attrs):
1c522f
 
1c522f
         if obj_dict is not None:
1c522f
             self.obj_dict = obj_dict
1c522f
-            
1c522f
         else:
1c522f
-
1c522f
             self.obj_dict = dict()
1c522f
-            
1c522f
+
1c522f
             self.obj_dict['attributes'] = dict(attrs)
1c522f
-            
1c522f
+
1c522f
             if graph_type not in ['graph', 'digraph']:
1c522f
-                raise Error, 'Invalid type "%s". Accepted graph types are: graph, digraph, subgraph' % graph_type
1c522f
-    
1c522f
-    
1c522f
+                raise Error((
1c522f
+                    'Invalid type "%s". Accepted graph types are: '
1c522f
+                    'graph, digraph, subgraph' % graph_type
1c522f
+                    ))
1c522f
+
1c522f
             self.obj_dict['name'] = quote_if_necessary(graph_name)
1c522f
             self.obj_dict['type'] = graph_type
1c522f
-            
1c522f
+
1c522f
             self.obj_dict['strict'] = strict
1c522f
             self.obj_dict['suppress_disconnected'] = suppress_disconnected
1c522f
             self.obj_dict['simplify'] = simplify
1c522f
-    
1c522f
+
1c522f
             self.obj_dict['current_child_sequence'] = 1
1c522f
             self.obj_dict['nodes'] = dict()
1c522f
             self.obj_dict['edges'] = dict()
1c522f
             self.obj_dict['subgraphs'] = dict()
1c522f
 
1c522f
             self.set_parent_graph(self)
1c522f
-            
1c522f
 
1c522f
         self.create_attribute_methods(GRAPH_ATTRIBUTES)
1c522f
 
1c522f
-
1c522f
     def get_graph_type(self):
1c522f
-    
1c522f
         return self.obj_dict['type']
1c522f
 
1c522f
-
1c522f
     def get_top_graph_type(self):
1c522f
-    
1c522f
         parent = self
1c522f
         while True:
1c522f
             parent_ = parent.get_parent_graph()
1c522f
             if parent_ == parent:
1c522f
                 break
1c522f
             parent = parent_
1c522f
-    
1c522f
+
1c522f
         return parent.obj_dict['type']
1c522f
-                
1c522f
 
1c522f
     def set_graph_defaults(self, **attrs):
1c522f
-
1c522f
-        self.add_node( Node('graph', **attrs) )
1c522f
-
1c522f
+        self.add_node(Node('graph', **attrs))
1c522f
 
1c522f
     def get_graph_defaults(self, **attrs):
1c522f
-    
1c522f
+
1c522f
         graph_nodes = self.get_node('graph')
1c522f
-        
1c522f
-        if isinstance( graph_nodes, (list, tuple)):
1c522f
-            return [ node.get_attributes() for node in graph_nodes ]
1c522f
-            
1c522f
-        return graph_nodes.get_attributes()            
1c522f
-            
1c522f
-        
1c522f
 
1c522f
-    def set_node_defaults(self, **attrs):
1c522f
+        if isinstance(graph_nodes, (list, tuple)):
1c522f
+            return [node.get_attributes() for node in graph_nodes]
1c522f
 
1c522f
-        self.add_node( Node('node', **attrs) )
1c522f
+        return graph_nodes.get_attributes()
1c522f
 
1c522f
+    def set_node_defaults(self, **attrs):
1c522f
+        self.add_node(Node('node', **attrs))
1c522f
 
1c522f
     def get_node_defaults(self, **attrs):
1c522f
-    
1c522f
-    
1c522f
         graph_nodes = self.get_node('node')
1c522f
 
1c522f
-        if isinstance( graph_nodes, (list, tuple)):
1c522f
-            return [ node.get_attributes() for node in graph_nodes ]
1c522f
-            
1c522f
-        return graph_nodes.get_attributes()            
1c522f
-        
1c522f
-
1c522f
-    def set_edge_defaults(self, **attrs):
1c522f
-
1c522f
-        self.add_node( Node('edge', **attrs) )
1c522f
+        if isinstance(graph_nodes, (list, tuple)):
1c522f
+            return [node.get_attributes() for node in graph_nodes]
1c522f
 
1c522f
+        return graph_nodes.get_attributes()
1c522f
 
1c522f
+    def set_edge_defaults(self, **attrs):
1c522f
+        self.add_node(Node('edge', **attrs))
1c522f
 
1c522f
     def get_edge_defaults(self, **attrs):
1c522f
-    
1c522f
         graph_nodes = self.get_node('edge')
1c522f
 
1c522f
-        if isinstance( graph_nodes, (list, tuple)):
1c522f
-            return [ node.get_attributes() for node in graph_nodes ]
1c522f
-            
1c522f
-        return graph_nodes.get_attributes()            
1c522f
+        if isinstance(graph_nodes, (list, tuple)):
1c522f
+            return [node.get_attributes() for node in graph_nodes]
1c522f
 
1c522f
-    
1c522f
+        return graph_nodes.get_attributes()
1c522f
 
1c522f
     def set_simplify(self, simplify):
1c522f
         """Set whether to simplify or not.
1c522f
-        
1c522f
+
1c522f
         If True it will avoid displaying equal edges, i.e.
1c522f
         only one edge between two nodes. removing the
1c522f
         duplicated ones.
1c522f
         """
1c522f
-        
1c522f
-        self.obj_dict['simplify'] = simplify
1c522f
-
1c522f
 
1c522f
+        self.obj_dict['simplify'] = simplify
1c522f
 
1c522f
     def get_simplify(self):
1c522f
         """Get whether to simplify or not.
1c522f
-        
1c522f
+
1c522f
         Refer to set_simplify for more information.
1c522f
         """
1c522f
-        
1c522f
+
1c522f
         return self.obj_dict['simplify']
1c522f
 
1c522f
-            
1c522f
     def set_type(self, graph_type):
1c522f
         """Set the graph's type, 'graph' or 'digraph'."""
1c522f
 
1c522f
         self.obj_dict['type'] = graph_type
1c522f
 
1c522f
-
1c522f
-        
1c522f
     def get_type(self):
1c522f
         """Get the graph's type, 'graph' or 'digraph'."""
1c522f
 
1c522f
         return self.obj_dict['type']
1c522f
 
1c522f
-
1c522f
-
1c522f
     def set_name(self, graph_name):
1c522f
         """Set the graph's name."""
1c522f
-        
1c522f
-        self.obj_dict['name'] = graph_name
1c522f
-
1c522f
 
1c522f
+        self.obj_dict['name'] = graph_name
1c522f
 
1c522f
     def get_name(self):
1c522f
         """Get the graph's name."""
1c522f
-        
1c522f
-        return self.obj_dict['name']
1c522f
 
1c522f
+        return self.obj_dict['name']
1c522f
 
1c522f
-                    
1c522f
     def set_strict(self, val):
1c522f
         """Set graph to 'strict' mode.
1c522f
-        
1c522f
+
1c522f
         This option is only valid for top level graphs.
1c522f
         """
1c522f
-        
1c522f
-        self.obj_dict['strict'] = val
1c522f
-
1c522f
 
1c522f
+        self.obj_dict['strict'] = val
1c522f
 
1c522f
     def get_strict(self, val):
1c522f
         """Get graph's 'strict' mode (True, False).
1c522f
-        
1c522f
+
1c522f
         This option is only valid for top level graphs.
1c522f
         """
1c522f
-        
1c522f
-        return self.obj_dict['strict']
1c522f
-        
1c522f
 
1c522f
+        return self.obj_dict['strict']
1c522f
 
1c522f
     def set_suppress_disconnected(self, val):
1c522f
         """Suppress disconnected nodes in the output graph.
1c522f
-        
1c522f
+
1c522f
         This option will skip nodes in the graph with no incoming or outgoing
1c522f
         edges. This option works also for subgraphs and has effect only in the
1c522f
         current graph/subgraph.
1c522f
         """
1c522f
-        
1c522f
-        self.obj_dict['suppress_disconnected'] = val
1c522f
-            
1c522f
 
1c522f
+        self.obj_dict['suppress_disconnected'] = val
1c522f
 
1c522f
     def get_suppress_disconnected(self, val):
1c522f
         """Get if suppress disconnected is set.
1c522f
-        
1c522f
+
1c522f
         Refer to set_suppress_disconnected for more information.
1c522f
         """
1c522f
-        
1c522f
+
1c522f
         return self.obj_dict['suppress_disconnected']
1c522f
-            
1c522f
 
1c522f
     def get_next_sequence_number(self):
1c522f
-    
1c522f
         seq = self.obj_dict['current_child_sequence']
1c522f
-        
1c522f
         self.obj_dict['current_child_sequence'] += 1
1c522f
-        
1c522f
         return seq
1c522f
-        
1c522f
-
1c522f
 
1c522f
     def add_node(self, graph_node):
1c522f
         """Adds a node object to the graph.
1c522f
@@ -1268,106 +1242,101 @@ class Graph(object, Common):
1c522f
         It takes a node object as its only argument and returns
1c522f
         None.
1c522f
         """
1c522f
-        
1c522f
+
1c522f
         if not isinstance(graph_node, Node):
1c522f
             raise TypeError('add_node() received a non node class object: ' + str(graph_node))
1c522f
 
1c522f
-            
1c522f
         node = self.get_node(graph_node.get_name())
1c522f
-        
1c522f
+
1c522f
         if not node:
1c522f
+            self.obj_dict['nodes'][graph_node.get_name()] = [graph_node.obj_dict]
1c522f
 
1c522f
-            self.obj_dict['nodes'][graph_node.get_name()] = [ graph_node.obj_dict ]
1c522f
-            
1c522f
             #self.node_dict[graph_node.get_name()] = graph_node.attributes
1c522f
             graph_node.set_parent_graph(self.get_parent_graph())
1c522f
-                
1c522f
         else:
1c522f
-        
1c522f
-            self.obj_dict['nodes'][graph_node.get_name()].append( graph_node.obj_dict )
1c522f
+            self.obj_dict['nodes'][graph_node.get_name()].append(graph_node.obj_dict)
1c522f
 
1c522f
         graph_node.set_sequence(self.get_next_sequence_number())
1c522f
 
1c522f
-
1c522f
-
1c522f
     def del_node(self, name, index=None):
1c522f
         """Delete a node from the graph.
1c522f
-        
1c522f
+
1c522f
         Given a node's name all node(s) with that same name
1c522f
         will be deleted if 'index' is not specified or set
1c522f
         to None.
1c522f
         If there are several nodes with that same name and
1c522f
         'index' is given, only the node in that position
1c522f
         will be deleted.
1c522f
-        
1c522f
-        'index' should be an integer specifying the position 
1c522f
+
1c522f
+        'index' should be an integer specifying the position
1c522f
         of the node to delete. If index is larger than the
1c522f
         number of nodes with that name, no action is taken.
1c522f
-        
1c522f
+
1c522f
         If nodes are deleted it returns True. If no action
1c522f
         is taken it returns False.
1c522f
         """
1c522f
-    
1c522f
+
1c522f
         if isinstance(name, Node):
1c522f
             name = name.get_name()
1c522f
-        
1c522f
-        if self.obj_dict['nodes'].has_key(name):
1c522f
-        
1c522f
+
1c522f
+        if name in self.obj_dict['nodes']:
1c522f
             if index is not None and index < len(self.obj_dict['nodes'][name]):
1c522f
                 del self.obj_dict['nodes'][name][index]
1c522f
                 return True
1c522f
             else:
1c522f
                 del self.obj_dict['nodes'][name]
1c522f
                 return True
1c522f
-        
1c522f
+
1c522f
         return False
1c522f
-                        
1c522f
 
1c522f
     def get_node(self, name):
1c522f
         """Retrieve a node from the graph.
1c522f
-        
1c522f
+
1c522f
         Given a node's name the corresponding Node
1c522f
         instance will be returned.
1c522f
-        
1c522f
+
1c522f
         If one or more nodes exist with that name a list of
1c522f
         Node instances is returned.
1c522f
         An empty list is returned otherwise.
1c522f
         """
1c522f
-        
1c522f
+
1c522f
         match = list()
1c522f
-        
1c522f
-        if self.obj_dict['nodes'].has_key(name):
1c522f
-        
1c522f
-            match.extend( [ Node( obj_dict = obj_dict ) for obj_dict in self.obj_dict['nodes'][name] ])
1c522f
-        
1c522f
-        return match
1c522f
 
1c522f
+        if name in self.obj_dict['nodes']:
1c522f
+            match.extend([
1c522f
+                Node(obj_dict=obj_dict)
1c522f
+                for obj_dict
1c522f
+                in self.obj_dict['nodes'][name]
1c522f
+                ])
1c522f
+
1c522f
+        return match
1c522f
 
1c522f
     def get_nodes(self):
1c522f
         """Get the list of Node instances."""
1c522f
-        
1c522f
+
1c522f
         return self.get_node_list()
1c522f
-        
1c522f
-        
1c522f
+
1c522f
     def get_node_list(self):
1c522f
         """Get the list of Node instances.
1c522f
-        
1c522f
+
1c522f
         This method returns the list of Node instances
1c522f
         composing the graph.
1c522f
         """
1c522f
-        
1c522f
+
1c522f
         node_objs = list()
1c522f
-        
1c522f
-        for node, obj_dict_list in self.obj_dict['nodes'].iteritems():
1c522f
-                node_objs.extend( [ Node( obj_dict = obj_d ) for obj_d in obj_dict_list ] )
1c522f
-        
1c522f
-        return node_objs
1c522f
 
1c522f
+        for node, obj_dict_list in self.obj_dict['nodes'].items():
1c522f
+            node_objs.extend([
1c522f
+                Node(obj_dict=obj_d)
1c522f
+                for obj_d
1c522f
+                in obj_dict_list
1c522f
+                ])
1c522f
 
1c522f
+        return node_objs
1c522f
 
1c522f
     def add_edge(self, graph_edge):
1c522f
         """Adds an edge object to the graph.
1c522f
-        
1c522f
+
1c522f
         It takes a edge object as its only argument and returns
1c522f
         None.
1c522f
         """
1c522f
@@ -1375,78 +1344,70 @@ class Graph(object, Common):
1c522f
         if not isinstance(graph_edge, Edge):
1c522f
             raise TypeError('add_edge() received a non edge class object: ' + str(graph_edge))
1c522f
 
1c522f
-        edge_points = ( graph_edge.get_source(), graph_edge.get_destination() )
1c522f
+        edge_points = (graph_edge.get_source(), graph_edge.get_destination())
1c522f
 
1c522f
-        if self.obj_dict['edges'].has_key(edge_points):
1c522f
+        if edge_points in self.obj_dict['edges']:
1c522f
 
1c522f
             edge_list = self.obj_dict['edges'][edge_points]
1c522f
             edge_list.append(graph_edge.obj_dict)
1c522f
-
1c522f
         else:
1c522f
+            self.obj_dict['edges'][edge_points] = [graph_edge.obj_dict]
1c522f
 
1c522f
-            self.obj_dict['edges'][edge_points] = [ graph_edge.obj_dict ]
1c522f
-
1c522f
-
1c522f
-        graph_edge.set_sequence( self.get_next_sequence_number() )
1c522f
-
1c522f
-        graph_edge.set_parent_graph( self.get_parent_graph() )
1c522f
-
1c522f
-
1c522f
+        graph_edge.set_sequence(self.get_next_sequence_number())
1c522f
+        graph_edge.set_parent_graph(self.get_parent_graph())
1c522f
 
1c522f
     def del_edge(self, src_or_list, dst=None, index=None):
1c522f
         """Delete an edge from the graph.
1c522f
-        
1c522f
+
1c522f
         Given an edge's (source, destination) node names all
1c522f
         matching edges(s) will be deleted if 'index' is not
1c522f
         specified or set to None.
1c522f
         If there are several matching edges and 'index' is
1c522f
         given, only the edge in that position will be deleted.
1c522f
-        
1c522f
-        'index' should be an integer specifying the position 
1c522f
+
1c522f
+        'index' should be an integer specifying the position
1c522f
         of the edge to delete. If index is larger than the
1c522f
         number of matching edges, no action is taken.
1c522f
-        
1c522f
+
1c522f
         If edges are deleted it returns True. If no action
1c522f
         is taken it returns False.
1c522f
         """
1c522f
 
1c522f
-        if isinstance( src_or_list, (list, tuple)):
1c522f
+        if isinstance(src_or_list, (list, tuple)):
1c522f
             if dst is not None and isinstance(dst, (int, long)):
1c522f
                 index = dst
1c522f
             src, dst = src_or_list
1c522f
         else:
1c522f
             src, dst = src_or_list, dst
1c522f
-    
1c522f
+
1c522f
         if isinstance(src, Node):
1c522f
             src = src.get_name()
1c522f
 
1c522f
         if isinstance(dst, Node):
1c522f
             dst = dst.get_name()
1c522f
-        
1c522f
-        if self.obj_dict['edges'].has_key( (src, dst) ):
1c522f
-        
1c522f
+
1c522f
+        if (src, dst) in self.obj_dict['edges']:
1c522f
             if index is not None and index < len(self.obj_dict['edges'][(src, dst)]):
1c522f
                 del self.obj_dict['edges'][(src, dst)][index]
1c522f
                 return True
1c522f
             else:
1c522f
                 del self.obj_dict['edges'][(src, dst)]
1c522f
                 return True
1c522f
-        
1c522f
+
1c522f
         return False
1c522f
-        
1c522f
 
1c522f
     def get_edge(self, src_or_list, dst=None):
1c522f
         """Retrieved an edge from the graph.
1c522f
-        
1c522f
+
1c522f
         Given an edge's source and destination the corresponding
1c522f
         Edge instance(s) will be returned.
1c522f
-        
1c522f
+
1c522f
         If one or more edges exist with that source and destination
1c522f
         a list of Edge instances is returned.
1c522f
         An empty list is returned otherwise.
1c522f
         """
1c522f
 
1c522f
-        if isinstance( src_or_list, (list, tuple)) and dst is None:
1c522f
+        if isinstance(src_or_list, (list, tuple)) and dst is None:
1c522f
             edge_points = tuple(src_or_list)
1c522f
             edge_points_reverse = (edge_points[1], edge_points[0])
1c522f
         else:
1c522f
@@ -1454,224 +1415,205 @@ class Graph(object, Common):
1c522f
             edge_points_reverse = (dst, src_or_list)
1c522f
 
1c522f
         match = list()
1c522f
-        
1c522f
-        if self.obj_dict['edges'].has_key( edge_points ) or (
1c522f
-            self.get_top_graph_type() == 'graph' and self.obj_dict['edges'].has_key( edge_points_reverse )):
1c522f
-        
1c522f
+
1c522f
+        if edge_points in self.obj_dict['edges'] or (
1c522f
+                self.get_top_graph_type() == 'graph' and
1c522f
+                edge_points_reverse in self.obj_dict['edges']
1c522f
+                ):
1c522f
+
1c522f
             edges_obj_dict = self.obj_dict['edges'].get(
1c522f
                 edge_points,
1c522f
-                self.obj_dict['edges'].get( edge_points_reverse, None ))
1c522f
-        
1c522f
+                self.obj_dict['edges'].get(edge_points_reverse, None))
1c522f
+
1c522f
             for edge_obj_dict in edges_obj_dict:
1c522f
-                match.append( Edge( edge_points[0], edge_points[1], obj_dict = edge_obj_dict ) )
1c522f
+                match.append(
1c522f
+                    Edge(edge_points[0], edge_points[1], obj_dict=edge_obj_dict)
1c522f
+                    )
1c522f
 
1c522f
         return match
1c522f
 
1c522f
-
1c522f
     def get_edges(self):
1c522f
         return self.get_edge_list()
1c522f
-        
1c522f
-        
1c522f
+
1c522f
     def get_edge_list(self):
1c522f
         """Get the list of Edge instances.
1c522f
-        
1c522f
+
1c522f
         This method returns the list of Edge instances
1c522f
         composing the graph.
1c522f
         """
1c522f
-        
1c522f
+
1c522f
         edge_objs = list()
1c522f
-        
1c522f
-        for edge, obj_dict_list in self.obj_dict['edges'].iteritems():
1c522f
-                edge_objs.extend( [ Edge( obj_dict = obj_d ) for obj_d in obj_dict_list ] )
1c522f
-        
1c522f
-        return edge_objs
1c522f
 
1c522f
+        for edge, obj_dict_list in self.obj_dict['edges'].items():
1c522f
+            edge_objs.extend([
1c522f
+                Edge(obj_dict=obj_d)
1c522f
+                for obj_d
1c522f
+                in obj_dict_list
1c522f
+                ])
1c522f
+
1c522f
+        return edge_objs
1c522f
 
1c522f
-            
1c522f
     def add_subgraph(self, sgraph):
1c522f
         """Adds an subgraph object to the graph.
1c522f
-        
1c522f
+
1c522f
         It takes a subgraph object as its only argument and returns
1c522f
         None.
1c522f
         """
1c522f
 
1c522f
         if not isinstance(sgraph, Subgraph) and not isinstance(sgraph, Cluster):
1c522f
             raise TypeError('add_subgraph() received a non subgraph class object:' + str(sgraph))
1c522f
-            
1c522f
-        if self.obj_dict['subgraphs'].has_key(sgraph.get_name()):
1c522f
-        
1c522f
-            sgraph_list = self.obj_dict['subgraphs'][ sgraph.get_name() ]
1c522f
-            sgraph_list.append( sgraph.obj_dict )
1c522f
-                
1c522f
+
1c522f
+        if sgraph.get_name() in self.obj_dict['subgraphs']:
1c522f
+
1c522f
+            sgraph_list = self.obj_dict['subgraphs'][sgraph.get_name()]
1c522f
+            sgraph_list.append(sgraph.obj_dict)
1c522f
+
1c522f
         else:
1c522f
-            self.obj_dict['subgraphs'][ sgraph.get_name() ] = [ sgraph.obj_dict ]
1c522f
-         
1c522f
-        sgraph.set_sequence( self.get_next_sequence_number() )
1c522f
-        
1c522f
-        sgraph.set_parent_graph( self.get_parent_graph() )
1c522f
+            self.obj_dict['subgraphs'][sgraph.get_name()] = [sgraph.obj_dict]
1c522f
 
1c522f
+        sgraph.set_sequence(self.get_next_sequence_number())
1c522f
 
1c522f
+        sgraph.set_parent_graph(self.get_parent_graph())
1c522f
 
1c522f
-    
1c522f
     def get_subgraph(self, name):
1c522f
         """Retrieved a subgraph from the graph.
1c522f
-        
1c522f
+
1c522f
         Given a subgraph's name the corresponding
1c522f
         Subgraph instance will be returned.
1c522f
-        
1c522f
+
1c522f
         If one or more subgraphs exist with the same name, a list of
1c522f
         Subgraph instances is returned.
1c522f
         An empty list is returned otherwise.
1c522f
         """
1c522f
-        
1c522f
+
1c522f
         match = list()
1c522f
-        
1c522f
-        if self.obj_dict['subgraphs'].has_key( name ):
1c522f
-        
1c522f
-            sgraphs_obj_dict = self.obj_dict['subgraphs'].get( name )
1c522f
-        
1c522f
+
1c522f
+        if name in self.obj_dict['subgraphs']:
1c522f
+            sgraphs_obj_dict = self.obj_dict['subgraphs'].get(name)
1c522f
+
1c522f
             for obj_dict_list in sgraphs_obj_dict:
1c522f
-                #match.extend( Subgraph( obj_dict = obj_d ) for obj_d in obj_dict_list )
1c522f
-                match.append( Subgraph( obj_dict = obj_dict_list ) )
1c522f
-        
1c522f
-        return match
1c522f
+                #match.extend(Subgraph(obj_dict = obj_d) for obj_d in obj_dict_list)
1c522f
+                match.append(Subgraph(obj_dict=obj_dict_list))
1c522f
 
1c522f
+        return match
1c522f
 
1c522f
     def get_subgraphs(self):
1c522f
-    
1c522f
         return self.get_subgraph_list()
1c522f
-        
1c522f
-        
1c522f
+
1c522f
     def get_subgraph_list(self):
1c522f
         """Get the list of Subgraph instances.
1c522f
-        
1c522f
+
1c522f
         This method returns the list of Subgraph instances
1c522f
         in the graph.
1c522f
         """
1c522f
-        
1c522f
+
1c522f
         sgraph_objs = list()
1c522f
-        
1c522f
-        for sgraph, obj_dict_list in self.obj_dict['subgraphs'].iteritems():
1c522f
-                sgraph_objs.extend( [ Subgraph( obj_dict = obj_d ) for obj_d in obj_dict_list ] )
1c522f
-        
1c522f
-        return sgraph_objs
1c522f
-            
1c522f
 
1c522f
+        for sgraph, obj_dict_list in self.obj_dict['subgraphs'].items():
1c522f
+            sgraph_objs.extend([
1c522f
+                Subgraph(obj_dict=obj_d)
1c522f
+                for obj_d
1c522f
+                in obj_dict_list
1c522f
+                ])
1c522f
+
1c522f
+        return sgraph_objs
1c522f
 
1c522f
     def set_parent_graph(self, parent_graph):
1c522f
-    
1c522f
+
1c522f
         self.obj_dict['parent_graph'] = parent_graph
1c522f
-        
1c522f
-        for obj_list in self.obj_dict['nodes'].itervalues():
1c522f
+
1c522f
+        for obj_list in self.obj_dict['nodes'].values():
1c522f
             for obj in obj_list:
1c522f
                 obj['parent_graph'] = parent_graph
1c522f
 
1c522f
-        for obj_list in self.obj_dict['edges'].itervalues():
1c522f
+        for obj_list in self.obj_dict['edges'].values():
1c522f
             for obj in obj_list:
1c522f
                 obj['parent_graph'] = parent_graph
1c522f
 
1c522f
-        for obj_list in self.obj_dict['subgraphs'].itervalues():
1c522f
+        for obj_list in self.obj_dict['subgraphs'].values():
1c522f
             for obj in obj_list:
1c522f
                 Graph(obj_dict=obj).set_parent_graph(parent_graph)
1c522f
 
1c522f
-
1c522f
-
1c522f
     def to_string(self):
1c522f
         """Returns a string representation of the graph in dot language.
1c522f
-        
1c522f
+
1c522f
         It will return the graph and all its subelements in string from.
1c522f
         """
1c522f
-        
1c522f
-        
1c522f
+
1c522f
         graph = list()
1c522f
-        
1c522f
+
1c522f
         if self.obj_dict.get('strict', None) is not None:
1c522f
-        
1c522f
-            if self==self.get_parent_graph() and self.obj_dict['strict']:
1c522f
-            
1c522f
+            if self == self.get_parent_graph() and self.obj_dict['strict']:
1c522f
                 graph.append('strict ')
1c522f
 
1c522f
         if self.obj_dict['name'] == '':
1c522f
             if 'show_keyword' in self.obj_dict and self.obj_dict['show_keyword']:
1c522f
-                graph.append( 'subgraph {\n' )
1c522f
+                graph.append('subgraph {\n')
1c522f
             else:
1c522f
-                graph.append( '{\n' )
1c522f
+                graph.append('{\n')
1c522f
         else:
1c522f
-            graph.append( '%s %s {\n' % (self.obj_dict['type'], self.obj_dict['name']) )
1c522f
-
1c522f
+            graph.append('%s %s {\n' % (self.obj_dict['type'], self.obj_dict['name']))
1c522f
 
1c522f
-        for attr in self.obj_dict['attributes'].iterkeys():
1c522f
-        
1c522f
-            if self.obj_dict['attributes'].get(attr, None) is not None:
1c522f
-       
1c522f
-                val = self.obj_dict['attributes'].get(attr)
1c522f
-                if val is not None:
1c522f
-                    graph.append( '%s=%s' % (attr, quote_if_necessary(val)) )
1c522f
-                else:
1c522f
-                    graph.append( attr )
1c522f
-                    
1c522f
-                graph.append( ';\n' )
1c522f
+        for attr, value in sorted(self.obj_dict['attributes'].items(), key=itemgetter(0)):
1c522f
+            if value is not None:
1c522f
+                graph.append('%s=%s' % (attr, quote_if_necessary(value)))
1c522f
+            else:
1c522f
+                graph.append(attr)
1c522f
 
1c522f
+            graph.append(';\n')
1c522f
 
1c522f
         edges_done = set()
1c522f
-        
1c522f
+
1c522f
         edge_obj_dicts = list()
1c522f
-        for e in self.obj_dict['edges'].itervalues():
1c522f
+        for e in self.obj_dict['edges'].values():
1c522f
             edge_obj_dicts.extend(e)
1c522f
-            
1c522f
+
1c522f
         if edge_obj_dicts:
1c522f
-            edge_src_set, edge_dst_set = zip( *[obj['points'] for obj in edge_obj_dicts] )
1c522f
+            edge_src_set, edge_dst_set = list(zip(*[obj['points'] for obj in edge_obj_dicts]))
1c522f
             edge_src_set, edge_dst_set = set(edge_src_set), set(edge_dst_set)
1c522f
         else:
1c522f
             edge_src_set, edge_dst_set = set(), set()
1c522f
-            
1c522f
+
1c522f
         node_obj_dicts = list()
1c522f
-        for e in self.obj_dict['nodes'].itervalues():
1c522f
+        for e in self.obj_dict['nodes'].values():
1c522f
             node_obj_dicts.extend(e)
1c522f
 
1c522f
         sgraph_obj_dicts = list()
1c522f
-        for sg in self.obj_dict['subgraphs'].itervalues():
1c522f
+        for sg in self.obj_dict['subgraphs'].values():
1c522f
             sgraph_obj_dicts.extend(sg)
1c522f
 
1c522f
-        
1c522f
-        obj_list = [ (obj['sequence'], obj) for obj in (edge_obj_dicts + node_obj_dicts + sgraph_obj_dicts) ]
1c522f
-        obj_list.sort()
1c522f
-        
1c522f
+        obj_list = sorted([
1c522f
+            (obj['sequence'], obj)
1c522f
+            for obj
1c522f
+            in (edge_obj_dicts + node_obj_dicts + sgraph_obj_dicts)
1c522f
+            ])
1c522f
+
1c522f
         for idx, obj in obj_list:
1c522f
-        
1c522f
             if obj['type'] == 'node':
1c522f
-
1c522f
                 node = Node(obj_dict=obj)
1c522f
-            
1c522f
+
1c522f
                 if self.obj_dict.get('suppress_disconnected', False):
1c522f
-                
1c522f
                     if (node.get_name() not in edge_src_set and
1c522f
-                        node.get_name() not in edge_dst_set):
1c522f
-                        
1c522f
+                            node.get_name() not in edge_dst_set):
1c522f
                         continue
1c522f
-                        
1c522f
-                graph.append( node.to_string()+'\n' )
1c522f
 
1c522f
-            elif obj['type'] == 'edge':
1c522f
+                graph.append(node.to_string() + '\n')
1c522f
 
1c522f
+            elif obj['type'] == 'edge':
1c522f
                 edge = Edge(obj_dict=obj)
1c522f
-                
1c522f
+
1c522f
                 if self.obj_dict.get('simplify', False) and edge in edges_done:
1c522f
                     continue
1c522f
-                
1c522f
-                graph.append( edge.to_string() + '\n' )
1c522f
+
1c522f
+                graph.append(edge.to_string() + '\n')
1c522f
                 edges_done.add(edge)
1c522f
-                
1c522f
             else:
1c522f
-            
1c522f
                 sgraph = Subgraph(obj_dict=obj)
1c522f
-                
1c522f
-                graph.append( sgraph.to_string()+'\n' )
1c522f
+                graph.append(sgraph.to_string() + '\n')
1c522f
 
1c522f
-        graph.append( '}\n' )
1c522f
-        
1c522f
-        return ''.join(graph)
1c522f
+        graph.append('}\n')
1c522f
 
1c522f
+        return ''.join(graph)
1c522f
 
1c522f
 
1c522f
 class Subgraph(Graph):
1c522f
@@ -1680,9 +1622,9 @@ class Subgraph(Graph):
1c522f
 
1c522f
     This class implements the methods to work on a representation
1c522f
     of a subgraph in Graphviz's dot language.
1c522f
-    
1c522f
+
1c522f
     subgraph(graph_name='subG', suppress_disconnected=False, attribute=value, ...)
1c522f
-    
1c522f
+
1c522f
     graph_name:
1c522f
         the subgraph's name
1c522f
     suppress_disconnected:
1c522f
@@ -1690,46 +1632,43 @@ class Subgraph(Graph):
1c522f
         subgraph any disconnected nodes.
1c522f
     All the attributes defined in the Graphviz dot language should
1c522f
     be supported.
1c522f
-    
1c522f
+
1c522f
     Attributes can be set through the dynamically generated methods:
1c522f
-    
1c522f
+
1c522f
      set_[attribute name], i.e. set_size, set_fontname
1c522f
-     
1c522f
+
1c522f
     or using the instance's attributes:
1c522f
-    
1c522f
-     Subgraph.obj_dict['attributes'][attribute name], i.e. 
1c522f
-     
1c522f
+
1c522f
+     Subgraph.obj_dict['attributes'][attribute name], i.e.
1c522f
+
1c522f
         subgraph_instance.obj_dict['attributes']['label']
1c522f
         subgraph_instance.obj_dict['attributes']['fontname']
1c522f
     """
1c522f
-    
1c522f
-    
1c522f
+
1c522f
     # RMF: subgraph should have all the attributes of graph so it can be passed
1c522f
     # as a graph to all methods
1c522f
     #
1c522f
-    def __init__(self, graph_name='', obj_dict=None, suppress_disconnected=False,
1c522f
-        simplify=False, **attrs):
1c522f
-        
1c522f
+    def __init__(
1c522f
+            self, graph_name='', obj_dict=None, suppress_disconnected=False,
1c522f
+            simplify=False, **attrs):
1c522f
 
1c522f
-        Graph.__init__(self, graph_name=graph_name, obj_dict=obj_dict,
1c522f
+        Graph.__init__(
1c522f
+            self, graph_name=graph_name, obj_dict=obj_dict,
1c522f
             suppress_disconnected=suppress_disconnected, simplify=simplify, **attrs)
1c522f
 
1c522f
         if obj_dict is None:
1c522f
-
1c522f
             self.obj_dict['type'] = 'subgraph'
1c522f
 
1c522f
 
1c522f
-
1c522f
-
1c522f
 class Cluster(Graph):
1c522f
 
1c522f
     """Class representing a cluster in Graphviz's dot language.
1c522f
 
1c522f
     This class implements the methods to work on a representation
1c522f
     of a cluster in Graphviz's dot language.
1c522f
-    
1c522f
+
1c522f
     cluster(graph_name='subG', suppress_disconnected=False, attribute=value, ...)
1c522f
-    
1c522f
+
1c522f
     graph_name:
1c522f
         the cluster's name (the string 'cluster' will be always prepended)
1c522f
     suppress_disconnected:
1c522f
@@ -1737,38 +1676,35 @@ class Cluster(Graph):
1c522f
         cluster any disconnected nodes.
1c522f
     All the attributes defined in the Graphviz dot language should
1c522f
     be supported.
1c522f
-    
1c522f
+
1c522f
     Attributes can be set through the dynamically generated methods:
1c522f
-    
1c522f
+
1c522f
      set_[attribute name], i.e. set_color, set_fontname
1c522f
-     
1c522f
+
1c522f
     or using the instance's attributes:
1c522f
-    
1c522f
-     Cluster.obj_dict['attributes'][attribute name], i.e. 
1c522f
-     
1c522f
+
1c522f
+     Cluster.obj_dict['attributes'][attribute name], i.e.
1c522f
+
1c522f
         cluster_instance.obj_dict['attributes']['label']
1c522f
         cluster_instance.obj_dict['attributes']['fontname']
1c522f
     """
1c522f
-    
1c522f
 
1c522f
-    def __init__(self, graph_name='subG', obj_dict=None, suppress_disconnected=False,
1c522f
-        simplify=False, **attrs):
1c522f
+    def __init__(
1c522f
+            self, graph_name='subG', obj_dict=None, suppress_disconnected=False,
1c522f
+            simplify=False, **attrs):
1c522f
 
1c522f
-        Graph.__init__(self, graph_name=graph_name, obj_dict=obj_dict,
1c522f
-            suppress_disconnected=suppress_disconnected, simplify=simplify, **attrs)
1c522f
+        Graph.__init__(
1c522f
+            self, graph_name=graph_name, obj_dict=obj_dict,
1c522f
+            suppress_disconnected=suppress_disconnected, simplify=simplify, **attrs
1c522f
+            )
1c522f
 
1c522f
         if obj_dict is None:
1c522f
-
1c522f
             self.obj_dict['type'] = 'subgraph'
1c522f
-            self.obj_dict['name'] = 'cluster_'+graph_name
1c522f
+            self.obj_dict['name'] = 'cluster_' + graph_name
1c522f
 
1c522f
         self.create_attribute_methods(CLUSTER_ATTRIBUTES)
1c522f
 
1c522f
 
1c522f
-
1c522f
-   
1c522f
-
1c522f
-
1c522f
 class Dot(Graph):
1c522f
     """A container for handling a dot language file.
1c522f
 
1c522f
@@ -1776,144 +1712,148 @@ class Dot(Graph):
1c522f
     a dot language file. It is a derived class of
1c522f
     the base class 'Graph'.
1c522f
     """
1c522f
-    
1c522f
-    
1c522f
-     
1c522f
+
1c522f
     def __init__(self, *argsl, **argsd):
1c522f
         Graph.__init__(self, *argsl, **argsd)
1c522f
 
1c522f
         self.shape_files = list()
1c522f
-
1c522f
         self.progs = None
1c522f
-        
1c522f
-        self.formats = ['canon', 'cmap', 'cmapx', 'cmapx_np', 'dia', 'dot',
1c522f
+        self.formats = [
1c522f
+            'canon', 'cmap', 'cmapx', 'cmapx_np', 'dia', 'dot',
1c522f
             'fig', 'gd', 'gd2', 'gif', 'hpgl', 'imap', 'imap_np', 'ismap',
1c522f
             'jpe', 'jpeg', 'jpg', 'mif', 'mp', 'pcl', 'pdf', 'pic', 'plain',
1c522f
             'plain-ext', 'png', 'ps', 'ps2', 'svg', 'svgz', 'vml', 'vmlz',
1c522f
-            'vrml', 'vtx', 'wbmp', 'xdot', 'xlib' ]
1c522f
-
1c522f
+            'vrml', 'vtx', 'wbmp', 'xdot', 'xlib'
1c522f
+            ]
1c522f
         self.prog = 'dot'
1c522f
-        
1c522f
+
1c522f
         # Automatically creates all the methods enabling the creation
1c522f
         # of output in any of the supported formats.
1c522f
         for frmt in self.formats:
1c522f
             self.__setattr__(
1c522f
-                'create_'+frmt,
1c522f
-                lambda f=frmt, prog=self.prog : self.create(format=f, prog=prog))
1c522f
-            f = self.__dict__['create_'+frmt]
1c522f
-            f.__doc__ = '''Refer to the docstring accompanying the 'create' method for more information.'''
1c522f
-            
1c522f
-        for frmt in self.formats+['raw']:
1c522f
+                'create_' + frmt,
1c522f
+                lambda f=frmt, prog=self.prog: self.create(format=f, prog=prog)
1c522f
+                )
1c522f
+            f = self.__dict__['create_' + frmt]
1c522f
+            f.__doc__ = (
1c522f
+                '''Refer to the docstring accompanying the'''
1c522f
+                ''''create' method for more information.'''
1c522f
+                )
1c522f
+
1c522f
+        for frmt in self.formats + ['raw']:
1c522f
             self.__setattr__(
1c522f
-                'write_'+frmt,
1c522f
-                lambda path, f=frmt, prog=self.prog : self.write(path, format=f, prog=prog))
1c522f
-                
1c522f
-            f = self.__dict__['write_'+frmt]
1c522f
-            f.__doc__ = '''Refer to the docstring accompanying the 'write' method for more information.'''
1c522f
-        
1c522f
-        
1c522f
-    
1c522f
+                'write_' + frmt,
1c522f
+                lambda path, f=frmt, prog=self.prog: self.write(path, format=f, prog=prog)
1c522f
+                )
1c522f
+
1c522f
+            f = self.__dict__['write_' + frmt]
1c522f
+            f.__doc__ = (
1c522f
+                '''Refer to the docstring accompanying the'''
1c522f
+                ''''write' method for more information.'''
1c522f
+                )
1c522f
+
1c522f
     def __getstate__(self):
1c522f
-        
1c522f
-        dict = copy.copy(self.obj_dict)
1c522f
-        
1c522f
-        return dict
1c522f
-    
1c522f
+        return copy.copy(self.obj_dict)
1c522f
+
1c522f
     def __setstate__(self, state):
1c522f
-        
1c522f
         self.obj_dict = state
1c522f
-        
1c522f
-    
1c522f
+
1c522f
     def set_shape_files(self, file_paths):
1c522f
         """Add the paths of the required image files.
1c522f
-        
1c522f
+
1c522f
         If the graph needs graphic objects to be used as shapes or otherwise
1c522f
         those need to be in the same folder as the graph is going to be rendered
1c522f
         from. Alternatively the absolute path to the files can be specified when
1c522f
         including the graphics in the graph.
1c522f
-        
1c522f
+
1c522f
         The files in the location pointed to by the path(s) specified as arguments
1c522f
         to this method will be copied to the same temporary location where the
1c522f
         graph is going to be rendered.
1c522f
         """
1c522f
-        
1c522f
-        if isinstance( file_paths, basestring ):
1c522f
-            self.shape_files.append( file_paths )
1c522f
-            
1c522f
-        if isinstance( file_paths, (list, tuple) ):
1c522f
-            self.shape_files.extend( file_paths )
1c522f
-    
1c522f
-                
1c522f
+
1c522f
+        if isinstance(file_paths, basestring):
1c522f
+            self.shape_files.append(file_paths)
1c522f
+
1c522f
+        if isinstance(file_paths, (list, tuple)):
1c522f
+            self.shape_files.extend(file_paths)
1c522f
+
1c522f
     def set_prog(self, prog):
1c522f
         """Sets the default program.
1c522f
-        
1c522f
+
1c522f
         Sets the default program in charge of processing
1c522f
         the dot file into a graph.
1c522f
         """
1c522f
         self.prog = prog
1c522f
-        
1c522f
 
1c522f
     def set_graphviz_executables(self, paths):
1c522f
         """This method allows to manually specify the location of the GraphViz executables.
1c522f
-        
1c522f
+
1c522f
         The argument to this method should be a dictionary where the keys are as follows:
1c522f
-        
1c522f
+
1c522f
             {'dot': '', 'twopi': '', 'neato': '', 'circo': '', 'fdp': ''}
1c522f
-            
1c522f
+
1c522f
         and the values are the paths to the corresponding executable, including the name
1c522f
         of the executable itself.
1c522f
         """
1c522f
-    
1c522f
-        self.progs = paths
1c522f
 
1c522f
+        self.progs = paths
1c522f
 
1c522f
     def write(self, path, prog=None, format='raw'):
1c522f
-        """Writes a graph to a file.
1c522f
-
1c522f
+        """
1c522f
         Given a filename 'path' it will open/create and truncate
1c522f
         such file and write on it a representation of the graph
1c522f
         defined by the dot object and in the format specified by
1c522f
-        'format'.
1c522f
+        'format'. 'path' can also be an open file-like object, such as
1c522f
+        a StringIO instance.
1c522f
+
1c522f
         The format 'raw' is used to dump the string representation
1c522f
         of the Dot object, without further processing.
1c522f
         The output can be processed by any of graphviz tools, defined
1c522f
         in 'prog', which defaults to 'dot'
1c522f
         Returns True or False according to the success of the write
1c522f
         operation.
1c522f
-        
1c522f
+
1c522f
         There's also the preferred possibility of using:
1c522f
-        
1c522f
+
1c522f
             write_'format'(path, prog='program')
1c522f
-            
1c522f
+
1c522f
         which are automatically defined for all the supported formats.
1c522f
         [write_ps(), write_gif(), write_dia(), ...]
1c522f
-        """
1c522f
 
1c522f
+        """
1c522f
         if prog is None:
1c522f
             prog = self.prog
1c522f
-        
1c522f
-        dot_fd = file(path, "w+b")
1c522f
-        if format == 'raw':
1c522f
-            data = self.to_string()
1c522f
-            if isinstance(data, basestring):
1c522f
-                if not isinstance(data, unicode):
1c522f
-                    try:
1c522f
-                        data = unicode(data, 'utf-8')
1c522f
-                    except:
1c522f
-                        pass
1c522f
-                        
1c522f
-            try:
1c522f
-                data = data.encode('utf-8')
1c522f
-            except:
1c522f
-                pass
1c522f
-            dot_fd.write(data)
1c522f
-        else:
1c522f
-            dot_fd.write(self.create(prog, format))
1c522f
-        dot_fd.close()
1c522f
 
1c522f
-        return True
1c522f
-        
1c522f
+        fobj, close = get_fobj(path, 'w+b')
1c522f
+        try:
1c522f
+            if format == 'raw':
1c522f
+                data = self.to_string()
1c522f
+                if isinstance(data, basestring):
1c522f
+                    if not isinstance(data, unicode):
1c522f
+                        try:
1c522f
+                            data = unicode(data, 'utf-8')
1c522f
+                        except:
1c522f
+                            pass
1c522f
+
1c522f
+                try:
1c522f
+                    charset = self.get_charset()
1c522f
+                    if not PY3 or not charset:
1c522f
+                        charset = 'utf-8'
1c522f
+                    data = data.encode(charset)
1c522f
+                except:
1c522f
+                    if PY3:
1c522f
+                        data = data.encode('utf-8')
1c522f
+                    pass
1c522f
+
1c522f
+                fobj.write(data)
1c522f
+
1c522f
+            else:
1c522f
+                fobj.write(self.create(prog, format))
1c522f
+        finally:
1c522f
+            if close:
1c522f
+                fobj.close()
1c522f
 
1c522f
+        return True
1c522f
 
1c522f
     def create(self, prog=None, format='ps'):
1c522f
         """Creates and returns a Postscript representation of the graph.
1c522f
@@ -1923,75 +1863,71 @@ class Dot(Graph):
1c522f
         reading the Postscript output and returning it as a string is the
1c522f
         operation is successful.
1c522f
         On failure None is returned.
1c522f
-        
1c522f
+
1c522f
         There's also the preferred possibility of using:
1c522f
-        
1c522f
+
1c522f
             create_'format'(prog='program')
1c522f
-            
1c522f
+
1c522f
         which are automatically defined for all the supported formats.
1c522f
         [create_ps(), create_gif(), create_dia(), ...]
1c522f
-        
1c522f
+
1c522f
         If 'prog' is a list instead of a string the fist item is expected
1c522f
         to be the program name, followed by any optional command-line
1c522f
         arguments for it:
1c522f
-        
1c522f
-            [ 'twopi', '-Tdot', '-s10' ]
1c522f
+
1c522f
+            ['twopi', '-Tdot', '-s10']
1c522f
         """
1c522f
-        
1c522f
+
1c522f
         if prog is None:
1c522f
             prog = self.prog
1c522f
-            
1c522f
+
1c522f
         if isinstance(prog, (list, tuple)):
1c522f
             prog, args = prog[0], prog[1:]
1c522f
         else:
1c522f
             args = []
1c522f
-            
1c522f
+
1c522f
         if self.progs is None:
1c522f
             self.progs = find_graphviz()
1c522f
             if self.progs is None:
1c522f
                 raise InvocationException(
1c522f
-                    'GraphViz\'s executables not found' )
1c522f
-                
1c522f
-        if not self.progs.has_key(prog):
1c522f
+                    'GraphViz\'s executables not found')
1c522f
+
1c522f
+        if prog not in self.progs:
1c522f
             raise InvocationException(
1c522f
-                'GraphViz\'s executable "%s" not found' % prog )
1c522f
-            
1c522f
-        if not os.path.exists( self.progs[prog] ) or not os.path.isfile( self.progs[prog] ):
1c522f
+                'GraphViz\'s executable "%s" not found' % prog)
1c522f
+
1c522f
+        if not os.path.exists(self.progs[prog]) or not os.path.isfile(self.progs[prog]):
1c522f
             raise InvocationException(
1c522f
-                'GraphViz\'s executable "%s" is not a file or doesn\'t exist' % self.progs[prog] )
1c522f
-            
1c522f
-            
1c522f
+                'GraphViz\'s executable "%s" is not a file or doesn\'t exist' % self.progs[prog])
1c522f
+
1c522f
         tmp_fd, tmp_name = tempfile.mkstemp()
1c522f
         os.close(tmp_fd)
1c522f
         self.write(tmp_name)
1c522f
-        tmp_dir = os.path.dirname(tmp_name )
1c522f
-        
1c522f
+        tmp_dir = os.path.dirname(tmp_name)
1c522f
+
1c522f
         # For each of the image files...
1c522f
-        #
1c522f
         for img in self.shape_files:
1c522f
-        
1c522f
+
1c522f
             # Get its data
1c522f
-            #
1c522f
-            f = file(img, 'rb')
1c522f
+            f = open(img, 'rb')
1c522f
             f_data = f.read()
1c522f
             f.close()
1c522f
-            
1c522f
+
1c522f
             # And copy it under a file with the same name in the temporary directory
1c522f
-            #
1c522f
-            f = file( os.path.join( tmp_dir, os.path.basename(img) ), 'wb' )
1c522f
+            f = open(os.path.join(tmp_dir, os.path.basename(img)), 'wb')
1c522f
             f.write(f_data)
1c522f
             f.close()
1c522f
-        
1c522f
-        cmdline = [self.progs[prog], '-T'+format, tmp_name] + args
1c522f
-        
1c522f
+
1c522f
+        cmdline = [self.progs[prog], '-T' + format, tmp_name] + args
1c522f
+
1c522f
         p = subprocess.Popen(
1c522f
             cmdline,
1c522f
             cwd=tmp_dir,
1c522f
             stderr=subprocess.PIPE, stdout=subprocess.PIPE)
1c522f
-            
1c522f
+
1c522f
         stderr = p.stderr
1c522f
         stdout = p.stdout
1c522f
-        
1c522f
+
1c522f
         stdout_output = list()
1c522f
         while True:
1c522f
             data = stdout.read()
1c522f
@@ -1999,9 +1935,9 @@ class Dot(Graph):
1c522f
                 break
1c522f
             stdout_output.append(data)
1c522f
         stdout.close()
1c522f
-            
1c522f
-        stdout_output = ''.join(stdout_output)
1c522f
-        
1c522f
+
1c522f
+        stdout_output = NULL_SEP.join(stdout_output)
1c522f
+
1c522f
         if not stderr.closed:
1c522f
             stderr_output = list()
1c522f
             while True:
1c522f
@@ -2010,29 +1946,28 @@ class Dot(Graph):
1c522f
                     break
1c522f
                 stderr_output.append(data)
1c522f
             stderr.close()
1c522f
-                
1c522f
+
1c522f
             if stderr_output:
1c522f
-                stderr_output = ''.join(stderr_output)
1c522f
-            
1c522f
+                stderr_output = NULL_SEP.join(stderr_output)
1c522f
+                if PY3:
1c522f
+                    stderr_output = stderr_output.decode(sys.stderr.encoding)
1c522f
+
1c522f
         #pid, status = os.waitpid(p.pid, 0)
1c522f
         status = p.wait()
1c522f
-        
1c522f
-        if status != 0 :
1c522f
+
1c522f
+        if status != 0:
1c522f
             raise InvocationException(
1c522f
                 'Program terminated with status: %d. stderr follows: %s' % (
1c522f
-                    status, stderr_output) )
1c522f
+                    status, stderr_output))
1c522f
         elif stderr_output:
1c522f
-            print stderr_output
1c522f
-        
1c522f
+            print(stderr_output)
1c522f
+
1c522f
         # For each of the image files...
1c522f
-        #
1c522f
         for img in self.shape_files:
1c522f
-        
1c522f
+
1c522f
             # remove it
1c522f
-            #
1c522f
-            os.unlink( os.path.join( tmp_dir, os.path.basename(img) ) )
1c522f
+            os.unlink(os.path.join(tmp_dir, os.path.basename(img)))
1c522f
 
1c522f
         os.unlink(tmp_name)
1c522f
-        
1c522f
-        return stdout_output
1c522f
 
1c522f
+        return stdout_output
1c522f
diff --git a/setup.py b/setup.py
1c522f
index 27328d8..92890d7 100644
1c522f
--- a/setup.py
1c522f
+++ b/setup.py
1c522f
@@ -1,10 +1,6 @@
1c522f
 #!/usr/bin/env python
1c522f
 
1c522f
-try:
1c522f
-    from distutils.core import setup
1c522f
-except ImportError, excp:
1c522f
-    from setuptools import setup
1c522f
-    
1c522f
+from setuptools import setup
1c522f
 import pydot
1c522f
 import os
1c522f