summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlly Cope <olly@ollycope.com>2016-09-04 13:04:51 +0000
committerOlly Cope <olly@ollycope.com>2016-09-04 13:04:51 +0000
commitb0eb051c76f9f5fa9d7737547ddadc83e8802ee5 (patch)
tree5c75b1a39ee91fc2d3f3684af10be45224014acb
parentf4bfd78de4fd71c8aa04db4fd91ba1a0524e5011 (diff)
downloadyoyo-b0eb051c76f9f5fa9d7737547ddadc83e8802ee5.tar.gz
Fix toposort of migrations when multiple migrations have the same dependency
This fixes #23
-rwxr-xr-xyoyo/migrations.py14
-rw-r--r--yoyo/tests/test_migrations.py16
2 files changed, 21 insertions, 9 deletions
diff --git a/yoyo/migrations.py b/yoyo/migrations.py
index dfc0c32..8aab47c 100755
--- a/yoyo/migrations.py
+++ b/yoyo/migrations.py
@@ -13,7 +13,7 @@
# limitations under the License.
from collections import (defaultdict, OrderedDict, Counter, MutableSequence,
- Iterable)
+ Iterable, deque)
from copy import copy
from itertools import chain, count
from logging import getLogger
@@ -508,17 +508,17 @@ def topological_sort(migration_list):
to_toposort = set(chain(forward_edges, backward_edges))
# Starting migrations: those with no dependencies
- # This is a reversed list so that popping/pushing from the end maintains
- # the desired order
- S = list(reversed([m for m in to_toposort
- if not any(n in valid_migrations for n in m.depends)]))
+ # To make this a stable sort we always need to pop from the left end
+ # of this list, hence use a deque.
+ S = deque(m for m in to_toposort
+ if not any(n in valid_migrations for n in m.depends))
while S:
- n = S.pop()
+ n = S.popleft()
L.append(n)
# for each node M with an edge E from N to M
- for m in forward_edges[n]:
+ for m in list(forward_edges[n]):
# remove edge E from the graph
del forward_edges[n][m]
diff --git a/yoyo/tests/test_migrations.py b/yoyo/tests/test_migrations.py
index 8173c9a..376fcab 100644
--- a/yoyo/tests/test_migrations.py
+++ b/yoyo/tests/test_migrations.py
@@ -236,8 +236,13 @@ def test_migrations_display_selected_data(tmpdir):
class TestTopologicalSort(object):
def get_mock_migrations(self):
- return [Mock(id='m1', depends=set()), Mock(id='m2', depends=set()),
- Mock(id='m3', depends=set()), Mock(id='m4', depends=set())]
+ class MockMigration(Mock):
+ def __repr__(self):
+ return "<MockMigration {}>".format(self.id)
+ return [MockMigration(id='m1', depends=set()),
+ MockMigration(id='m2', depends=set()),
+ MockMigration(id='m3', depends=set()),
+ MockMigration(id='m4', depends=set())]
def test_it_keeps_stable_order(self):
m1, m2, m3, m4 = self.get_mock_migrations()
@@ -265,6 +270,13 @@ class TestTopologicalSort(object):
with pytest.raises(exceptions.BadMigration):
list(topological_sort([m1, m2, m3, m4]))
+ def test_it_handles_multiple_edges_to_the_same_node(self):
+ m1, m2, m3, m4 = self.get_mock_migrations()
+ m2.depends.add(m1)
+ m3.depends.add(m1)
+ m4.depends.add(m1)
+ assert list(topological_sort([m1, m2, m3, m4])) == [m1, m2, m3, m4]
+
class TestMigrationList(object):