summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/mapping/topological.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2006-01-28 18:02:42 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2006-01-28 18:02:42 +0000
commit8d1d7ec0316f3b325aa9cf30cdb714c768f65c74 (patch)
treef42ba58f7ef4a4d75e4b196650e3aaac9460251e /lib/sqlalchemy/mapping/topological.py
parentf05b29eb096135a8399586e40f0d07152808c091 (diff)
downloadsqlalchemy-8d1d7ec0316f3b325aa9cf30cdb714c768f65c74.tar.gz
topological sort can detect cycles, and assemble them into a "big node" with all
the nodes in the cycle aggregated into one node
Diffstat (limited to 'lib/sqlalchemy/mapping/topological.py')
-rw-r--r--lib/sqlalchemy/mapping/topological.py83
1 files changed, 62 insertions, 21 deletions
diff --git a/lib/sqlalchemy/mapping/topological.py b/lib/sqlalchemy/mapping/topological.py
index 92db01c23..8c51b8f67 100644
--- a/lib/sqlalchemy/mapping/topological.py
+++ b/lib/sqlalchemy/mapping/topological.py
@@ -30,7 +30,8 @@ work again, then break, etc. Most of the bugs I've chased down while developin
have been of this nature - very tricky to reproduce and track down, particularly before I
realized this characteristic of the algorithm.
"""
-import string
+import string, StringIO
+from sets import *
class QueueDependencySorter(object):
"""this is a topological sort from wikipedia. its very stable. it creates a straight-line
@@ -59,6 +60,13 @@ class QueueDependencySorter(object):
def __init__(self, tuples, allitems):
self.tuples = tuples
self.allitems = allitems
+
+ def _dump_edges(self, edges):
+ s = StringIO.StringIO()
+ for key, value in edges.iteritems():
+ for c in value.keys():
+ s.write("%s->%s\n" % (repr(key), repr(c)))
+ return s.getvalue()
def sort(self, allow_self_cycles=True, allow_all_cycles=False):
(tuples, allitems) = (self.tuples, self.allitems)
@@ -99,33 +107,31 @@ class QueueDependencySorter(object):
cycles = {}
output = []
while len(edges) > 0:
+ print self._dump_edges(edges)
if len(queue) == 0:
# edges remain but no edgeless nodes to remove; this indicates
# a cycle
if allow_all_cycles:
- # for each cycle, throw all the nodes involved in that
- # cycle into a list attached to one of those nodes, then
- # add just that node to the output.
- for parentnode in edges.keys():
- d = edges[parentnode]
- for childnode in d.keys():
- if cycles.has_key(parentnode):
- cycles[parentnode].append(childnode)
- cycles[childnode] = cycles[parentnode]
- elif cycles.has_key(childnode):
- cycles[childnode].append(parentnode)
- cycles[parentnode] = cycles[childnode]
- else:
- cycles[parentnode] = parentnode.cycles
- parentnode.cycles.append(childnode)
- cycles[childnode] = parentnode.cycles
- output.append(parentnode)
- break
+ cycle = self._find_cycle(edges)
+ lead = cycle[0][0]
+ for edge in cycle:
+ n = self._remove_edge(edges, edge)
+ lead.cycles.append(edge[0])
+ lead.cycles.append(edge[1])
+ if n is not None:
+ queue.append(n)
+ if n is not lead:
+ n.foo = True
+ # loop through cycle
+ # remove edges from the edge dictionary
+ # install the cycled nodes in the "cycle" list of one of the nodes
+ continue
else:
# long cycles not allowed
raise "Circular dependency detected " + repr(edges) + repr(queue)
node = queue.pop()
- output.append(node)
+ if not hasattr(node, 'foo'):
+ output.append(node)
nodeedges = edges.pop(node, None)
if nodeedges is None:
continue
@@ -134,7 +140,6 @@ class QueueDependencySorter(object):
if len(childnode.edges) == 0:
queue.append(childnode)
-
#print repr(output)
head = None
node = None
@@ -149,6 +154,42 @@ class QueueDependencySorter(object):
node.children.append(o)
return head
+ def _remove_edge(self, edges, edge):
+ (parentnode, childnode) = edge
+ del edges[parentnode][childnode]
+ del childnode.edges[parentnode]
+ del parentnode.dependencies[childnode]
+ if len(childnode.edges) == 0:
+ return childnode
+
+ def _find_cycle(self, edges):
+ """given a structure of edges, locates a cycle in the strucure and returns
+ as a list of tuples representing edges involved in the cycle."""
+ seen = Set()
+ cycled_edges = []
+ def traverse(d, parent=None):
+ for key in d.keys():
+ if not edges.has_key(key):
+ continue
+ if key in seen:
+ if parent is not None:
+ cycled_edges.append((parent, key))
+ return key
+ seen.add(key)
+ x = traverse(edges[key], parent=key)
+ if x is None:
+ seen.remove(key)
+ else:
+ if parent is not None:
+ cycled_edges.append((parent, key))
+ return x
+ else:
+ return None
+ s = traverse(edges)
+ if s is None:
+ return None
+ else:
+ return cycled_edges
class TreeDependencySorter(object):
"""