Add root_node and leaf_node functions to MigrationGraph

This commit is contained in:
Andrew Godwin 2013-05-10 16:09:57 +01:00
parent 9ce8354672
commit 8a1f017777
2 changed files with 46 additions and 2 deletions

View File

@ -19,8 +19,9 @@ class MigrationGraph(object):
replacing migration, and repoint any dependencies that pointed to the
replaced migrations to point to the replacing one.
A node should be a tuple: (app_path, migration_name) - but the code
here doesn't really care.
A node should be a tuple: (app_path, migration_name). The tree special-cases
things within an app - namely, root nodes and leaf nodes ignore dependencies
to other apps.
"""
def __init__(self):
@ -59,6 +60,31 @@ class MigrationGraph(object):
raise ValueError("Node %r not a valid node" % node)
return self.dfs(node, lambda x: self.dependents.get(x, set()))
def root_nodes(self):
"""
Returns all root nodes - that is, nodes with no dependencies inside
their app. These are the starting point for an app.
"""
roots = set()
for node in self.nodes:
if not filter(lambda key: key[0] == node[0], self.dependencies.get(node, set())):
roots.add(node)
return roots
def leaf_nodes(self):
"""
Returns all leaf nodes - that is, nodes with no dependents in their app.
These are the "most current" version of an app's schema.
Having more than one per app is technically an error, but one that
gets handled further up, in the interactive command - it's usually the
result of a VCS merge and needs some user input.
"""
leaves = set()
for node in self.nodes:
if not filter(lambda key: key[0] == node[0], self.dependents.get(node, set())):
leaves.add(node)
return leaves
def dfs(self, start, get_children):
"""
Dynamic programming based depth first search, for finding dependencies.

View File

@ -44,6 +44,15 @@ class GraphTests(TransactionTestCase):
graph.backwards_plan(("app_b", "0002")),
[('app_a', '0004'), ('app_a', '0003'), ('app_b', '0002')],
)
# Test roots and leaves
self.assertEqual(
graph.root_nodes(),
set([('app_a', '0001'), ('app_b', '0001')]),
)
self.assertEqual(
graph.leaf_nodes(),
set([('app_a', '0004'), ('app_b', '0002')]),
)
def test_complex_graph(self):
"""
@ -81,6 +90,15 @@ class GraphTests(TransactionTestCase):
graph.backwards_plan(("app_b", "0001")),
[('app_a', '0004'), ('app_c', '0002'), ('app_c', '0001'), ('app_a', '0003'), ('app_b', '0002'), ('app_b', '0001')],
)
# Test roots and leaves
self.assertEqual(
graph.root_nodes(),
set([('app_a', '0001'), ('app_b', '0001'), ('app_c', '0001')]),
)
self.assertEqual(
graph.leaf_nodes(),
set([('app_a', '0004'), ('app_b', '0002'), ('app_c', '0002')]),
)
def test_circular_graph(self):
"""