|
1 #!/usr/bin/env python |
|
2 |
|
3 import sys |
|
4 |
|
5 import cPickle |
|
6 import os |
|
7 import graph |
|
8 |
|
9 |
|
10 ROOTDIR = "soc" |
|
11 |
|
12 |
|
13 def parseFile(file_name): |
|
14 if os.path.exists("imports.dat"): |
|
15 log = open("imports.dat", "r") |
|
16 all_imports = cPickle.load(log) |
|
17 log.close() |
|
18 else: |
|
19 all_imports = {} |
|
20 |
|
21 if file_name in all_imports: |
|
22 print "Overwriting previous data on '%s'." % file_name |
|
23 |
|
24 imports = [] |
|
25 |
|
26 file = open(file_name) |
|
27 |
|
28 for line in file: |
|
29 if line.lstrip().startswith('import soc'): |
|
30 splitline = line[:-1].split(' ') |
|
31 mod = splitline[1] |
|
32 imports.append(mod) |
|
33 |
|
34 if line.lstrip().startswith('from soc'): |
|
35 splitline = line[:-1].split(' ') |
|
36 mod = splitline[1] + '.' + splitline[3] |
|
37 imports.append(mod) |
|
38 |
|
39 for idx, imp in enumerate(imports): |
|
40 if imp in set(imports[idx+1:]): |
|
41 sys.stderr.write("Warning: file '%s' has a redundant import: '%s'.\n" % (file_name, imp)) |
|
42 |
|
43 if file_name.endswith("__init__.py"): |
|
44 normalized = file_name[:-12].replace('/', '.') |
|
45 else: |
|
46 normalized = file_name[:-3].replace('/', '.') |
|
47 |
|
48 print "Writing imports for file %s (%s)." % (file_name, normalized) |
|
49 all_imports[normalized] = imports |
|
50 |
|
51 log = open("imports.dat", "w") |
|
52 cPickle.dump(all_imports, log) |
|
53 log.close() |
|
54 |
|
55 return 0 |
|
56 |
|
57 |
|
58 def buildGraph(): |
|
59 if not os.path.exists("imports.dat"): |
|
60 sys.stderr.write("Missing imports.dat file, run 'build' first\n") |
|
61 return |
|
62 |
|
63 log = open("imports.dat", "r") |
|
64 all_imports = cPickle.load(log) |
|
65 |
|
66 gr = graph.digraph() |
|
67 |
|
68 gr.add_nodes(all_imports.keys()) |
|
69 |
|
70 for file_name, imports in all_imports.iteritems(): |
|
71 for imp in imports: |
|
72 gr.add_edge(file_name, imp) |
|
73 |
|
74 return gr |
|
75 |
|
76 |
|
77 def showGraph(gr): |
|
78 for node in gr: |
|
79 print "%s: " % node |
|
80 for edge in gr[node]: |
|
81 print "\t%s" % edge |
|
82 |
|
83 return 0 |
|
84 |
|
85 |
|
86 def getParents(gst, target): |
|
87 parents = [] |
|
88 current = target |
|
89 |
|
90 while True: |
|
91 for node in gst: |
|
92 if current in gst[node]: |
|
93 parents.append(node) |
|
94 current = node |
|
95 break |
|
96 else: |
|
97 break |
|
98 |
|
99 return parents |
|
100 |
|
101 |
|
102 def pathFrom(parents, first, target): |
|
103 idx = parents.index(first) |
|
104 path = parents[idx::-1] |
|
105 return [target] + path + [target] |
|
106 |
|
107 |
|
108 def findCycle(gr, gst, target): |
|
109 parents = getParents(gst, target) |
|
110 cycles = [] |
|
111 |
|
112 for node in gr[target]: |
|
113 if node in parents: |
|
114 cycles.append(pathFrom(parents, node, target)) |
|
115 |
|
116 return cycles |
|
117 |
|
118 |
|
119 def findCycles(gr): |
|
120 st, pre, post = gr.depth_first_search() |
|
121 gst = graph.digraph() |
|
122 gst.add_spanning_tree(st) |
|
123 |
|
124 cycles = [] |
|
125 |
|
126 for node in gr: |
|
127 cycles += findCycle(gr, gst, node) |
|
128 |
|
129 return cycles |
|
130 |
|
131 |
|
132 def drawGraph(gr): |
|
133 st, pre, post = gr.depth_first_search() |
|
134 gst = graph.digraph() |
|
135 gst.add_spanning_tree(st) |
|
136 |
|
137 sys.path.append('/usr/lib/graphviz/python/') |
|
138 import gv |
|
139 dot = gst.write(fmt='dot') |
|
140 gvv = gv.readstring(dot) |
|
141 gv.layout(gvv,'dot') |
|
142 gv.render(gvv,'png','imports.png') |
|
143 |
|
144 |
|
145 def accumulate(arg, dirname, fnames): |
|
146 for fname in fnames: |
|
147 if not fname.endswith(".py"): |
|
148 continue |
|
149 |
|
150 arg.append(os.path.join(dirname, fname)) |
|
151 |
|
152 |
|
153 def main(args): |
|
154 if len(args) != 1: |
|
155 print "Supported options: 'print', 'build', 'find', and 'draw'." |
|
156 return -1 |
|
157 |
|
158 action = args[0] |
|
159 |
|
160 if action == "build": |
|
161 fnames = [] |
|
162 os.path.walk(ROOTDIR, accumulate, fnames) |
|
163 |
|
164 for fname in fnames: |
|
165 parseFile(fname) |
|
166 |
|
167 print "Done parsing." |
|
168 return 0 |
|
169 |
|
170 gr = buildGraph() |
|
171 if not gr: |
|
172 return 1 |
|
173 |
|
174 if action == "show": |
|
175 return showGraph(gr) |
|
176 |
|
177 if action == "draw": |
|
178 return drawGraph(gr) |
|
179 |
|
180 if action == "find": |
|
181 cycles = findCycles(gr) |
|
182 for cycle in cycles: |
|
183 print cycle |
|
184 return 0 |
|
185 |
|
186 print "Unknown option." |
|
187 return -1 |
|
188 |
|
189 |
|
190 if __name__ == '__main__': |
|
191 import sys |
|
192 os.chdir("../app") |
|
193 res = main(sys.argv[1:]) |
|
194 sys.exit(0) |