From edad4d89e357c92f70c0324b937845d652b20afd Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Wed, 25 Sep 2019 03:32:08 -0700 Subject: [PATCH] bpo-38248: Fix inconsistent immediate asyncio.Task cancellation (GH-16330) --- 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 a0cb884eaca..38d982716d4 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -284,7 +284,7 @@ class Task(futures._PyFuture): # Inherit Python Task implementation 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 576714faa88..dde84b84b10 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -604,9 +604,11 @@ class BaseTaskTests: 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 @@ class BaseTaskTests: 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 00000000000..fc92209af38 --- /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 4d503a418a2..8ee0d7a7e14 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); } - PyObject *res = future_set_result((FutureObj*)task, o); + else { + res = future_set_result((FutureObj*)task, o); + } + Py_DECREF(o); + if (res == NULL) { return NULL; }