1
1
from __future__ import annotations
2
2
3
3
import heapq
4
+ import itertools
4
5
from collections import defaultdict
5
6
from collections .abc import Iterable
6
7
from dataclasses import dataclass , field
@@ -94,30 +95,29 @@ class PriorityQueue:
94
95
"""
95
96
96
97
_REMOVED = "<removed>" # Mark removed items
97
- _UPDATED = "<updated>" # Mark updated items
98
98
99
99
def __init__ (self ):
100
100
self ._entries : list [QueueEntry ] = []
101
101
self ._entry_finder : dict [MigrationNode , QueueEntry ] = {}
102
- self ._counter = 0 # Tiebreaker with equal priorities, then "first in, first out"
102
+ self ._counter = itertools . count () # Tiebreaker with equal priorities, then "first in, first out"
103
103
104
104
def put (self , priority : int , task : MigrationNode ) -> None :
105
105
"""Put or update task in the queue.
106
106
107
107
The lowest priority is retrieved from the queue first.
108
108
"""
109
109
if task in self ._entry_finder :
110
- raise KeyError (f"Use `:meth:update` to update existing task: { task } " )
111
- entry : QueueEntry = [priority , self ._counter , task ]
110
+ self ._remove (task )
111
+ count = next (self ._counter )
112
+ entry = [priority , count , task ]
112
113
self ._entry_finder [task ] = entry
113
114
heapq .heappush (self ._entries , entry )
114
- self ._counter += 1
115
115
116
116
def get (self ) -> MigrationNode | None :
117
117
"""Gets the tasks with lowest priority."""
118
118
while self ._entries :
119
119
_ , _ , task = heapq .heappop (self ._entries )
120
- if task in ( self ._REMOVED , self . _UPDATED ) :
120
+ if task == self ._REMOVED :
121
121
continue
122
122
assert isinstance (task , MigrationNode )
123
123
self ._remove (task )
@@ -130,15 +130,6 @@ def _remove(self, task: MigrationNode) -> None:
130
130
entry = self ._entry_finder .pop (task )
131
131
entry [2 ] = self ._REMOVED
132
132
133
- def update (self , priority : int , task : MigrationNode ) -> None :
134
- """Update a task in the queue."""
135
- entry = self ._entry_finder .pop (task )
136
- if entry is None :
137
- raise KeyError (f"Cannot update unknown task: { task } " )
138
- if entry [2 ] != self ._REMOVED : # Do not update REMOVED tasks
139
- entry [2 ] = self ._UPDATED
140
- self .put (priority , task )
141
-
142
133
143
134
class MigrationSequencer :
144
135
"""Sequence the migration dependencies in order to execute the migration.
@@ -244,8 +235,10 @@ def generate_steps(self) -> Iterable[MigrationStep]:
244
235
seen .add (node )
245
236
# Update the queue priority as if the migration step was completed
246
237
for dependency in self ._outgoing [node .key ]:
238
+ if dependency in seen :
239
+ continue
247
240
priority = len (incoming [dependency .key ] - seen )
248
- queue .update (priority , dependency )
241
+ queue .put (priority , dependency )
249
242
node = queue .get ()
250
243
return ordered_steps
251
244
0 commit comments