From 7ea8d45d41c88160bfca67ae380759452ab021f2 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Sun, 22 Sep 2019 13:06:20 -0700 Subject: [PATCH] bpo-38248: Fix inconsistent immediate asyncio.Task cancellation --- Lib/asyncio/tasks.py | 2 +- Lib/test/test_asyncio/test_tasks.py | 4 ++++ .../2019-09-22-13-05-36.bpo-38248.Yo3N_1.rst | 1 + Modules/_asynciomodule.c | 15 ++++++++------- 4 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-09-22-13-05-36.bpo-38248.Yo3N_1.rst diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index a0cb884eacab7c..38d982716d46ad 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -284,7 +284,7 @@ def __step(self, exc=None): if self._must_cancel: # Task is cancelled right before coro stops. self._must_cancel = False - super().set_exception(exceptions.CancelledError()) + super().cancel() else: super().set_result(exc.value) except exceptions.CancelledError: diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 576714faa8884b..dde84b84b103e9 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -604,9 +604,11 @@ async def task(): return 12 t = self.new_task(loop, task()) + self.assertFalse(t.cancelled()) self.assertRaises( asyncio.CancelledError, loop.run_until_complete, t) self.assertTrue(t.done()) + self.assertTrue(t.cancelled()) self.assertFalse(t._must_cancel) # White-box test. self.assertFalse(t.cancel()) @@ -621,9 +623,11 @@ async def task(): return 12 t = self.new_task(loop, task()) + self.assertFalse(t.cancelled()) self.assertRaises( asyncio.CancelledError, loop.run_until_complete, t) self.assertTrue(t.done()) + self.assertTrue(t.cancelled()) self.assertFalse(t._must_cancel) # White-box test. self.assertFalse(t.cancel()) diff --git a/Misc/NEWS.d/next/Library/2019-09-22-13-05-36.bpo-38248.Yo3N_1.rst b/Misc/NEWS.d/next/Library/2019-09-22-13-05-36.bpo-38248.Yo3N_1.rst new file mode 100644 index 00000000000000..fc92209af3875f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-09-22-13-05-36.bpo-38248.Yo3N_1.rst @@ -0,0 +1 @@ +asyncio: Fix inconsistent immediate Task cancellation diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 4d503a418a2e64..8ee0d7a7e14c48 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -2628,18 +2628,19 @@ 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 */ + + PyObject *res; 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; + res = future_cancel((FutureObj*)task); + } + else { + res = future_set_result((FutureObj*)task, o); } - PyObject *res = future_set_result((FutureObj*)task, o); + Py_DECREF(o); + if (res == NULL) { return NULL; }