]> granicus.if.org Git - python/commitdiff
bpo-32684: Fix gather to propagate cancel of itself with return_exceptions (GH-7209)
authorYury Selivanov <yury@magic.io>
Tue, 29 May 2018 21:20:02 +0000 (17:20 -0400)
committerGitHub <noreply@github.com>
Tue, 29 May 2018 21:20:02 +0000 (17:20 -0400)
Doc/library/asyncio-task.rst
Lib/asyncio/tasks.py
Lib/test/test_asyncio/test_tasks.py
Misc/NEWS.d/next/Library/2018-05-29-12-51-18.bpo-32684.ZEIism.rst [new file with mode: 0644]

index 233cc9454967ebbf278e7697c868230efdb79be3..dc450c375aade4a9374b1cce08bdad4bdf1adaf9 100644 (file)
@@ -640,6 +640,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.7.0
+      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>`,
index 67fb57c6a78159e6ba49e1f2f2a661d88f9cd9bf..6cef33d5212ee25acdaafeb6a9e6aff19deec856 100644 (file)
@@ -591,6 +591,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():
@@ -599,6 +600,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
 
 
@@ -673,7 +679,13 @@ def gather(*coros_or_futures, loop=None, return_exceptions=False):
                         res = fut.result()
                 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 = {}
     children = []
index 33300c91a3678fb01fdabca22ff524a7af785f7e..1280584d318c38842afdd54570d553958f9c0d7a 100644 (file)
@@ -2037,7 +2037,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()
@@ -2067,6 +2067,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),
+                                     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):
         # See http://bugs.python.org/issue28843
 
diff --git a/Misc/NEWS.d/next/Library/2018-05-29-12-51-18.bpo-32684.ZEIism.rst b/Misc/NEWS.d/next/Library/2018-05-29-12-51-18.bpo-32684.ZEIism.rst
new file mode 100644 (file)
index 0000000..b360bbc
--- /dev/null
@@ -0,0 +1 @@
+Fix gather to propagate cancellation of itself even with return_exceptions.