From f77d887d81366c1f4405a9644272efb2ee1aa42a Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Thu, 13 Apr 2017 10:35:10 +0900 Subject: [PATCH 1/3] add test --- Lib/test/test_asyncio/test_tasks.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 754a67519ba9d5b..988b2885c30cd93 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -587,6 +587,24 @@ def task(): 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 = self.new_task(loop, task()) + 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(): From 1cc94c94c58f61cf93f404cfbaf06028e1c65865 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Thu, 13 Apr 2017 10:18:45 +0900 Subject: [PATCH 2/3] bpo-30048: asyncio: fix Task.cancel() was ignored. example: async def coro(): asyncio.Task.current_task().cancel() return 42 ... res = await coro() # should raise CancelledError --- Lib/asyncio/tasks.py | 7 ++++++- Modules/_asynciomodule.c | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 4d79367d5cb600e..24238899aae7ca0 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -180,7 +180,12 @@ def _step(self, exc=None): 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: diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index e902c041e50587e..150ca198d27cfcd 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1985,6 +1985,16 @@ task_step_impl(TaskObj *task, PyObject *exc) if (_PyGen_FetchStopIterationValue(&o) == 0) { /* The error is StopIteration and that means that the underlying coroutine has resolved */ + if (task->task_must_cancel) { + // Task is cancelled right before coro stops. + Py_DECREF(o); + task->task_must_cancel = 0; + et = asyncio_CancelledError; + Py_INCREF(et); + ev = NULL; + tb = NULL; + goto set_exception; + } PyObject *res = future_set_result((FutureObj*)task, o); Py_DECREF(o); if (res == NULL) { @@ -2002,6 +2012,8 @@ task_step_impl(TaskObj *task, PyObject *exc) /* Some other exception; pop it and call Task.set_exception() */ PyErr_Fetch(&et, &ev, &tb); + +set_exception: assert(et); if (!ev || !PyObject_TypeCheck(ev, (PyTypeObject *) et)) { PyErr_NormalizeException(&et, &ev, &tb); From 0af81434cd8ea2c9b05369e07ae704191fbc3cfa Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Thu, 11 May 2017 20:51:22 +0900 Subject: [PATCH 3/3] Add news entry --- Misc/NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Misc/NEWS b/Misc/NEWS index b6f8c7094c9ef5d..60740bae4ac5f14 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -306,6 +306,10 @@ 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-29692: Fixed arbitrary unchaining of RuntimeError exceptions in contextlib.contextmanager. Patch by Siddharth Velankar.