Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 440a702

Browse files
committed
Add asyncio queue shutdown
* Include docs
1 parent f508800 commit 440a702

3 files changed

Lines changed: 145 additions & 1 deletion

File tree

Doc/library/asyncio-queue.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ Queue
6262
Remove and return an item from the queue. If queue is empty,
6363
wait until an item is available.
6464

65+
Raises :exc:`QueueShutDown` if the queue has been shut down and
66+
is empty, or if the queue has been shut down immediately.
67+
6568
.. method:: get_nowait()
6669

6770
Return an item if one is immediately available, else raise
@@ -77,11 +80,16 @@ Queue
7780
work on it is complete. When the count of unfinished tasks drops
7881
to zero, :meth:`join` unblocks.
7982

83+
Raises :exc:`QueueShutDown` if the queue has been shut down
84+
immediately.
85+
8086
.. coroutinemethod:: put(item)
8187

8288
Put an item into the queue. If the queue is full, wait until a
8389
free slot is available before adding the item.
8490

91+
Raises :exc:`QueueShutDown` if the queue has been shut down.
92+
8593
.. method:: put_nowait(item)
8694

8795
Put an item into the queue without blocking.
@@ -92,6 +100,19 @@ Queue
92100

93101
Return the number of items in the queue.
94102

103+
.. method:: shutdown(immediate=False)
104+
105+
Shut-down the queue, making queue gets and puts raise
106+
:exc:`QueueShutDown`.
107+
108+
By default, gets will only raise once the queue is empty. Set
109+
*immediate* to true to make gets raise immediately instead.
110+
111+
All blocked callers of put() will be unblocked, and also get()
112+
and join() if *immediate* is true.
113+
114+
.. versionadded:: 3.12
115+
95116
.. method:: task_done()
96117

97118
Indicate that a formerly enqueued task is complete.
@@ -108,6 +129,9 @@ Queue
108129
Raises :exc:`ValueError` if called more times than there were
109130
items placed in the queue.
110131

132+
Raises :exc:`QueueShutDown` if the queue has been shut down
133+
immediately.
134+
111135

112136
Priority Queue
113137
==============
@@ -145,6 +169,14 @@ Exceptions
145169
on a queue that has reached its *maxsize*.
146170

147171

172+
.. exception:: QueueShutDown
173+
174+
Exception raised when getting an item from or putting an item onto a
175+
queue which has been shut down.
176+
177+
.. versionadded:: 3.12
178+
179+
148180
Examples
149181
========
150182

Lib/asyncio/queues.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
__all__ = ('Queue', 'PriorityQueue', 'LifoQueue', 'QueueFull', 'QueueEmpty')
1+
__all__ = (
2+
'Queue',
3+
'PriorityQueue',
4+
'LifoQueue',
5+
'QueueFull',
6+
'QueueEmpty',
7+
'QueueShutDown',
8+
)
29

310
import collections
411
import heapq
@@ -18,6 +25,16 @@ class QueueFull(Exception):
1825
pass
1926

2027

28+
class QueueShutDown(Exception):
29+
"""Raised when putting on to or getting from a shut-down Queue."""
30+
pass
31+
32+
33+
_queue_alive = "alive"
34+
_queue_shutdown = "shutdown"
35+
_queue_shutdown_immediate = "shutdown-immediate"
36+
37+
2138
class Queue(mixins._LoopBoundMixin):
2239
"""A queue, useful for coordinating producer and consumer coroutines.
2340
@@ -41,6 +58,7 @@ def __init__(self, maxsize=0):
4158
self._finished = locks.Event()
4259
self._finished.set()
4360
self._init(maxsize)
61+
self.shutdown_state = _queue_alive
4462

4563
# These three are overridable in subclasses.
4664

@@ -113,6 +131,8 @@ async def put(self, item):
113131
Put an item into the queue. If the queue is full, wait until a free
114132
slot is available before adding item.
115133
"""
134+
if self.shutdown_state != _queue_alive:
135+
raise QueueShutDown
116136
while self.full():
117137
putter = self._get_loop().create_future()
118138
self._putters.append(putter)
@@ -132,13 +152,17 @@ async def put(self, item):
132152
# the call. Wake up the next in line.
133153
self._wakeup_next(self._putters)
134154
raise
155+
if self.shutdown_state != _queue_alive:
156+
raise QueueShutDown
135157
return self.put_nowait(item)
136158

137159
def put_nowait(self, item):
138160
"""Put an item into the queue without blocking.
139161
140162
If no free slot is immediately available, raise QueueFull.
141163
"""
164+
if self.shutdown_state != _queue_alive:
165+
raise QueueShutDown
142166
if self.full():
143167
raise QueueFull
144168
self._put(item)
@@ -151,7 +175,11 @@ async def get(self):
151175
152176
If queue is empty, wait until an item is available.
153177
"""
178+
if self.shutdown_state == _queue_shutdown_immediate:
179+
raise QueueShutDown
154180
while self.empty():
181+
if self.shutdown_state != _queue_alive:
182+
raise QueueShutDown
155183
getter = self._get_loop().create_future()
156184
self._getters.append(getter)
157185
try:
@@ -170,6 +198,8 @@ async def get(self):
170198
# the call. Wake up the next in line.
171199
self._wakeup_next(self._getters)
172200
raise
201+
if self.shutdown_state == _queue_shutdown_immediate:
202+
raise QueueShutDown
173203
return self.get_nowait()
174204

175205
def get_nowait(self):
@@ -178,7 +208,11 @@ def get_nowait(self):
178208
Return an item if one is immediately available, else raise QueueEmpty.
179209
"""
180210
if self.empty():
211+
if self.shutdown_state != _queue_alive:
212+
raise QueueShutDown
181213
raise QueueEmpty
214+
elif self.shutdown_state == _queue_shutdown_immediate:
215+
raise QueueShutDown
182216
item = self._get()
183217
self._wakeup_next(self._putters)
184218
return item
@@ -214,6 +248,27 @@ async def join(self):
214248
if self._unfinished_tasks > 0:
215249
await self._finished.wait()
216250

251+
def shutdown(self, immediate=False):
252+
"""Shut-down the queue, making queue gets and puts raise.
253+
254+
By default, gets will only raise once the queue is empty. Set
255+
'immediate' to True to make gets raise immediately instead.
256+
257+
All blocked callers of put() will be unblocked, and also get()
258+
and join() if 'immediate'. The QueueShutDown exception is raised.
259+
"""
260+
if immediate:
261+
self.shutdown_state = _queue_shutdown_immediate
262+
while self._getters:
263+
getter = self._getters.popleft()
264+
if not getter.done():
265+
getter.set_result(None)
266+
else:
267+
self.shutdown_state = _queue_shutdown
268+
while self._putters:
269+
putter = self._putters.popleft()
270+
if not putter.done():
271+
putter.set_result(None)
217272

218273
class PriorityQueue(Queue):
219274
"""A subclass of Queue; retrieves entries in priority order (lowest first).

Lib/test/test_asyncio/test_queues.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,5 +522,62 @@ class PriorityQueueJoinTests(_QueueJoinTestMixin, unittest.IsolatedAsyncioTestCa
522522
q_class = asyncio.PriorityQueue
523523

524524

525+
class _QueueShutdownTestMixin:
526+
q_class = None
527+
528+
async def test_empty(self):
529+
q = self.q_class()
530+
q.shutdown()
531+
try:
532+
await q.put("data")
533+
self.fail("Didn't appear to shut-down queue")
534+
except asyncio.QueueShutDown:
535+
pass
536+
try:
537+
await q.get()
538+
self.fail("Didn't appear to shut-down queue")
539+
except asyncio.QueueShutDown:
540+
pass
541+
542+
async def test_nonempty(self):
543+
q = self.q_class()
544+
q.put_nowait("data")
545+
q.shutdown()
546+
await q.get()
547+
try:
548+
await q.get()
549+
self.fail("Didn't appear to shut-down queue")
550+
except asyncio.QueueShutDown:
551+
pass
552+
553+
async def test_immediate(self):
554+
q = self.q_class()
555+
q.put_nowait("data")
556+
q.shutdown(immediate=True)
557+
try:
558+
await q.get()
559+
self.fail("Didn't appear to shut-down queue")
560+
except asyncio.QueueShutDown:
561+
pass
562+
563+
564+
class QueueShutdownTests(
565+
_QueueShutdownTestMixin, unittest.IsolatedAsyncioTestCase
566+
):
567+
q_class = asyncio.Queue
568+
569+
570+
class LifoQueueShutdownTests(
571+
_QueueShutdownTestMixin, unittest.IsolatedAsyncioTestCase
572+
):
573+
q_class = asyncio.LifoQueue
574+
575+
576+
class PriorityQueueShutdownTests(
577+
_QueueShutdownTestMixin, unittest.IsolatedAsyncioTestCase
578+
):
579+
q_class = asyncio.PriorityQueue
580+
581+
525582
if __name__ == '__main__':
526583
unittest.main()

0 commit comments

Comments
 (0)