1
- __all__ = ('Queue' , 'PriorityQueue' , 'LifoQueue' , 'QueueFull' , 'QueueEmpty' )
1
+ __all__ = (
2
+ 'Queue' ,
3
+ 'PriorityQueue' ,
4
+ 'LifoQueue' ,
5
+ 'QueueFull' ,
6
+ 'QueueEmpty' ,
7
+ 'QueueShutDown' ,
8
+ )
2
9
3
10
import collections
4
11
import heapq
@@ -18,6 +25,11 @@ class QueueFull(Exception):
18
25
pass
19
26
20
27
28
+ class QueueShutDown (Exception ):
29
+ """Raised when putting on to or getting from a shut-down Queue."""
30
+ pass
31
+
32
+
21
33
class Queue (mixins ._LoopBoundMixin ):
22
34
"""A queue, useful for coordinating producer and consumer coroutines.
23
35
@@ -41,6 +53,7 @@ def __init__(self, maxsize=0):
41
53
self ._finished = locks .Event ()
42
54
self ._finished .set ()
43
55
self ._init (maxsize )
56
+ self ._is_shutdown = False
44
57
45
58
# These three are overridable in subclasses.
46
59
@@ -81,6 +94,8 @@ def _format(self):
81
94
result += f' _putters[{ len (self ._putters )} ]'
82
95
if self ._unfinished_tasks :
83
96
result += f' tasks={ self ._unfinished_tasks } '
97
+ if self ._is_shutdown :
98
+ result += ' shutdown'
84
99
return result
85
100
86
101
def qsize (self ):
@@ -112,8 +127,12 @@ async def put(self, item):
112
127
113
128
Put an item into the queue. If the queue is full, wait until a free
114
129
slot is available before adding item.
130
+
131
+ Raises QueueShutDown if the queue has been shut down.
115
132
"""
116
133
while self .full ():
134
+ if self ._is_shutdown :
135
+ raise QueueShutDown
117
136
putter = self ._get_loop ().create_future ()
118
137
self ._putters .append (putter )
119
138
try :
@@ -125,7 +144,7 @@ async def put(self, item):
125
144
self ._putters .remove (putter )
126
145
except ValueError :
127
146
# The putter could be removed from self._putters by a
128
- # previous get_nowait call.
147
+ # previous get_nowait call or a shutdown call .
129
148
pass
130
149
if not self .full () and not putter .cancelled ():
131
150
# We were woken up by get_nowait(), but can't take
@@ -138,7 +157,11 @@ def put_nowait(self, item):
138
157
"""Put an item into the queue without blocking.
139
158
140
159
If no free slot is immediately available, raise QueueFull.
160
+
161
+ Raises QueueShutDown if the queue has been shut down.
141
162
"""
163
+ if self ._is_shutdown :
164
+ raise QueueShutDown
142
165
if self .full ():
143
166
raise QueueFull
144
167
self ._put (item )
@@ -150,8 +173,13 @@ async def get(self):
150
173
"""Remove and return an item from the queue.
151
174
152
175
If queue is empty, wait until an item is available.
176
+
177
+ Raises QueueShutDown if the queue has been shut down and is empty, or
178
+ if the queue has been shut down immediately.
153
179
"""
154
180
while self .empty ():
181
+ if self ._is_shutdown and self .empty ():
182
+ raise QueueShutDown
155
183
getter = self ._get_loop ().create_future ()
156
184
self ._getters .append (getter )
157
185
try :
@@ -163,7 +191,7 @@ async def get(self):
163
191
self ._getters .remove (getter )
164
192
except ValueError :
165
193
# The getter could be removed from self._getters by a
166
- # previous put_nowait call.
194
+ # previous put_nowait call, or a shutdown call .
167
195
pass
168
196
if not self .empty () and not getter .cancelled ():
169
197
# We were woken up by put_nowait(), but can't take
@@ -176,8 +204,13 @@ def get_nowait(self):
176
204
"""Remove and return an item from the queue.
177
205
178
206
Return an item if one is immediately available, else raise QueueEmpty.
207
+
208
+ Raises QueueShutDown if the queue has been shut down and is empty, or
209
+ if the queue has been shut down immediately.
179
210
"""
180
211
if self .empty ():
212
+ if self ._is_shutdown :
213
+ raise QueueShutDown
181
214
raise QueueEmpty
182
215
item = self ._get ()
183
216
self ._wakeup_next (self ._putters )
@@ -194,6 +227,9 @@ def task_done(self):
194
227
been processed (meaning that a task_done() call was received for every
195
228
item that had been put() into the queue).
196
229
230
+ shutdown(immediate=True) calls task_done() for each remaining item in
231
+ the queue.
232
+
197
233
Raises ValueError if called more times than there were items placed in
198
234
the queue.
199
235
"""
@@ -214,6 +250,32 @@ async def join(self):
214
250
if self ._unfinished_tasks > 0 :
215
251
await self ._finished .wait ()
216
252
253
+ def shutdown (self , immediate = False ):
254
+ """Shut-down the queue, making queue gets and puts raise QueueShutDown.
255
+
256
+ By default, gets will only raise once the queue is empty. Set
257
+ 'immediate' to True to make gets raise immediately instead.
258
+
259
+ All blocked callers of put() will be unblocked, and also get()
260
+ and join() if 'immediate'.
261
+ """
262
+ self ._is_shutdown = True
263
+ if immediate :
264
+ while not self .empty ():
265
+ self ._get ()
266
+ if self ._unfinished_tasks > 0 :
267
+ self ._unfinished_tasks -= 1
268
+ if self ._unfinished_tasks == 0 :
269
+ self ._finished .set ()
270
+ while self ._getters :
271
+ getter = self ._getters .popleft ()
272
+ if not getter .done ():
273
+ getter .set_result (None )
274
+ while self ._putters :
275
+ putter = self ._putters .popleft ()
276
+ if not putter .done ():
277
+ putter .set_result (None )
278
+
217
279
218
280
class PriorityQueue (Queue ):
219
281
"""A subclass of Queue; retrieves entries in priority order (lowest first).
0 commit comments