bpo-32684: Fix gather to propagate cancel of itself with return_exceptions (GH-7224)
This commit is contained in:
parent
51bf38f796
commit
3b263e65a8
|
@ -568,6 +568,10 @@ Task functions
|
|||
outer Future is *not* cancelled in this case. (This is to prevent the
|
||||
cancellation of one child to cause other children to be cancelled.)
|
||||
|
||||
.. versionchanged:: 3.6.6
|
||||
If the *gather* itself is cancelled, the cancellation is propagated
|
||||
regardless of *return_exceptions*.
|
||||
|
||||
.. function:: iscoroutine(obj)
|
||||
|
||||
Return ``True`` if *obj* is a :ref:`coroutine object <coroutine>`,
|
||||
|
|
|
@ -548,6 +548,7 @@ class _GatheringFuture(futures.Future):
|
|||
def __init__(self, children, *, loop=None):
|
||||
super().__init__(loop=loop)
|
||||
self._children = children
|
||||
self._cancel_requested = False
|
||||
|
||||
def cancel(self):
|
||||
if self.done():
|
||||
|
@ -556,6 +557,11 @@ class _GatheringFuture(futures.Future):
|
|||
for child in self._children:
|
||||
if child.cancel():
|
||||
ret = True
|
||||
if ret:
|
||||
# If any child tasks were actually cancelled, we should
|
||||
# propagate the cancellation request regardless of
|
||||
# *return_exceptions* argument. See issue 32684.
|
||||
self._cancel_requested = True
|
||||
return ret
|
||||
|
||||
|
||||
|
@ -636,7 +642,10 @@ def gather(*coros_or_futures, loop=None, return_exceptions=False):
|
|||
results[i] = res
|
||||
nfinished += 1
|
||||
if nfinished == nchildren:
|
||||
outer.set_result(results)
|
||||
if outer._cancel_requested:
|
||||
outer.set_exception(futures.CancelledError())
|
||||
else:
|
||||
outer.set_result(results)
|
||||
|
||||
for i, fut in enumerate(children):
|
||||
fut.add_done_callback(functools.partial(_done_callback, i))
|
||||
|
|
|
@ -1991,7 +1991,7 @@ class BaseTaskTests:
|
|||
def test_cancel_wait_for(self):
|
||||
self._test_cancel_wait_for(60.0)
|
||||
|
||||
def test_cancel_gather(self):
|
||||
def test_cancel_gather_1(self):
|
||||
"""Ensure that a gathering future refuses to be cancelled once all
|
||||
children are done"""
|
||||
loop = asyncio.new_event_loop()
|
||||
|
@ -2021,6 +2021,33 @@ class BaseTaskTests:
|
|||
self.assertFalse(gather_task.cancelled())
|
||||
self.assertEqual(gather_task.result(), [42])
|
||||
|
||||
def test_cancel_gather_2(self):
|
||||
loop = asyncio.new_event_loop()
|
||||
self.addCleanup(loop.close)
|
||||
|
||||
async def test():
|
||||
time = 0
|
||||
while True:
|
||||
time += 0.05
|
||||
await asyncio.gather(asyncio.sleep(0.05, loop=loop),
|
||||
return_exceptions=True,
|
||||
loop=loop)
|
||||
if time > 1:
|
||||
return
|
||||
|
||||
async def main():
|
||||
qwe = self.new_task(loop, test())
|
||||
await asyncio.sleep(0.2, loop=loop)
|
||||
qwe.cancel()
|
||||
try:
|
||||
await qwe
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
else:
|
||||
self.fail('gather did not propagate the cancellation request')
|
||||
|
||||
loop.run_until_complete(main())
|
||||
|
||||
def test_exception_traceback(self):
|
||||
# See http://bugs.python.org/issue28843
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Fix gather to propagate cancellation of itself even with return_exceptions.
|
Loading…
Reference in New Issue