gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / pylint / graph.py @ 745
History | View | Annotate | Download (6.61 KB)
1 |
# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
|
---|---|
2 |
# This program is free software; you can redistribute it and/or modify it under
|
3 |
# the terms of the GNU General Public License as published by the Free Software
|
4 |
# Foundation; either version 2 of the License, or (at your option) any later
|
5 |
# version.
|
6 |
#
|
7 |
# This program is distributed in the hope that it will be useful, but WITHOUT
|
8 |
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
9 |
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
|
10 |
#
|
11 |
# You should have received a copy of the GNU General Public License along with
|
12 |
# this program; if not, write to the Free Software Foundation, Inc.,
|
13 |
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
14 |
|
15 |
# This file was copied from logilab-common with the same license:
|
16 |
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
|
17 |
"""Graph manipulation utilities.
|
18 |
|
19 |
(dot generation adapted from pypy/translator/tool/make_dot.py)
|
20 |
"""
|
21 |
|
22 |
import os.path as osp |
23 |
import os |
24 |
import sys |
25 |
import tempfile |
26 |
import codecs |
27 |
|
28 |
def target_info_from_filename(filename): |
29 |
"""Transforms /some/path/foo.png into ('/some/path', 'foo.png', 'png')."""
|
30 |
basename = osp.basename(filename) |
31 |
storedir = osp.dirname(osp.abspath(filename)) |
32 |
target = filename.split('.')[-1] |
33 |
return storedir, basename, target
|
34 |
|
35 |
|
36 |
class DotBackend(object): |
37 |
"""Dot File backend."""
|
38 |
def __init__(self, graphname, rankdir=None, size=None, ratio=None, |
39 |
charset='utf-8', renderer='dot', additional_param=None): |
40 |
if additional_param is None: |
41 |
additional_param = {} |
42 |
self.graphname = graphname
|
43 |
self.renderer = renderer
|
44 |
self.lines = []
|
45 |
self._source = None |
46 |
self.emit("digraph %s {" % normalize_node_id(graphname)) |
47 |
if rankdir:
|
48 |
self.emit('rankdir=%s' % rankdir) |
49 |
if ratio:
|
50 |
self.emit('ratio=%s' % ratio) |
51 |
if size:
|
52 |
self.emit('size="%s"' % size) |
53 |
if charset:
|
54 |
assert charset.lower() in ('utf-8', 'iso-8859-1', 'latin1'), \ |
55 |
'unsupported charset %s' % charset
|
56 |
self.emit('charset="%s"' % charset) |
57 |
for param in additional_param.items(): |
58 |
self.emit('='.join(param)) |
59 |
|
60 |
def get_source(self): |
61 |
"""returns self._source"""
|
62 |
if self._source is None: |
63 |
self.emit("}\n") |
64 |
self._source = '\n'.join(self.lines) |
65 |
del self.lines |
66 |
return self._source |
67 |
|
68 |
source = property(get_source)
|
69 |
|
70 |
def generate(self, outputfile=None, dotfile=None, mapfile=None): |
71 |
"""Generates a graph file.
|
72 |
|
73 |
:param outputfile: filename and path [defaults to graphname.png]
|
74 |
:param dotfile: filename and path [defaults to graphname.dot]
|
75 |
|
76 |
:rtype: str
|
77 |
:return: a path to the generated file
|
78 |
"""
|
79 |
import subprocess # introduced in py 2.4 |
80 |
name = self.graphname
|
81 |
if not dotfile: |
82 |
# if 'outputfile' is a dot file use it as 'dotfile'
|
83 |
if outputfile and outputfile.endswith(".dot"): |
84 |
dotfile = outputfile |
85 |
else:
|
86 |
dotfile = '%s.dot' % name
|
87 |
if outputfile is not None: |
88 |
storedir, _, target = target_info_from_filename(outputfile) |
89 |
if target != "dot": |
90 |
pdot, dot_sourcepath = tempfile.mkstemp(".dot", name)
|
91 |
os.close(pdot) |
92 |
else:
|
93 |
dot_sourcepath = osp.join(storedir, dotfile) |
94 |
else:
|
95 |
target = 'png'
|
96 |
pdot, dot_sourcepath = tempfile.mkstemp(".dot", name)
|
97 |
ppng, outputfile = tempfile.mkstemp(".png", name)
|
98 |
os.close(pdot) |
99 |
os.close(ppng) |
100 |
pdot = codecs.open(dot_sourcepath, 'w', encoding='utf8') |
101 |
pdot.write(self.source)
|
102 |
pdot.close() |
103 |
if target != 'dot': |
104 |
use_shell = sys.platform == 'win32'
|
105 |
if mapfile:
|
106 |
subprocess.call([self.renderer, '-Tcmapx', '-o', |
107 |
mapfile, '-T', target, dot_sourcepath,
|
108 |
'-o', outputfile],
|
109 |
shell=use_shell) |
110 |
else:
|
111 |
subprocess.call([self.renderer, '-T', target, |
112 |
dot_sourcepath, '-o', outputfile],
|
113 |
shell=use_shell) |
114 |
os.unlink(dot_sourcepath) |
115 |
return outputfile
|
116 |
|
117 |
def emit(self, line): |
118 |
"""Adds <line> to final output."""
|
119 |
self.lines.append(line)
|
120 |
|
121 |
def emit_edge(self, name1, name2, **props): |
122 |
"""emit an edge from <name1> to <name2>.
|
123 |
edge properties: see http://www.graphviz.org/doc/info/attrs.html
|
124 |
"""
|
125 |
attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()] |
126 |
n_from, n_to = normalize_node_id(name1), normalize_node_id(name2) |
127 |
self.emit('%s -> %s [%s];' % (n_from, n_to, ', '.join(sorted(attrs)))) |
128 |
|
129 |
def emit_node(self, name, **props): |
130 |
"""emit a node with given properties.
|
131 |
node properties: see http://www.graphviz.org/doc/info/attrs.html
|
132 |
"""
|
133 |
attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()] |
134 |
self.emit('%s [%s];' % (normalize_node_id(name), ', '.join(sorted(attrs)))) |
135 |
|
136 |
def normalize_node_id(nid): |
137 |
"""Returns a suitable DOT node id for `nid`."""
|
138 |
return '"%s"' % nid |
139 |
|
140 |
def get_cycles(graph_dict, vertices=None): |
141 |
'''given a dictionary representing an ordered graph (i.e. key are vertices
|
142 |
and values is a list of destination vertices representing edges), return a
|
143 |
list of detected cycles
|
144 |
'''
|
145 |
if not graph_dict: |
146 |
return ()
|
147 |
result = [] |
148 |
if vertices is None: |
149 |
vertices = graph_dict.keys() |
150 |
for vertice in vertices: |
151 |
_get_cycles(graph_dict, [], set(), result, vertice)
|
152 |
return result
|
153 |
|
154 |
def _get_cycles(graph_dict, path, visited, result, vertice): |
155 |
"""recursive function doing the real work for get_cycles"""
|
156 |
if vertice in path: |
157 |
cycle = [vertice] |
158 |
for node in path[::-1]: |
159 |
if node == vertice:
|
160 |
break
|
161 |
cycle.insert(0, node)
|
162 |
# make a canonical representation
|
163 |
start_from = min(cycle)
|
164 |
index = cycle.index(start_from) |
165 |
cycle = cycle[index:] + cycle[0:index]
|
166 |
# append it to result if not already in
|
167 |
if cycle not in result: |
168 |
result.append(cycle) |
169 |
return
|
170 |
path.append(vertice) |
171 |
try:
|
172 |
for node in graph_dict[vertice]: |
173 |
# don't check already visited nodes again
|
174 |
if node not in visited: |
175 |
_get_cycles(graph_dict, path, visited, result, node) |
176 |
visited.add(node) |
177 |
except KeyError: |
178 |
pass
|
179 |
path.pop() |