Proper working implementation of a cycle detection algorithm, that
authorSverre Rabbelier <srabbelier@gmail.com>
Thu, 27 Nov 2008 21:57:24 +0000
changeset 597 66088092f849
parent 596 7dd98eeba61b
child 598 c1f9435bb645
Proper working implementation of a cycle detection algorithm, that returns the cycles (rather than printing them) by constructing the path between the two nodes that were found to be cyclic. Patch by: Sverre Rabbelier
app/check_includes.py
--- a/app/check_includes.py	Thu Nov 27 17:22:39 2008 +0000
+++ b/app/check_includes.py	Thu Nov 27 21:57:24 2008 +0000
@@ -1,12 +1,10 @@
 #!/usr/bin/env python
 
 import sys
-sys.path.append('/usr/lib/graphviz/python/')
 
 import cPickle
 import os
 import graph
-import gv
 
 def parseFile(file_name):
   if os.path.exists("imports.dat"):
@@ -38,7 +36,10 @@
     if imp in set(imports[idx+1:]):
       print "Warning: file '%s' has a redundant import: '%s'." % (file_name, imp)
 
-  normalized = "soc.%s" % file_name[:-3].replace('/', '.')
+  if file_name.endswith("__init__.py"):
+    normalized = file_name[:-12].replace('/', '.')
+  else:
+    normalized = file_name[:-3].replace('/', '.')
 
   print "Writing imports for file %s (%s)." % (file_name, normalized)
   all_imports[normalized] = imports
@@ -51,45 +52,104 @@
 
 
 def buildGraph():
+  import graph
+
   log = open("imports.dat", "r")
   all_imports = cPickle.load(log)
 
-  gr = graph.graph()
+  gr = graph.digraph()
 
   gr.add_nodes(all_imports.keys())
 
   for file_name, imports in all_imports.iteritems():
     for imp in imports:
-      if imp not in gr:
-        gr.add_node(imp)
       gr.add_edge(file_name, imp)
 
   return gr
 
 
-def showGraph():
-  gr = buildGraph()
+def showGraph(gr):
   for node in gr:
     print "%s: " % node
     for edge in gr[node]:
       print "\t%s" % edge
 
+  return 0
 
-def drawGraph():
-  gr = buildGraph()
-  dot = gr.write(fmt='dot')
+
+def getParents(gst, target):
+  parents = []
+  current = target
+
+  while True:
+    for node in gst:
+      if current in gst[node]:
+        parents.append(node)
+        current = node
+        break
+    else:
+      break
+
+  return parents
+
+
+def pathFrom(parents, first, target):
+  idx = parents.index(first)
+  path = parents[idx::-1]
+  return [target] + path + [target]
+
+
+def findCycle(gr, gst, target):
+  parents = getParents(gst, target)
+  cycles = []
+
+  for node in gr[target]:
+    if node in parents:
+      cycles.append(pathFrom(parents, node, target))
+
+  return cycles
+
+
+def findCycles(gr):
+  st, pre, post = gr.depth_first_search()
+  gst = graph.digraph()
+  gst.add_spanning_tree(st)
+
+  cycles = []
+
+  for node in gr:
+    cycles += findCycle(gr, gst, node)
+
+  return cycles
+
+
+def drawGraph(gr):
+  st, pre, post = gr.depth_first_search()
+  gst = graph.digraph()
+  gst.add_spanning_tree(st)
+
+  sys.path.append('/usr/lib/graphviz/python/')
+  import gv
+  dot = gst.write(fmt='dot')
   gvv = gv.readstring(dot)
   gv.layout(gvv,'dot')
   gv.render(gvv,'png','imports.png')
 
 
 def main(args):
-  if len(args) == 1:
+  if len(args) == 1 and args[0].startswith("."):
+    gr = buildGraph()
     if args[0] == '.':
-      return showGraph()
+      return showGraph(gr)
 
     if args[0] == '..':
-      return drawGraph()
+      return drawGraph(gr)
+
+    if args[0] == '...':
+      cycles = findCycles(gr)
+      for cycle in cycles:
+        print cycle
+      return 0
 
   if not args:
     print "Please specify a filename to parse, or '.' to list the parsed imports"