summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlly Cope <olly@ollycope.com>2021-05-16 17:16:32 +0000
committerOlly Cope <olly@ollycope.com>2021-05-16 17:16:32 +0000
commit0fe38b2a7517a2f3b49c33ece701f50d08ac25c7 (patch)
treea23dd83508d0d398848783a1dc7df60e71b90f04
parentc0ee96a21d53df088547fe3236bd90f093fa673c (diff)
downloadyoyo-0fe38b2a7517a2f3b49c33ece701f50d08ac25c7.tar.gz
topological sort: more fixes for cycle detection
-rw-r--r--yoyo/topologicalsort.py33
1 files changed, 18 insertions, 15 deletions
diff --git a/yoyo/topologicalsort.py b/yoyo/topologicalsort.py
index 6d4e592..f74944b 100644
--- a/yoyo/topologicalsort.py
+++ b/yoyo/topologicalsort.py
@@ -33,29 +33,32 @@ def topological_sort(
# Map blockers to the list of items they block
blocked_on: Dict[T, Set[T]] = defaultdict(set)
+ blocked: Set[T] = set()
+
while pqueue:
- if seen_since_last_change == len(pqueue):
+ if seen_since_last_change == len(pqueue) + len(blocked):
raise_cycle_error(ordering, pqueue, blocked_on)
_, n = heappop(pqueue)
- if all(d in output for d in dependency_graph.get(n, [])):
- changed = True
+ blockers = {d for d in dependency_graph.get(n, []) if d not in output}
+ if not blockers:
+ seen_since_last_change = 0
output.add(n)
+ if n in blocked:
+ blocked.remove(n)
yield n
- for blocked in blocked_on.pop(n, []):
- heappush(pqueue, (ordering[blocked], blocked))
+ for b in blocked_on.pop(n, []):
+ if not any(b in other for other in blocked_on.values()):
+ heappush(pqueue, (ordering[b], b))
else:
- changed = False
- for d in dependency_graph.get(n, []):
- if n not in blocked_on[d]:
- blocked_on[d].add(n)
- changed = True
- if changed:
- seen_since_last_change = 0
- else:
- seen_since_last_change += 1
-
+ if n in blocked:
+ seen_since_last_change += 1
+ else:
+ seen_since_last_change = 0
+ blocked.add(n)
+ for b in blockers:
+ blocked_on[b].add(n)
if blocked_on:
raise_cycle_error(ordering, pqueue, blocked_on)