[Python-checkins] cpython (3.5): Issue #26923: Fix asyncio.Gather to refuse being cancelled once all children

yury.selivanov python-checkins at python.org
Fri Oct 21 17:25:10 EDT 2016


https://hg.python.org/cpython/rev/b1aa485fad1b
changeset:   104620:b1aa485fad1b
branch:      3.5
parent:      104615:9feff7ba89b2
user:        Yury Selivanov <yury at magic.io>
date:        Fri Oct 21 17:22:17 2016 -0400
summary:
  Issue #26923: Fix asyncio.Gather to refuse being cancelled once all children are done.

Patch by Johannes Ebke.

files:
  Lib/asyncio/tasks.py                |   6 ++-
  Lib/test/test_asyncio/test_tasks.py |  30 +++++++++++++++++
  Misc/NEWS                           |   4 ++
  3 files changed, 38 insertions(+), 2 deletions(-)


diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py
--- a/Lib/asyncio/tasks.py
+++ b/Lib/asyncio/tasks.py
@@ -592,9 +592,11 @@
     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):
diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -1899,6 +1899,36 @@
     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:
 
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -398,6 +398,10 @@
 
 - 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
 ----
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list