kahn-queue 0.1.1__tar.gz → 1.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {kahn_queue-0.1.1/src/kahn_queue.egg-info → kahn_queue-1.0.1}/PKG-INFO +33 -4
  2. kahn_queue-0.1.1/PKG-INFO → kahn_queue-1.0.1/README.md +29 -11
  3. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/pyproject.toml +16 -4
  4. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/kahnQueue/concurrent_kahn_queue.py +11 -12
  5. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/kahnQueue/default_kahn_queue.py +10 -10
  6. kahn_queue-0.1.1/README.md → kahn_queue-1.0.1/src/kahn_queue.egg-info/PKG-INFO +40 -0
  7. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/tests/test_concurrent_kahn_queue.py +26 -26
  8. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/tests/test_default_kahn_queue.py +15 -15
  9. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/setup.cfg +0 -0
  10. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/dag.py +0 -0
  11. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/exception.py +0 -0
  12. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/kahnQueue/__init__.py +0 -0
  13. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/kahnQueue/kahn_queue.py +0 -0
  14. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/kahnQueue/node_machine.py +0 -0
  15. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/kahnQueue/node_state.py +0 -0
  16. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/kahn_queue.egg-info/SOURCES.txt +0 -0
  17. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/kahn_queue.egg-info/dependency_links.txt +0 -0
  18. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/kahn_queue.egg-info/top_level.txt +0 -0
  19. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/scheduler.py +0 -0
  20. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/tracker.py +0 -0
  21. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/utils/__init__.py +0 -0
  22. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/src/utils/state_machine.py +0 -0
  23. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/tests/test_dag.py +0 -0
  24. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/tests/test_kahn_scheduler.py +0 -0
  25. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/tests/test_node_machine.py +0 -0
  26. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/tests/test_node_progress_tracker.py +0 -0
  27. {kahn_queue-0.1.1 → kahn_queue-1.0.1}/tests/test_state_machine.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kahn-queue
3
- Version: 0.1.1
4
- Summary: Kahn-style ready-queue for dependency scheduling
3
+ Version: 1.0.1
4
+ Summary: Lightweight Kahn-based ready queue for dependency-driven scheduling and workflows
5
5
  License: MIT
6
- Project-URL: Homepage, https://github.com/Flashlock/kahn-queue
6
+ Project-URL: Homepage, https://flashlock.github.io/kahn-queue/
7
7
  Project-URL: Repository, https://github.com/Flashlock/kahn-queue
8
- Keywords: dag,scheduler,workflow,kahn
8
+ Keywords: kahn queue,kahn-style scheduler,topological sort queue,dynamic topological sort,ready queue,dependency scheduler,dag scheduler,incremental topological sort,agent orchestration,parallel task scheduling,deterministic scheduler
9
9
  Requires-Python: >=3.10
10
10
  Description-Content-Type: text/markdown
11
11
 
@@ -84,3 +84,32 @@ result = sched.get_result()
84
84
  # result.completed, result.failed, result.pruned — frozensets of ids
85
85
  done = sched.is_finished
86
86
  ```
87
+
88
+ ### Manual KahnQueue
89
+
90
+ ```python
91
+ from dag import Dag
92
+ from kahnQueue.default_kahn_queue import DefaultKahnQueue
93
+
94
+ b = Dag.builder()
95
+ lint = b.add("lint")
96
+ compile = b.add("compile")
97
+ test = b.add("test")
98
+ b.connect(lint, compile).connect(compile, test)
99
+ dag = b.build()
100
+
101
+ q = DefaultKahnQueue(dag)
102
+
103
+ ready = list(q.ready_ids())
104
+
105
+ while ready:
106
+ id_ = ready.pop(0)
107
+
108
+ # do work for `id_` (e.g. run_step(dag[id_]))
109
+
110
+ promoted = q.pop(id_)
111
+ ready.extend(promoted)
112
+
113
+ # If a node fails, you can prune it (and its descendants):
114
+ # q.prune(id_)
115
+ ```
@@ -1,14 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: kahn-queue
3
- Version: 0.1.1
4
- Summary: Kahn-style ready-queue for dependency scheduling
5
- License: MIT
6
- Project-URL: Homepage, https://github.com/Flashlock/kahn-queue
7
- Project-URL: Repository, https://github.com/Flashlock/kahn-queue
8
- Keywords: dag,scheduler,workflow,kahn
9
- Requires-Python: >=3.10
10
- Description-Content-Type: text/markdown
11
-
12
1
  # kahn-queue (Python)
13
2
 
14
3
  ## Getting started
@@ -84,3 +73,32 @@ result = sched.get_result()
84
73
  # result.completed, result.failed, result.pruned — frozensets of ids
85
74
  done = sched.is_finished
86
75
  ```
76
+
77
+ ### Manual KahnQueue
78
+
79
+ ```python
80
+ from dag import Dag
81
+ from kahnQueue.default_kahn_queue import DefaultKahnQueue
82
+
83
+ b = Dag.builder()
84
+ lint = b.add("lint")
85
+ compile = b.add("compile")
86
+ test = b.add("test")
87
+ b.connect(lint, compile).connect(compile, test)
88
+ dag = b.build()
89
+
90
+ q = DefaultKahnQueue(dag)
91
+
92
+ ready = list(q.ready_ids())
93
+
94
+ while ready:
95
+ id_ = ready.pop(0)
96
+
97
+ # do work for `id_` (e.g. run_step(dag[id_]))
98
+
99
+ promoted = q.pop(id_)
100
+ ready.extend(promoted)
101
+
102
+ # If a node fails, you can prune it (and its descendants):
103
+ # q.prune(id_)
104
+ ```
@@ -4,15 +4,27 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "kahn-queue"
7
- version = "0.1.1"
8
- description = "Kahn-style ready-queue for dependency scheduling"
7
+ version = "1.0.1"
8
+ description = "Lightweight Kahn-based ready queue for dependency-driven scheduling and workflows"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  license = { text = "MIT" }
12
- keywords = ["dag", "scheduler", "workflow", "kahn"]
12
+ keywords = [
13
+ "kahn queue",
14
+ "kahn-style scheduler",
15
+ "topological sort queue",
16
+ "dynamic topological sort",
17
+ "ready queue",
18
+ "dependency scheduler",
19
+ "dag scheduler",
20
+ "incremental topological sort",
21
+ "agent orchestration",
22
+ "parallel task scheduling",
23
+ "deterministic scheduler",
24
+ ]
13
25
 
14
26
  [project.urls]
15
- Homepage = "https://github.com/Flashlock/kahn-queue"
27
+ Homepage = "https://flashlock.github.io/kahn-queue/"
16
28
  Repository = "https://github.com/Flashlock/kahn-queue"
17
29
 
18
30
  # Layout: packages under src/ plus top-level modules (dag, scheduler, …).
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from threading import RLock
4
- from typing import List, Set
4
+ from typing import List
5
5
 
6
6
  from dag import Dag, validate_node
7
7
  from kahnQueue.node_machine import NodeMachine
@@ -25,7 +25,7 @@ class ConcurrentKahnQueue(KahnQueue):
25
25
  ]
26
26
  self._locks = [RLock() for _ in range(dag.size())]
27
27
 
28
- def pop(self, id: int) -> Set[int]:
28
+ def pop(self, id: int) -> List[int]:
29
29
  validate_node(id, self._dag.size())
30
30
 
31
31
  with self._locks[id]:
@@ -35,7 +35,7 @@ class ConcurrentKahnQueue(KahnQueue):
35
35
 
36
36
  machine.transition(NodeState.COMPLETE)
37
37
 
38
- promoted: Set[int] = set()
38
+ promoted: set[int] = set()
39
39
  for cid in self._dag.targets(id):
40
40
  with self._locks[cid]:
41
41
  child = self._node_machines[cid]
@@ -44,12 +44,12 @@ class ConcurrentKahnQueue(KahnQueue):
44
44
  if child.can_transition(NodeState.ACTIVE):
45
45
  child.transition(NodeState.ACTIVE)
46
46
  promoted.add(cid)
47
- return promoted
47
+ return sorted(promoted)
48
48
 
49
- def prune(self, id: int) -> Set[int]:
49
+ def prune(self, id: int) -> List[int]:
50
50
  validate_node(id, self._dag.size())
51
51
 
52
- affected: Set[int] = set()
52
+ affected: set[int] = set()
53
53
  stack: List[int] = [id]
54
54
 
55
55
  while stack:
@@ -66,10 +66,9 @@ class ConcurrentKahnQueue(KahnQueue):
66
66
  # Adding targets outside the lock is safe as the DAG structure is immutable
67
67
  stack.extend(self._dag.targets(curr))
68
68
 
69
- return affected
69
+ return sorted(affected)
70
70
 
71
- def ready_ids(self) -> Set[int]:
72
- return {
73
- m.id for m in self._node_machines
74
- if m.is_(NodeState.READY)
75
- }
71
+ def ready_ids(self) -> List[int]:
72
+ # Deterministic ordering for sequential callers; may not reflect a consistent snapshot
73
+ # under concurrent updates.
74
+ return [m.id for m in self._node_machines if m.is_(NodeState.READY)]
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections import deque
4
- from typing import List, Set
3
+ from typing import List
5
4
 
6
5
  from dag import Dag, validate_node
7
6
  from kahnQueue.node_machine import NodeMachine
@@ -17,7 +16,7 @@ class DefaultKahnQueue(KahnQueue):
17
16
  NodeMachine.create(i, dag.in_degree(i)) for i in range(dag.size())
18
17
  ]
19
18
 
20
- def pop(self, id: int) -> Set[int]:
19
+ def pop(self, id: int) -> List[int]:
21
20
  validate_node(id, self._dag.size())
22
21
  machine = self._node_machines[id]
23
22
 
@@ -26,7 +25,7 @@ class DefaultKahnQueue(KahnQueue):
26
25
 
27
26
  machine.transition(NodeState.COMPLETE)
28
27
 
29
- promoted: Set[int] = set()
28
+ promoted: List[int] = []
30
29
  for cid in self._dag.targets(id):
31
30
  child = self._node_machines[cid]
32
31
  child.decrement()
@@ -34,12 +33,12 @@ class DefaultKahnQueue(KahnQueue):
34
33
  # in a single-threaded queue.
35
34
  if child.is_(NodeState.READY):
36
35
  child.transition(NodeState.ACTIVE)
37
- promoted.add(cid)
36
+ promoted.append(cid)
38
37
  return promoted
39
38
 
40
- def prune(self, id: int) -> Set[int]:
39
+ def prune(self, id: int) -> List[int]:
41
40
  validate_node(id, self._dag.size())
42
- affected: Set[int] = set()
41
+ affected: set[int] = set()
43
42
  stack: List[int] = [id]
44
43
 
45
44
  while stack:
@@ -54,7 +53,8 @@ class DefaultKahnQueue(KahnQueue):
54
53
  affected.add(curr)
55
54
  stack.extend(self._dag.targets(curr))
56
55
 
57
- return affected
56
+ return sorted(affected)
58
57
 
59
- def ready_ids(self) -> Set[int]:
60
- return {m.id for m in self._node_machines if m.is_(NodeState.READY)}
58
+ def ready_ids(self) -> List[int]:
59
+ # Deterministic: ids are scanned in ascending order.
60
+ return [m.id for m in self._node_machines if m.is_(NodeState.READY)]
@@ -1,3 +1,14 @@
1
+ Metadata-Version: 2.4
2
+ Name: kahn-queue
3
+ Version: 1.0.1
4
+ Summary: Lightweight Kahn-based ready queue for dependency-driven scheduling and workflows
5
+ License: MIT
6
+ Project-URL: Homepage, https://flashlock.github.io/kahn-queue/
7
+ Project-URL: Repository, https://github.com/Flashlock/kahn-queue
8
+ Keywords: kahn queue,kahn-style scheduler,topological sort queue,dynamic topological sort,ready queue,dependency scheduler,dag scheduler,incremental topological sort,agent orchestration,parallel task scheduling,deterministic scheduler
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+
1
12
  # kahn-queue (Python)
2
13
 
3
14
  ## Getting started
@@ -73,3 +84,32 @@ result = sched.get_result()
73
84
  # result.completed, result.failed, result.pruned — frozensets of ids
74
85
  done = sched.is_finished
75
86
  ```
87
+
88
+ ### Manual KahnQueue
89
+
90
+ ```python
91
+ from dag import Dag
92
+ from kahnQueue.default_kahn_queue import DefaultKahnQueue
93
+
94
+ b = Dag.builder()
95
+ lint = b.add("lint")
96
+ compile = b.add("compile")
97
+ test = b.add("test")
98
+ b.connect(lint, compile).connect(compile, test)
99
+ dag = b.build()
100
+
101
+ q = DefaultKahnQueue(dag)
102
+
103
+ ready = list(q.ready_ids())
104
+
105
+ while ready:
106
+ id_ = ready.pop(0)
107
+
108
+ # do work for `id_` (e.g. run_step(dag[id_]))
109
+
110
+ promoted = q.pop(id_)
111
+ ready.extend(promoted)
112
+
113
+ # If a node fails, you can prune it (and its descendants):
114
+ # q.prune(id_)
115
+ ```
@@ -17,7 +17,7 @@ def _force_node_state(q: ConcurrentKahnQueue, id_: int, state: NodeState) -> Non
17
17
  def test_basics_ready_ids_and_pop_validation():
18
18
  empty = Dag.builder().build()
19
19
  q0 = ConcurrentKahnQueue(empty)
20
- assert q0.ready_ids() == set()
20
+ assert q0.ready_ids() == []
21
21
  with pytest.raises(IndexError):
22
22
  q0.pop(0)
23
23
 
@@ -28,7 +28,7 @@ def test_basics_ready_ids_and_pop_validation():
28
28
  chain.connect(root, mid).connect(mid, leaf)
29
29
  dag_chain = chain.build()
30
30
  qc = ConcurrentKahnQueue(dag_chain)
31
- assert qc.ready_ids() == {root}
31
+ assert qc.ready_ids() == [root]
32
32
 
33
33
  join = Dag.builder()
34
34
  a = join.add("a")
@@ -37,13 +37,13 @@ def test_basics_ready_ids_and_pop_validation():
37
37
  join.connect(a, jn).connect(c, jn)
38
38
  dag_join = join.build()
39
39
  qj = ConcurrentKahnQueue(dag_join)
40
- assert qj.ready_ids() == {a, c}
40
+ assert qj.ready_ids() == [a, c]
41
41
 
42
42
  one = Dag.builder()
43
43
  only = one.add("x")
44
44
  dag_one = one.build()
45
45
  q1 = ConcurrentKahnQueue(dag_one)
46
- assert q1.ready_ids() == {only}
46
+ assert q1.ready_ids() == [only]
47
47
  with pytest.raises(ValueError) as ex:
48
48
  q1.pop(only)
49
49
  assert "Pop failed. Node" in str(ex.value)
@@ -64,7 +64,7 @@ def test_prune_collects_reachable_in_linear_and_fork_shapes():
64
64
  l = b1.add("l")
65
65
  b1.connect(r, m).connect(m, l)
66
66
  q1 = ConcurrentKahnQueue(b1.build())
67
- assert q1.prune(r) == {r, m, l}
67
+ assert set(q1.prune(r)) == {r, m, l}
68
68
 
69
69
  b2 = Dag.builder()
70
70
  root = b2.add("root")
@@ -72,7 +72,7 @@ def test_prune_collects_reachable_in_linear_and_fork_shapes():
72
72
  right = b2.add("right")
73
73
  b2.connect(root, left).connect(root, right)
74
74
  q2 = ConcurrentKahnQueue(b2.build())
75
- assert q2.prune(root) == {root, left, right}
75
+ assert set(q2.prune(root)) == {root, left, right}
76
76
 
77
77
 
78
78
  def test_prune_updates_ready_set():
@@ -83,9 +83,9 @@ def test_prune_updates_ready_set():
83
83
  b.connect(a, join).connect(c, join)
84
84
  dag = b.build()
85
85
  q = ConcurrentKahnQueue(dag)
86
- assert q.ready_ids() == {a, c}
86
+ assert q.ready_ids() == [a, c]
87
87
  q.prune(a)
88
- assert q.ready_ids() == {c}
88
+ assert q.ready_ids() == [c]
89
89
 
90
90
 
91
91
  def test_ready_ids_stable_under_sequential_repeats():
@@ -108,10 +108,10 @@ def test_prune_second_call_throws():
108
108
  r = b.add("r")
109
109
  dag = b.build()
110
110
  q = ConcurrentKahnQueue(dag)
111
- assert q.prune(r) == {r}
112
- assert q.ready_ids() == set()
111
+ assert q.prune(r) == [r]
112
+ assert q.ready_ids() == []
113
113
  # ConcurrentKahnQueue.prune is idempotent for already-pruned branches.
114
- assert q.prune(r) == set()
114
+ assert q.prune(r) == []
115
115
  for id_ in q.ready_ids():
116
116
  Dag.validate_node(id_, dag.size())
117
117
 
@@ -125,10 +125,10 @@ def test_kahn_progression_pop_active_node_returns_promoted_dependents():
125
125
  dag = b.build()
126
126
  q = ConcurrentKahnQueue(dag)
127
127
  _force_node_state(q, root, NodeState.ACTIVE)
128
- assert q.pop(root) == {mid}
129
- assert q.pop(mid) == {leaf}
130
- assert q.pop(leaf) == set()
131
- assert q.ready_ids() == set()
128
+ assert q.pop(root) == [mid]
129
+ assert q.pop(mid) == [leaf]
130
+ assert q.pop(leaf) == []
131
+ assert q.ready_ids() == []
132
132
  for id_ in q.ready_ids():
133
133
  Dag.validate_node(id_, dag.size())
134
134
 
@@ -141,7 +141,7 @@ def test_concurrent_ready_ids_stress_reads_match_snapshot():
141
141
  b.connect(x, y)
142
142
  dag = b.build()
143
143
  q = ConcurrentKahnQueue(dag)
144
- expected = {x}
144
+ expected = [x]
145
145
  assert q.ready_ids() == expected
146
146
 
147
147
  def worker():
@@ -166,9 +166,9 @@ def test_concurrent_disjoint_prune():
166
166
  with concurrent.futures.ThreadPoolExecutor(max_workers=2) as pool:
167
167
  f1 = pool.submit(q.prune, left)
168
168
  f2 = pool.submit(q.prune, right)
169
- assert f1.result() == {left}
170
- assert f2.result() == {right}
171
- assert q.ready_ids() == set()
169
+ assert f1.result() == [left]
170
+ assert f2.result() == [right]
171
+ assert q.ready_ids() == []
172
172
 
173
173
 
174
174
  def test_concurrent_pop_failures_do_not_mutate_ready():
@@ -208,9 +208,9 @@ def test_concurrent_same_id_prune_contention():
208
208
  futures = [pool.submit(worker) for _ in range(8)]
209
209
  start.set()
210
210
  results = [f.result(timeout=30) for f in futures]
211
- assert results.count({root}) == 1
212
- assert results.count(set()) == 7
213
- assert q.ready_ids() == set()
211
+ assert results.count([root]) == 1
212
+ assert results.count([]) == 7
213
+ assert q.ready_ids() == []
214
214
 
215
215
 
216
216
  def test_concurrent_overlapping_prune():
@@ -240,9 +240,9 @@ def test_concurrent_overlapping_prune():
240
240
 
241
241
  assert set(s1) | set(s2) == {r, m, l}
242
242
 
243
- any_full = (s1 == {r, m, l}) or (s2 == {r, m, l})
243
+ any_full = (set(s1) == {r, m, l}) or (set(s2) == {r, m, l})
244
244
  if any_full:
245
- assert q.ready_ids() == set()
245
+ assert q.ready_ids() == []
246
246
 
247
247
 
248
248
  def test_prune_mutation_visible_to_other_thread_after_future_get():
@@ -253,8 +253,8 @@ def test_prune_mutation_visible_to_other_thread_after_future_get():
253
253
  q = ConcurrentKahnQueue(dag)
254
254
  with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
255
255
  done = pool.submit(q.prune, left)
256
- assert done.result(timeout=10) == {left}
257
- assert q.ready_ids() == {right}
256
+ assert done.result(timeout=10) == [left]
257
+ assert q.ready_ids() == [right]
258
258
 
259
259
 
260
260
  def _get_prune_result_or_illegal_state(fut: "concurrent.futures.Future[set[int]]"):
@@ -12,7 +12,7 @@ def _force_node_state(q: DefaultKahnQueue, id_: int, state: NodeState) -> None:
12
12
  def test_empty_dag_ready_ids_empty_and_pop_rejects_invalid_id():
13
13
  dag = Dag.builder().build()
14
14
  q = DefaultKahnQueue(dag)
15
- assert q.ready_ids() == set()
15
+ assert q.ready_ids() == []
16
16
  with pytest.raises(IndexError):
17
17
  q.pop(0)
18
18
 
@@ -25,7 +25,7 @@ def test_ready_ids_contains_only_zero_in_degree_nodes():
25
25
  b.connect(root, mid).connect(mid, leaf)
26
26
  dag = b.build()
27
27
  q = DefaultKahnQueue(dag)
28
- assert q.ready_ids() == {root}
28
+ assert q.ready_ids() == [root]
29
29
 
30
30
 
31
31
  def test_ready_ids_two_independent_roots():
@@ -36,7 +36,7 @@ def test_ready_ids_two_independent_roots():
36
36
  b.connect(a, join).connect(c, join)
37
37
  dag = b.build()
38
38
  q = DefaultKahnQueue(dag)
39
- assert q.ready_ids() == {a, c}
39
+ assert q.ready_ids() == [a, c]
40
40
 
41
41
 
42
42
  def test_pop_throws_when_node_is_ready_not_active():
@@ -44,7 +44,7 @@ def test_pop_throws_when_node_is_ready_not_active():
44
44
  only = b.add("x")
45
45
  dag = b.build()
46
46
  q = DefaultKahnQueue(dag)
47
- assert q.ready_ids() == {only}
47
+ assert q.ready_ids() == [only]
48
48
  with pytest.raises(ValueError) as ex:
49
49
  q.pop(only)
50
50
  assert "Pop failed. Node" in str(ex.value)
@@ -68,7 +68,7 @@ def test_prune_marks_root_and_reachable_descendants():
68
68
  b.connect(r, m).connect(m, l)
69
69
  dag = b.build()
70
70
  q = DefaultKahnQueue(dag)
71
- assert q.prune(r) == {r, m, l}
71
+ assert set(q.prune(r)) == {r, m, l}
72
72
 
73
73
 
74
74
  def test_prune_fork_collects_all_branches():
@@ -79,7 +79,7 @@ def test_prune_fork_collects_all_branches():
79
79
  b.connect(root, left).connect(root, right)
80
80
  dag = b.build()
81
81
  q = DefaultKahnQueue(dag)
82
- assert q.prune(root) == {root, left, right}
82
+ assert set(q.prune(root)) == {root, left, right}
83
83
 
84
84
 
85
85
  def test_prune_removes_ids_from_ready_set():
@@ -90,9 +90,9 @@ def test_prune_removes_ids_from_ready_set():
90
90
  b.connect(a, join).connect(c, join)
91
91
  dag = b.build()
92
92
  q = DefaultKahnQueue(dag)
93
- assert q.ready_ids() == {a, c}
93
+ assert q.ready_ids() == [a, c]
94
94
  q.prune(a)
95
- assert q.ready_ids() == {c}
95
+ assert q.ready_ids() == [c]
96
96
 
97
97
 
98
98
  def test_prune_second_call_throws():
@@ -100,10 +100,10 @@ def test_prune_second_call_throws():
100
100
  r = b.add("r")
101
101
  dag = b.build()
102
102
  q = DefaultKahnQueue(dag)
103
- assert q.prune(r) == {r}
104
- assert q.ready_ids() == set()
103
+ assert q.prune(r) == [r]
104
+ assert q.ready_ids() == []
105
105
  # DefaultKahnQueue.prune is idempotent for already-pruned branches.
106
- assert q.prune(r) == set()
106
+ assert q.prune(r) == []
107
107
 
108
108
 
109
109
  def test_kahn_progression_pop_active_node_returns_promoted_dependents():
@@ -116,8 +116,8 @@ def test_kahn_progression_pop_active_node_returns_promoted_dependents():
116
116
  q = DefaultKahnQueue(dag)
117
117
 
118
118
  _force_node_state(q, root, NodeState.ACTIVE)
119
- assert q.pop(root) == {mid}
120
- assert q.pop(mid) == {leaf}
121
- assert q.pop(leaf) == set()
122
- assert q.ready_ids() == set()
119
+ assert q.pop(root) == [mid]
120
+ assert q.pop(mid) == [leaf]
121
+ assert q.pop(leaf) == []
122
+ assert q.ready_ids() == []
123
123
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes