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

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
440a702
Add asyncio queue shutdown
EpicWink Sep 1, 2022
fb458db
Fix queue shutdown
YvesDup Feb 10, 2023
e5951ac
πŸ“œπŸ€– Added by blurb_it.
blurb-it[bot] May 6, 2023
a72aedd
Merge remote-tracking branch 'upstream/main' into asyncio-queue-shutdown
EpicWink Feb 20, 2024
d5e925d
Add references in docs and news entry
EpicWink Feb 20, 2024
f3517fb
Merge remote-tracking branch 'upstream/main' into asyncio-queue-shutdown
EpicWink Mar 20, 2024
bd2a7c3
Improve docs
EpicWink Mar 20, 2024
e9ac8de
Consume queue on immediate shutdown
EpicWink Mar 20, 2024
1e7813a
Fix links in what's-new
EpicWink Mar 22, 2024
1275bb6
Merge remote-tracking branch 'upstream/main' into asyncio-queue-shutdown
EpicWink Mar 22, 2024
eec29bb
Fix formatting in news entry
EpicWink Mar 22, 2024
2c6156f
Merge remote-tracking branch 'upstream/main' into asyncio-queue-shutdown
EpicWink Mar 22, 2024
17f1f32
Merge remote-tracking branch 'upstream/main' into asyncio-queue-shutdown
EpicWink Mar 26, 2024
a233830
Improve tests
EpicWink Mar 26, 2024
420a247
Improve tests even more
EpicWink Mar 26, 2024
25ad2ac
Merge remote-tracking branch 'upstream/main' into asyncio-queue-shutdown
EpicWink Mar 26, 2024
f3321b4
Merge remote-tracking branch 'upstream/main' into asyncio-queue-shutdown
EpicWink Mar 27, 2024
6d9edd6
Document tests
EpicWink Mar 27, 2024
1135d85
Merge remote-tracking branch 'upstream/main' into asyncio-queue-shutdown
EpicWink Mar 28, 2024
ddc6ad6
Always allow getters to re-check queue empty
EpicWink Mar 28, 2024
2fa1bd9
Merge branch 'main' into asyncio-queue-shutdown
gvanrossum Apr 3, 2024
aef4063
Simplify shutdown-check in put and get
EpicWink Apr 4, 2024
d49c6dd
Format shutdown docstring
EpicWink Apr 4, 2024
5a435a6
Check for 0 unfinised tasks in shutdown
EpicWink Apr 4, 2024
c8db40e
Use asyncio.sleep to run other tasks
EpicWink Apr 4, 2024
ca01ee1
Use public method to shut down queue in format test
EpicWink Apr 4, 2024
b02c4dd
Only start queue join after shutdown in test
EpicWink Apr 4, 2024
8deca77
Test join before failing task-done
EpicWink Apr 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix queue shutdown
* Queue state enum members are capitalised
* Termination state in str/repr
* Include raised exception in docstrings
* Factor out queue-state checks and updates to methods
* Logic fixes in get_nowait and shutdown
* Handle queue shutdown in task_done and join
* Updated tests
* Document feature added in 3.13
  • Loading branch information
YvesDup authored and EpicWink committed May 6, 2023
commit fb458db0a2e6adce92fe846c2dfaa781888fc256
4 changes: 2 additions & 2 deletions Doc/library/asyncio-queue.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ Queue
All blocked callers of put() will be unblocked, and also get()
and join() if *immediate* is true.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry but I have a doubt, shouldn't this documentation block be rather:

    All blocked callers of :meth:`~Queue.put` and :meth:`~Queue.get` 
    will be unblocked. If *immediate* is true, also unblock callers of 
    :meth:`~Queue.join`.

In event of change, the docstring of the shutdown method must be updated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

join callers aren't necessarily even unblocked anyway, if consumers are processing any items. I should probably say that a task is marked as done for each item in the queue if immediate shutdown.

Also, I think the threading queue docs are the same.

Copy link
Copy Markdown
Contributor

@YvesDup YvesDup Apr 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a task is marked as done for each item in the queue if immediate shutdown.

It's very precise, better.

Also, I think the threading queue docs are the same.

Yes, I commented here so as not to forget (see #117532 (comment)).

English is your native language, I think it'is best if you update documentations and docstrings.

Update: but I can create the follow-up PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made a PR: #117621

.. versionadded:: 3.12
.. versionadded:: 3.13

.. method:: task_done()

Expand Down Expand Up @@ -174,7 +174,7 @@ Exceptions
Exception raised when getting an item from or putting an item onto a
queue which has been shut down.

.. versionadded:: 3.12
.. versionadded:: 3.13


Examples
Expand Down
80 changes: 63 additions & 17 deletions Lib/asyncio/queues.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
)

import collections
import enum
import heapq
from types import GenericAlias

Expand All @@ -30,9 +31,10 @@ class QueueShutDown(Exception):
pass


_queue_alive = "alive"
_queue_shutdown = "shutdown"
_queue_shutdown_immediate = "shutdown-immediate"
class _QueueState(enum.Enum):
ALIVE = "alive"
SHUTDOWN = "shutdown"
SHUTDOWN_IMMEDIATE = "shutdown-immediate"


class Queue(mixins._LoopBoundMixin):
Expand All @@ -58,7 +60,7 @@ def __init__(self, maxsize=0):
self._finished = locks.Event()
self._finished.set()
self._init(maxsize)
self.shutdown_state = _queue_alive
self._shutdown_state = _QueueState.ALIVE

# These three are overridable in subclasses.

Expand Down Expand Up @@ -99,6 +101,8 @@ def _format(self):
result += f' _putters[{len(self._putters)}]'
if self._unfinished_tasks:
result += f' tasks={self._unfinished_tasks}'
if not self._is_alive():
result += f' state={self._shutdown_state.value}'
return result

def qsize(self):
Expand Down Expand Up @@ -130,8 +134,10 @@ async def put(self, item):

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

Raises QueueShutDown if the queue has been shut down.
"""
if self.shutdown_state != _queue_alive:
if not self._is_alive():
raise QueueShutDown
while self.full():
putter = self._get_loop().create_future()
Expand All @@ -145,23 +151,25 @@ async def put(self, item):
self._putters.remove(putter)
except ValueError:
# The putter could be removed from self._putters by a
# previous get_nowait call.
# previous get_nowait call or a shutdown call.
pass
if not self.full() and not putter.cancelled():
# We were woken up by get_nowait(), but can't take
# the call. Wake up the next in line.
self._wakeup_next(self._putters)
raise
if self.shutdown_state != _queue_alive:
if not self._is_alive():
raise QueueShutDown
return self.put_nowait(item)

def put_nowait(self, item):
"""Put an item into the queue without blocking.

If no free slot is immediately available, raise QueueFull.

Raises QueueShutDown if the queue has been shut down.
"""
if self.shutdown_state != _queue_alive:
if not self._is_alive():
raise QueueShutDown
if self.full():
raise QueueFull
Expand All @@ -174,11 +182,14 @@ async def get(self):
"""Remove and return an item from the queue.

If queue is empty, wait until an item is available.

Raises QueueShutDown if the queue has been shut down and is empty, or
if the queue has been shut down immediately.
"""
if self.shutdown_state == _queue_shutdown_immediate:
if self._is_shutdown_immediate():
raise QueueShutDown
while self.empty():
if self.shutdown_state != _queue_alive:
if self._is_shutdown():
raise QueueShutDown
getter = self._get_loop().create_future()
self._getters.append(getter)
Expand All @@ -191,28 +202,32 @@ async def get(self):
self._getters.remove(getter)
except ValueError:
# The getter could be removed from self._getters by a
# previous put_nowait call.
# previous put_nowait call,
# or a shutdown call.
pass
if not self.empty() and not getter.cancelled():
# We were woken up by put_nowait(), but can't take
# the call. Wake up the next in line.
self._wakeup_next(self._getters)
raise
if self.shutdown_state == _queue_shutdown_immediate:
if self._is_shutdown_immediate():
raise QueueShutDown
return self.get_nowait()

def get_nowait(self):
"""Remove and return an item from the queue.

Return an item if one is immediately available, else raise QueueEmpty.

Raises QueueShutDown if the queue has been shut down and is empty, or
if the queue has been shut down immediately.
"""
if self._is_shutdown_immediate():
raise QueueShutDown
if self.empty():
Comment thread
EpicWink marked this conversation as resolved.
if self.shutdown_state != _queue_alive:
if self._is_shutdown():
raise QueueShutDown
raise QueueEmpty
elif self.shutdown_state == _queue_shutdown_immediate:
raise QueueShutDown
item = self._get()
self._wakeup_next(self._putters)
return item
Expand All @@ -230,7 +245,11 @@ def task_done(self):

Raises ValueError if called more times than there were items placed in
the queue.

Raises QueueShutDown if the queue has been shut down immediately.
"""
if self._is_shutdown_immediate():
raise QueueShutDown
if self._unfinished_tasks <= 0:
raise ValueError('task_done() called too many times')
self._unfinished_tasks -= 1
Expand All @@ -244,9 +263,15 @@ async def join(self):
queue. The count goes down whenever a consumer calls task_done() to
indicate that the item was retrieved and all work on it is complete.
When the count of unfinished tasks drops to zero, join() unblocks.

Raises QueueShutDown if the queue has been shut down immediately.
"""
if self._is_shutdown_immediate():
raise QueueShutDown
if self._unfinished_tasks > 0:
await self._finished.wait()
if self._is_shutdown_immediate():
raise QueueShutDown

def shutdown(self, immediate=False):
"""Shut-down the queue, making queue gets and puts raise.
Expand All @@ -257,19 +282,40 @@ def shutdown(self, immediate=False):
All blocked callers of put() will be unblocked, and also get()
and join() if 'immediate'. The QueueShutDown exception is raised.
"""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring to modify depending of agree/disagree on my first remark about `blocked callers'.
Changes could be:

All blocked callers of put() and get() will be unblocked, and
also join() if 'immediate

if self._is_shutdown_immediate():
return
# here _shutdown_state is ALIVE or SHUTDOWN
if immediate:
self.shutdown_state = _queue_shutdown_immediate
self._set_shutdown_immediate()
while self._getters:
getter = self._getters.popleft()
if not getter.done():
getter.set_result(None)
# Release all 'blocked' tasks/coros in `join()`
self._finished.set()
else:
self.shutdown_state = _queue_shutdown
self._set_shutdown()
while self._putters:
putter = self._putters.popleft()
if not putter.done():
putter.set_result(None)

def _is_alive(self):
return self._shutdown_state is _QueueState.ALIVE

def _is_shutdown(self):
return self._shutdown_state is _QueueState.SHUTDOWN

def _is_shutdown_immediate(self):
return self._shutdown_state is _QueueState.SHUTDOWN_IMMEDIATE

def _set_shutdown(self):
self._shutdown_state = _QueueState.SHUTDOWN

def _set_shutdown_immediate(self):
self._shutdown_state = _QueueState.SHUTDOWN_IMMEDIATE


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

Expand Down
Loading