mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
bpo-32684: Fix gather to propagate cancel of itself with return_exceptions (GH-7209)
This commit is contained in:
parent
1cee216cf3
commit
863b674909
4 changed files with 46 additions and 2 deletions
|
@ -640,6 +640,10 @@ Task functions
|
||||||
outer Future is *not* cancelled in this case. (This is to prevent the
|
outer Future is *not* cancelled in this case. (This is to prevent the
|
||||||
cancellation of one child to cause other children to be cancelled.)
|
cancellation of one child to cause other children to be cancelled.)
|
||||||
|
|
||||||
|
.. versionchanged:: 3.7.0
|
||||||
|
If the *gather* itself is cancelled, the cancellation is propagated
|
||||||
|
regardless of *return_exceptions*.
|
||||||
|
|
||||||
.. function:: iscoroutine(obj)
|
.. function:: iscoroutine(obj)
|
||||||
|
|
||||||
Return ``True`` if *obj* is a :ref:`coroutine object <coroutine>`,
|
Return ``True`` if *obj* is a :ref:`coroutine object <coroutine>`,
|
||||||
|
|
|
@ -591,6 +591,7 @@ class _GatheringFuture(futures.Future):
|
||||||
def __init__(self, children, *, loop=None):
|
def __init__(self, children, *, loop=None):
|
||||||
super().__init__(loop=loop)
|
super().__init__(loop=loop)
|
||||||
self._children = children
|
self._children = children
|
||||||
|
self._cancel_requested = False
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
if self.done():
|
if self.done():
|
||||||
|
@ -599,6 +600,11 @@ class _GatheringFuture(futures.Future):
|
||||||
for child in self._children:
|
for child in self._children:
|
||||||
if child.cancel():
|
if child.cancel():
|
||||||
ret = True
|
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
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
@ -673,7 +679,13 @@ def gather(*coros_or_futures, loop=None, return_exceptions=False):
|
||||||
res = fut.result()
|
res = fut.result()
|
||||||
results.append(res)
|
results.append(res)
|
||||||
|
|
||||||
outer.set_result(results)
|
if outer._cancel_requested:
|
||||||
|
# If gather is being cancelled we must propagate the
|
||||||
|
# cancellation regardless of *return_exceptions* argument.
|
||||||
|
# See issue 32684.
|
||||||
|
outer.set_exception(futures.CancelledError())
|
||||||
|
else:
|
||||||
|
outer.set_result(results)
|
||||||
|
|
||||||
arg_to_fut = {}
|
arg_to_fut = {}
|
||||||
children = []
|
children = []
|
||||||
|
|
|
@ -2037,7 +2037,7 @@ class BaseTaskTests:
|
||||||
def test_cancel_wait_for(self):
|
def test_cancel_wait_for(self):
|
||||||
self._test_cancel_wait_for(60.0)
|
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
|
"""Ensure that a gathering future refuses to be cancelled once all
|
||||||
children are done"""
|
children are done"""
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
|
@ -2067,6 +2067,33 @@ class BaseTaskTests:
|
||||||
self.assertFalse(gather_task.cancelled())
|
self.assertFalse(gather_task.cancelled())
|
||||||
self.assertEqual(gather_task.result(), [42])
|
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),
|
||||||
|
return_exceptions=True,
|
||||||
|
loop=loop)
|
||||||
|
if time > 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
qwe = asyncio.Task(test())
|
||||||
|
await asyncio.sleep(0.2)
|
||||||
|
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):
|
def test_exception_traceback(self):
|
||||||
# See http://bugs.python.org/issue28843
|
# See http://bugs.python.org/issue28843
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix gather to propagate cancellation of itself even with return_exceptions.
|
Loading…
Add table
Add a link
Reference in a new issue