class PriorityQueue(object):
|
|
"""A priority queue implemented using multiple internal queues (typically,
|
|
FIFO queues). The internal queue must implement the following methods:
|
|
|
|
* push(obj)
|
|
* pop()
|
|
* close()
|
|
* __len__()
|
|
|
|
The constructor receives a qfactory argument, which is a callable used to
|
|
instantiate a new (internal) queue when a new priority is allocated. The
|
|
qfactory function is called with the priority number as first and only
|
|
argument.
|
|
|
|
Only integer priorities should be used. Lower numbers are higher
|
|
priorities.
|
|
|
|
startprios is a sequence of priorities to start with. If the queue was
|
|
previously closed leaving some priority buckets non-empty, those priorities
|
|
should be passed in startprios.
|
|
|
|
"""
|
|
|
|
def __init__(self, qfactory, startprios=()):
|
|
self.queues = {}
|
|
self.qfactory = qfactory
|
|
for p in startprios:
|
|
self.queues[p] = self.qfactory(p)
|
|
self.curprio = min(startprios) if startprios else None
|
|
|
|
def push(self, obj, priority=0):
|
|
if priority not in self.queues:
|
|
self.queues[priority] = self.qfactory(priority)
|
|
q = self.queues[priority]
|
|
q.push(obj) # this may fail (eg. serialization error)
|
|
if self.curprio is None or priority < self.curprio:
|
|
self.curprio = priority
|
|
|
|
def pop(self):
|
|
if self.curprio is None:
|
|
return
|
|
q = self.queues[self.curprio]
|
|
m = q.pop()
|
|
if len(q) == 0:
|
|
del self.queues[self.curprio]
|
|
q.close()
|
|
prios = [p for p, q in self.queues.items() if len(q) > 0]
|
|
self.curprio = min(prios) if prios else None
|
|
return m
|
|
|
|
def close(self):
|
|
active = []
|
|
for p, q in self.queues.items():
|
|
if len(q):
|
|
active.append(p)
|
|
q.close()
|
|
return active
|
|
|
|
def __len__(self):
|
|
return sum(len(x) for x in self.queues.values()) if self.queues else 0
|