Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Implement asyncio.Task.__cancel_requested__ #31313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Lib/asyncio/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ def __init__(self, coro, *, loop=None, name=None):
self._fut_waiter = None
self._coro = coro
self._context = contextvars.copy_context()
self.__cancel_requested__ = False

self._loop.call_soon(self.__step, context=self._context)
_register_task(self)
Expand Down Expand Up @@ -201,6 +202,10 @@ def cancel(self, msg=None):
self._log_traceback = False
if self.done():
return False
if self.__cancel_requested__:
# Cancel was requested by previous task.cancel() call
return False
self.__cancel_requested__ = True
if self._fut_waiter is not None:
if self._fut_waiter.cancel(msg=msg):
# Leave self._fut_waiter; it may be a Task that
Expand Down Expand Up @@ -634,7 +639,7 @@ def _ensure_future(coro_or_future, *, loop=None):
loop = events._get_event_loop(stacklevel=4)
try:
return loop.create_task(coro_or_future)
except RuntimeError:
except RuntimeError:
if not called_wrap_awaitable:
coro_or_future.close()
raise
Expand Down
19 changes: 19 additions & 0 deletions Lib/test/test_asyncio/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,25 @@ async def run():
# This also distinguishes from the initial has_cycle=None.
self.assertEqual(has_cycle, False)

def test___cancel_requested__(self):
loop = asyncio.new_event_loop()

async def task():
await asyncio.sleep(10)
return 12

try:
t = self.new_task(loop, task())
self.assertFalse(t.__cancel_requested__)
self.assertTrue(t.cancel())
self.assertTrue(t.__cancel_requested__)
self.assertFalse(t.cancel())

with self.assertRaises(asyncio.CancelledError):
loop.run_until_complete(t)
finally:
loop.close()

def test_cancel(self):

def gen():
Expand Down
35 changes: 35 additions & 0 deletions Modules/_asynciomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ typedef struct {
PyObject *task_context;
int task_must_cancel;
int task_log_destroy_pending;
int task_cancel_requested;
} TaskObj;

typedef struct {
Expand Down Expand Up @@ -2039,6 +2040,7 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop,
Py_CLEAR(self->task_fut_waiter);
self->task_must_cancel = 0;
self->task_log_destroy_pending = 1;
self->task_cancel_requested = 0;
Py_INCREF(coro);
Py_XSETREF(self->task_coro, coro);

Expand Down Expand Up @@ -2141,6 +2143,32 @@ TaskObj_get_fut_waiter(TaskObj *task, void *Py_UNUSED(ignored))
Py_RETURN_NONE;
}

static PyObject *
TaskObj_get_cancel_requested(TaskObj *task, void *Py_UNUSED(ignored))
{
if (task->task_cancel_requested) {
Py_RETURN_TRUE;
}
else {
Py_RETURN_FALSE;
}
}

static int
TaskObj_set_cancel_requested(TaskObj *task, PyObject *val, void *Py_UNUSED(ignored))
{
if (val == NULL) {
PyErr_SetString(PyExc_AttributeError, "cannot delete attribute");
return -1;
}
int is_true = PyObject_IsTrue(val);
if (is_true < 0) {
return -1;
}
task->task_cancel_requested = is_true;
return 0;
}

/*[clinic input]
_asyncio.Task._make_cancelled_error

Expand Down Expand Up @@ -2205,6 +2233,11 @@ _asyncio_Task_cancel_impl(TaskObj *self, PyObject *msg)
Py_RETURN_FALSE;
}

if (self->task_cancel_requested) {
Py_RETURN_FALSE;
}
self->task_cancel_requested = 1;

if (self->task_fut_waiter) {
PyObject *res;
int is_true;
Expand Down Expand Up @@ -2473,6 +2506,8 @@ static PyGetSetDef TaskType_getsetlist[] = {
{"_must_cancel", (getter)TaskObj_get_must_cancel, NULL, NULL},
{"_coro", (getter)TaskObj_get_coro, NULL, NULL},
{"_fut_waiter", (getter)TaskObj_get_fut_waiter, NULL, NULL},
{"__cancel_requested__", (getter)TaskObj_get_cancel_requested,
(setter)TaskObj_set_cancel_requested, NULL},
{NULL} /* Sentinel */
};

Expand Down