Index: third_party/logilab/common/graph.py |
=================================================================== |
--- third_party/logilab/common/graph.py (revision 292986) |
+++ third_party/logilab/common/graph.py (working copy) |
@@ -28,7 +28,8 @@ |
import os |
import sys |
import tempfile |
-from logilab.common.compat import str_encode |
+import codecs |
+import errno |
def escape(value): |
"""Make <value> usable in a dot file.""" |
@@ -63,7 +64,7 @@ |
assert charset.lower() in ('utf-8', 'iso-8859-1', 'latin1'), \ |
'unsupported charset %s' % charset |
self.emit('charset="%s"' % charset) |
- for param in additionnal_param.iteritems(): |
+ for param in sorted(additionnal_param.items()): |
self.emit('='.join(param)) |
def get_source(self): |
@@ -106,8 +107,8 @@ |
ppng, outputfile = tempfile.mkstemp(".png", name) |
os.close(pdot) |
os.close(ppng) |
- pdot = open(dot_sourcepath, 'w') |
- pdot.write(str_encode(self.source, 'utf8')) |
+ pdot = codecs.open(dot_sourcepath, 'w', encoding='utf8') |
+ pdot.write(self.source) |
pdot.close() |
if target != 'dot': |
if sys.platform == 'win32': |
@@ -114,13 +115,18 @@ |
use_shell = True |
else: |
use_shell = False |
- if mapfile: |
- subprocess.call([self.renderer, '-Tcmapx', '-o', mapfile, '-T', target, dot_sourcepath, '-o', outputfile], |
- shell=use_shell) |
- else: |
- subprocess.call([self.renderer, '-T', target, |
- dot_sourcepath, '-o', outputfile], |
- shell=use_shell) |
+ try: |
+ if mapfile: |
+ subprocess.call([self.renderer, '-Tcmapx', '-o', mapfile, '-T', target, dot_sourcepath, '-o', outputfile], |
+ shell=use_shell) |
+ else: |
+ subprocess.call([self.renderer, '-T', target, |
+ dot_sourcepath, '-o', outputfile], |
+ shell=use_shell) |
+ except OSError as e: |
+ if e.errno == errno.ENOENT: |
+ e.strerror = 'File not found: {0}'.format(self.renderer) |
+ raise |
os.unlink(dot_sourcepath) |
return outputfile |
@@ -134,7 +140,7 @@ |
""" |
attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()] |
n_from, n_to = normalize_node_id(name1), normalize_node_id(name2) |
- self.emit('%s -> %s [%s];' % (n_from, n_to, ", ".join(attrs)) ) |
+ self.emit('%s -> %s [%s];' % (n_from, n_to, ', '.join(sorted(attrs))) ) |
def emit_node(self, name, **props): |
"""emit a node with given properties. |
@@ -141,7 +147,7 @@ |
node properties: see http://www.graphviz.org/doc/info/attrs.html |
""" |
attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()] |
- self.emit('%s [%s];' % (normalize_node_id(name), ", ".join(attrs))) |
+ self.emit('%s [%s];' % (normalize_node_id(name), ', '.join(sorted(attrs)))) |
def normalize_node_id(nid): |
"""Returns a suitable DOT node id for `nid`.""" |
@@ -226,10 +232,10 @@ |
if vertices is None: |
vertices = graph_dict.keys() |
for vertice in vertices: |
- _get_cycles(graph_dict, vertice, [], result) |
+ _get_cycles(graph_dict, [], set(), result, vertice) |
return result |
-def _get_cycles(graph_dict, vertice=None, path=None, result=None): |
+def _get_cycles(graph_dict, path, visited, result, vertice): |
"""recursive function doing the real work for get_cycles""" |
if vertice in path: |
cycle = [vertice] |
@@ -248,7 +254,10 @@ |
path.append(vertice) |
try: |
for node in graph_dict[vertice]: |
- _get_cycles(graph_dict, node, path, result) |
+ # don't check already visited nodes again |
+ if node not in visited: |
+ _get_cycles(graph_dict, path, visited, result, node) |
+ visited.add(node) |
except KeyError: |
pass |
path.pop() |