]> granicus.if.org Git - python/commitdiff
bpo-30048: asyncio: fix Task.cancel() was ignored. (GH-1547)
authorINADA Naoki <methane@users.noreply.github.com>
Fri, 12 May 2017 05:34:40 +0000 (14:34 +0900)
committerGitHub <noreply@github.com>
Fri, 12 May 2017 05:34:40 +0000 (14:34 +0900)
* bpo-30048: asyncio: fix Task.cancel() was ignored. (GH-1097)

when there are no more `await` or `yield (from)` before return in coroutine,
cancel was ignored.

example:

    async def coro():
        asyncio.Task.current_task().cancel()
        return 42
    ...
    res = await coro()  # should raise CancelledError
(cherry picked from commit 991adca012f5e106c2d4040ce619c696ba6f9c46)

* fix test

Lib/asyncio/tasks.py
Lib/test/test_asyncio/test_tasks.py
Misc/NEWS

index b6bd53cef1aa41abd462da3a3984f2e190b37480..89d0989c614a5636d52283e45b6965f338063406 100644 (file)
@@ -240,7 +240,12 @@ class Task(futures.Future):
             else:
                 result = coro.throw(exc)
         except StopIteration as exc:
-            self.set_result(exc.value)
+            if self._must_cancel:
+                # Task is cancelled right before coro stops.
+                self._must_cancel = False
+                self.set_exception(futures.CancelledError())
+            else:
+                self.set_result(exc.value)
         except futures.CancelledError:
             super().cancel()  # I.e., Future.cancel(self).
         except Exception as exc:
index 9872926739f01ce395f870a2e7805e832ef0a8f8..b3d0653e82e573e709899a98e4e6eb28311969f1 100644 (file)
@@ -573,6 +573,24 @@ class TaskTests(test_utils.TestCase):
         self.assertFalse(t._must_cancel)  # White-box test.
         self.assertFalse(t.cancel())
 
+    def test_cancel_at_end(self):
+        """coroutine end right after task is cancelled"""
+        loop = asyncio.new_event_loop()
+        self.set_event_loop(loop)
+
+        @asyncio.coroutine
+        def task():
+            t.cancel()
+            self.assertTrue(t._must_cancel)  # White-box test.
+            return 12
+
+        t = asyncio.Task(task(), loop=loop)
+        self.assertRaises(
+            asyncio.CancelledError, loop.run_until_complete, t)
+        self.assertTrue(t.done())
+        self.assertFalse(t._must_cancel)  # White-box test.
+        self.assertFalse(t.cancel())
+
     def test_stop_while_run_in_complete(self):
 
         def gen():
index 889386dba05de42a4249bf2b228c732db000a057..41fe80f44c3f123031a308ea35ba89d99eca2f92 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -49,6 +49,9 @@ Extension Modules
 Library
 -------
 
+- bpo-30048: Fixed ``Task.cancel()`` can be ignored when the task is
+  running coroutine and the coroutine returned without any more ``await``.
+
 - bpo-29990: Fix range checking in GB18030 decoder.  Original patch by Ma Lin.
 
 - Revert bpo-26293 for zipfile breakage. See also bpo-29094.