Issue #26923: Fix asyncio.Gather to refuse being cancelled once all children are done.

Patch by Johannes Ebke.
This commit is contained in:
Yury Selivanov 2016-10-21 17:22:17 -04:00
parent f8c1505736
commit 3d67615a48
3 changed files with 38 additions and 2 deletions

View File

@ -592,9 +592,11 @@ class _GatheringFuture(futures.Future):
def cancel(self):
if self.done():
return False
ret = False
for child in self._children:
child.cancel()
return True
if child.cancel():
ret = True
return ret
def gather(*coros_or_futures, loop=None, return_exceptions=False):

View File

@ -1899,6 +1899,36 @@ class TaskTests(test_utils.TestCase):
def test_cancel_wait_for(self):
self._test_cancel_wait_for(60.0)
def test_cancel_gather(self):
"""Ensure that a gathering future refuses to be cancelled once all
children are done"""
loop = asyncio.new_event_loop()
self.addCleanup(loop.close)
fut = asyncio.Future(loop=loop)
# The indirection fut->child_coro is needed since otherwise the
# gathering task is done at the same time as the child future
def child_coro():
return (yield from fut)
gather_future = asyncio.gather(child_coro(), loop=loop)
gather_task = asyncio.ensure_future(gather_future, loop=loop)
cancel_result = None
def cancelling_callback(_):
nonlocal cancel_result
cancel_result = gather_task.cancel()
fut.add_done_callback(cancelling_callback)
fut.set_result(42) # calls the cancelling_callback after fut is done()
# At this point the task should complete.
loop.run_until_complete(gather_task)
# Python issue #26923: asyncio.gather drops cancellation
self.assertEqual(cancel_result, False)
self.assertFalse(gather_task.cancelled())
self.assertEqual(gather_task.result(), [42])
class GatherTestsBase:

View File

@ -398,6 +398,10 @@ Library
- Issue #27972: Prohibit Tasks to await on themselves.
- Issue #26923: Fix asyncio.Gather to refuse being cancelled once all
children are done.
Patch by Johannes Ebke.
IDLE
----