bpo-31033: Add a msg argument to Future.cancel() and Task.cancel() (GH-19979)

This commit is contained in:
Chris Jerdonek 2020-05-15 16:55:50 -07:00 committed by GitHub
parent fe1176e882
commit 1ce5841eca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 355 additions and 70 deletions

View File

@ -170,7 +170,7 @@ Future Object
Returns the number of callbacks removed, which is typically 1,
unless a callback was added more than once.
.. method:: cancel()
.. method:: cancel(msg=None)
Cancel the Future and schedule callbacks.
@ -178,6 +178,9 @@ Future Object
Otherwise, change the Future's state to *cancelled*,
schedule the callbacks, and return ``True``.
.. versionchanged:: 3.9
Added the ``msg`` parameter.
.. method:: exception()
Return the exception that was set on this Future.
@ -255,3 +258,6 @@ the Future has a result::
- asyncio Future is not compatible with the
:func:`concurrent.futures.wait` and
:func:`concurrent.futures.as_completed` functions.
- :meth:`asyncio.Future.cancel` accepts an optional ``msg`` argument,
but :func:`concurrent.futures.cancel` does not.

View File

@ -724,7 +724,7 @@ Task Object
.. deprecated-removed:: 3.8 3.10
The *loop* parameter.
.. method:: cancel()
.. method:: cancel(msg=None)
Request the Task to be cancelled.
@ -739,6 +739,9 @@ Task Object
suppressing cancellation completely is not common and is actively
discouraged.
.. versionchanged:: 3.9
Added the ``msg`` parameter.
.. _asyncio_example_task_cancel:
The following example illustrates how coroutines can intercept

View File

@ -51,6 +51,7 @@ class Future:
_exception = None
_loop = None
_source_traceback = None
_cancel_message = None
# This field is used for a dual purpose:
# - Its presence is a marker to declare that a class implements
@ -123,7 +124,7 @@ class Future:
raise RuntimeError("Future object is not initialized.")
return loop
def cancel(self):
def cancel(self, msg=None):
"""Cancel the future and schedule callbacks.
If the future is already done or cancelled, return False. Otherwise,
@ -134,6 +135,7 @@ class Future:
if self._state != _PENDING:
return False
self._state = _CANCELLED
self._cancel_message = msg
self.__schedule_callbacks()
return True
@ -173,7 +175,9 @@ class Future:
the future is done and has an exception set, this exception is raised.
"""
if self._state == _CANCELLED:
raise exceptions.CancelledError
raise exceptions.CancelledError(
'' if self._cancel_message is None else self._cancel_message)
if self._state != _FINISHED:
raise exceptions.InvalidStateError('Result is not ready.')
self.__log_traceback = False
@ -190,7 +194,8 @@ class Future:
InvalidStateError.
"""
if self._state == _CANCELLED:
raise exceptions.CancelledError
raise exceptions.CancelledError(
'' if self._cancel_message is None else self._cancel_message)
if self._state != _FINISHED:
raise exceptions.InvalidStateError('Exception is not set.')
self.__log_traceback = False

View File

@ -230,7 +230,7 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
"""
return base_tasks._task_print_stack(self, limit, file)
def cancel(self):
def cancel(self, msg=None):
"""Request that this task cancel itself.
This arranges for a CancelledError to be thrown into the
@ -254,13 +254,14 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
if self.done():
return False
if self._fut_waiter is not None:
if self._fut_waiter.cancel():
if self._fut_waiter.cancel(msg=msg):
# Leave self._fut_waiter; it may be a Task that
# catches and ignores the cancellation so we may have
# to cancel it again later.
return True
# It must be the case that self.__step is already scheduled.
self._must_cancel = True
self._cancel_message = msg
return True
def __step(self, exc=None):
@ -269,7 +270,8 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
f'_step(): already done: {self!r}, {exc!r}')
if self._must_cancel:
if not isinstance(exc, exceptions.CancelledError):
exc = exceptions.CancelledError()
exc = exceptions.CancelledError(''
if self._cancel_message is None else self._cancel_message)
self._must_cancel = False
coro = self._coro
self._fut_waiter = None
@ -287,11 +289,15 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
if self._must_cancel:
# Task is cancelled right before coro stops.
self._must_cancel = False
super().cancel()
super().cancel(msg=self._cancel_message)
else:
super().set_result(exc.value)
except exceptions.CancelledError:
super().cancel() # I.e., Future.cancel(self).
except exceptions.CancelledError as exc:
if exc.args:
cancel_msg = exc.args[0]
else:
cancel_msg = None
super().cancel(msg=cancel_msg) # I.e., Future.cancel(self).
except (KeyboardInterrupt, SystemExit) as exc:
super().set_exception(exc)
raise
@ -319,7 +325,8 @@ class Task(futures._PyFuture): # Inherit Python Task implementation
self.__wakeup, context=self._context)
self._fut_waiter = result
if self._must_cancel:
if self._fut_waiter.cancel():
if self._fut_waiter.cancel(
msg=self._cancel_message):
self._must_cancel = False
else:
new_exc = RuntimeError(
@ -716,12 +723,12 @@ class _GatheringFuture(futures.Future):
self._children = children
self._cancel_requested = False
def cancel(self):
def cancel(self, msg=None):
if self.done():
return False
ret = False
for child in self._children:
if child.cancel():
if child.cancel(msg=msg):
ret = True
if ret:
# If any child tasks were actually cancelled, we should
@ -780,7 +787,8 @@ def gather(*coros_or_futures, loop=None, return_exceptions=False):
# Check if 'fut' is cancelled first, as
# 'fut.exception()' will *raise* a CancelledError
# instead of returning it.
exc = exceptions.CancelledError()
exc = exceptions.CancelledError(''
if fut._cancel_message is None else fut._cancel_message)
outer.set_exception(exc)
return
else:
@ -799,7 +807,9 @@ def gather(*coros_or_futures, loop=None, return_exceptions=False):
# Check if 'fut' is cancelled first, as
# 'fut.exception()' will *raise* a CancelledError
# instead of returning it.
res = exceptions.CancelledError()
res = exceptions.CancelledError(
'' if fut._cancel_message is None else
fut._cancel_message)
else:
res = fut.exception()
if res is None:
@ -810,7 +820,9 @@ def gather(*coros_or_futures, loop=None, return_exceptions=False):
# If gather is being cancelled we must propagate the
# cancellation regardless of *return_exceptions* argument.
# See issue 32684.
outer.set_exception(exceptions.CancelledError())
exc = exceptions.CancelledError(''
if fut._cancel_message is None else fut._cancel_message)
outer.set_exception(exc)
else:
outer.set_result(results)

View File

@ -75,9 +75,9 @@ class _OverlappedFuture(futures.Future):
self._loop.call_exception_handler(context)
self._ov = None
def cancel(self):
def cancel(self, msg=None):
self._cancel_overlapped()
return super().cancel()
return super().cancel(msg=msg)
def set_exception(self, exception):
super().set_exception(exception)
@ -149,9 +149,9 @@ class _BaseWaitHandleFuture(futures.Future):
self._unregister_wait_cb(None)
def cancel(self):
def cancel(self, msg=None):
self._unregister_wait()
return super().cancel()
return super().cancel(msg=msg)
def set_exception(self, exception):
self._unregister_wait()

View File

@ -201,6 +201,27 @@ class BaseFutureTests:
self.assertFalse(fut.cancelled())
self.assertFalse(fut.done())
def test_future_cancel_message_getter(self):
f = self._new_future(loop=self.loop)
self.assertTrue(hasattr(f, '_cancel_message'))
self.assertEqual(f._cancel_message, None)
f.cancel('my message')
with self.assertRaises(asyncio.CancelledError):
self.loop.run_until_complete(f)
self.assertEqual(f._cancel_message, 'my message')
def test_future_cancel_message_setter(self):
f = self._new_future(loop=self.loop)
f.cancel('my message')
f._cancel_message = 'my new message'
self.assertEqual(f._cancel_message, 'my new message')
# Also check that the value is used for cancel().
with self.assertRaises(asyncio.CancelledError):
self.loop.run_until_complete(f)
self.assertEqual(f._cancel_message, 'my new message')
def test_cancel(self):
f = self._new_future(loop=self.loop)
self.assertTrue(f.cancel())

View File

@ -103,6 +103,31 @@ class BaseTaskTests:
self.loop.set_task_factory(self.new_task)
self.loop.create_future = lambda: self.new_future(self.loop)
def test_task_cancel_message_getter(self):
async def coro():
pass
t = self.new_task(self.loop, coro())
self.assertTrue(hasattr(t, '_cancel_message'))
self.assertEqual(t._cancel_message, None)
t.cancel('my message')
with self.assertRaises(asyncio.CancelledError):
self.loop.run_until_complete(t)
self.assertEqual(t._cancel_message, 'my message')
def test_task_cancel_message_setter(self):
async def coro():
pass
t = self.new_task(self.loop, coro())
t.cancel('my message')
t._cancel_message = 'my new message'
self.assertEqual(t._cancel_message, 'my new message')
# Also check that the value is used for cancel().
with self.assertRaises(asyncio.CancelledError):
self.loop.run_until_complete(t)
self.assertEqual(t._cancel_message, 'my new message')
def test_task_del_collect(self):
class Evil:
def __del__(self):
@ -520,6 +545,86 @@ class BaseTaskTests:
self.assertTrue(t.cancelled())
self.assertFalse(t.cancel())
def test_cancel_with_message_then_future_result(self):
# Test Future.result() after calling cancel() with a message.
cases = [
((), ('',)),
((None,), ('',)),
(('my message',), ('my message',)),
# Non-string values should roundtrip.
((5,), (5,)),
]
for cancel_args, expected_args in cases:
with self.subTest(cancel_args=cancel_args):
loop = asyncio.new_event_loop()
self.set_event_loop(loop)
async def sleep():
await asyncio.sleep(10)
async def coro():
task = self.new_task(loop, sleep())
await asyncio.sleep(0)
task.cancel(*cancel_args)
done, pending = await asyncio.wait([task])
task.result()
task = self.new_task(loop, coro())
with self.assertRaises(asyncio.CancelledError) as cm:
loop.run_until_complete(task)
exc = cm.exception
self.assertEqual(exc.args, expected_args)
def test_cancel_with_message_then_future_exception(self):
# Test Future.exception() after calling cancel() with a message.
cases = [
((), ('',)),
((None,), ('',)),
(('my message',), ('my message',)),
# Non-string values should roundtrip.
((5,), (5,)),
]
for cancel_args, expected_args in cases:
with self.subTest(cancel_args=cancel_args):
loop = asyncio.new_event_loop()
self.set_event_loop(loop)
async def sleep():
await asyncio.sleep(10)
async def coro():
task = self.new_task(loop, sleep())
await asyncio.sleep(0)
task.cancel(*cancel_args)
done, pending = await asyncio.wait([task])
task.exception()
task = self.new_task(loop, coro())
with self.assertRaises(asyncio.CancelledError) as cm:
loop.run_until_complete(task)
exc = cm.exception
self.assertEqual(exc.args, expected_args)
def test_cancel_with_message_before_starting_task(self):
loop = asyncio.new_event_loop()
self.set_event_loop(loop)
async def sleep():
await asyncio.sleep(10)
async def coro():
task = self.new_task(loop, sleep())
# We deliberately leave out the sleep here.
task.cancel('my message')
done, pending = await asyncio.wait([task])
task.exception()
task = self.new_task(loop, coro())
with self.assertRaises(asyncio.CancelledError) as cm:
loop.run_until_complete(task)
exc = cm.exception
self.assertEqual(exc.args, ('my message',))
def test_cancel_yield(self):
with self.assertWarns(DeprecationWarning):
@asyncio.coroutine
@ -2285,31 +2390,42 @@ class BaseTaskTests:
self.assertEqual(gather_task.result(), [42])
def test_cancel_gather_2(self):
loop = asyncio.new_event_loop()
self.addCleanup(loop.close)
cases = [
((), ('',)),
((None,), ('',)),
(('my message',), ('my message',)),
# Non-string values should roundtrip.
((5,), (5,)),
]
for cancel_args, expected_args in cases:
with self.subTest(cancel_args=cancel_args):
async def test():
time = 0
while True:
time += 0.05
await asyncio.gather(asyncio.sleep(0.05),
return_exceptions=True,
loop=loop)
if time > 1:
return
loop = asyncio.new_event_loop()
self.addCleanup(loop.close)
async def main():
qwe = self.new_task(loop, test())
await asyncio.sleep(0.2)
qwe.cancel()
try:
await qwe
except asyncio.CancelledError:
pass
else:
self.fail('gather did not propagate the cancellation request')
async def test():
time = 0
while True:
time += 0.05
await asyncio.gather(asyncio.sleep(0.05),
return_exceptions=True,
loop=loop)
if time > 1:
return
loop.run_until_complete(main())
async def main():
qwe = self.new_task(loop, test())
await asyncio.sleep(0.2)
qwe.cancel(*cancel_args)
try:
await qwe
except asyncio.CancelledError as exc:
self.assertEqual(exc.args, expected_args)
else:
self.fail('gather did not propagate the cancellation '
'request')
loop.run_until_complete(main())
def test_exception_traceback(self):
# See http://bugs.python.org/issue28843

View File

@ -0,0 +1 @@
Add a ``msg`` argument to :meth:`Future.cancel` and :meth:`Task.cancel`.

View File

@ -67,6 +67,7 @@ typedef enum {
PyObject *prefix##_exception; \
PyObject *prefix##_result; \
PyObject *prefix##_source_tb; \
PyObject *prefix##_cancel_msg; \
fut_state prefix##_state; \
int prefix##_log_tb; \
int prefix##_blocking; \
@ -480,6 +481,7 @@ future_init(FutureObj *fut, PyObject *loop)
Py_CLEAR(fut->fut_result);
Py_CLEAR(fut->fut_exception);
Py_CLEAR(fut->fut_source_tb);
Py_CLEAR(fut->fut_cancel_msg);
fut->fut_state = STATE_PENDING;
fut->fut_log_tb = 0;
@ -594,11 +596,33 @@ future_set_exception(FutureObj *fut, PyObject *exc)
Py_RETURN_NONE;
}
static PyObject *
create_cancelled_error(PyObject *msg)
{
PyObject *exc;
if (msg == NULL || msg == Py_None) {
msg = PyUnicode_FromString("");
exc = PyObject_CallOneArg(asyncio_CancelledError, msg);
Py_DECREF(msg);
} else {
exc = PyObject_CallOneArg(asyncio_CancelledError, msg);
}
return exc;
}
static void
set_cancelled_error(PyObject *msg)
{
PyObject *exc = create_cancelled_error(msg);
PyErr_SetObject(asyncio_CancelledError, exc);
Py_DECREF(exc);
}
static int
future_get_result(FutureObj *fut, PyObject **result)
{
if (fut->fut_state == STATE_CANCELLED) {
PyErr_SetNone(asyncio_CancelledError);
set_cancelled_error(fut->fut_cancel_msg);
return -1;
}
@ -695,7 +719,7 @@ future_add_done_callback(FutureObj *fut, PyObject *arg, PyObject *ctx)
}
static PyObject *
future_cancel(FutureObj *fut)
future_cancel(FutureObj *fut, PyObject *msg)
{
fut->fut_log_tb = 0;
@ -704,6 +728,9 @@ future_cancel(FutureObj *fut)
}
fut->fut_state = STATE_CANCELLED;
Py_XINCREF(msg);
Py_XSETREF(fut->fut_cancel_msg, msg);
if (future_schedule_callbacks(fut) == -1) {
return NULL;
}
@ -749,6 +776,7 @@ FutureObj_clear(FutureObj *fut)
Py_CLEAR(fut->fut_result);
Py_CLEAR(fut->fut_exception);
Py_CLEAR(fut->fut_source_tb);
Py_CLEAR(fut->fut_cancel_msg);
Py_CLEAR(fut->dict);
return 0;
}
@ -763,6 +791,7 @@ FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg)
Py_VISIT(fut->fut_result);
Py_VISIT(fut->fut_exception);
Py_VISIT(fut->fut_source_tb);
Py_VISIT(fut->fut_cancel_msg);
Py_VISIT(fut->dict);
return 0;
}
@ -828,7 +857,7 @@ _asyncio_Future_exception_impl(FutureObj *self)
}
if (self->fut_state == STATE_CANCELLED) {
PyErr_SetNone(asyncio_CancelledError);
set_cancelled_error(self->fut_cancel_msg);
return NULL;
}
@ -1029,6 +1058,8 @@ fail:
/*[clinic input]
_asyncio.Future.cancel
msg: object = None
Cancel the future and schedule callbacks.
If the future is already done or cancelled, return False. Otherwise,
@ -1037,11 +1068,11 @@ return True.
[clinic start generated code]*/
static PyObject *
_asyncio_Future_cancel_impl(FutureObj *self)
/*[clinic end generated code: output=e45b932ba8bd68a1 input=515709a127995109]*/
_asyncio_Future_cancel_impl(FutureObj *self, PyObject *msg)
/*[clinic end generated code: output=3edebbc668e5aba3 input=925eb545251f2c5a]*/
{
ENSURE_FUTURE_ALIVE(self)
return future_cancel(self);
return future_cancel(self, msg);
}
/*[clinic input]
@ -1254,6 +1285,29 @@ FutureObj_get_source_traceback(FutureObj *fut, void *Py_UNUSED(ignored))
return fut->fut_source_tb;
}
static PyObject *
FutureObj_get_cancel_message(FutureObj *fut, void *Py_UNUSED(ignored))
{
if (fut->fut_cancel_msg == NULL) {
Py_RETURN_NONE;
}
Py_INCREF(fut->fut_cancel_msg);
return fut->fut_cancel_msg;
}
static int
FutureObj_set_cancel_message(FutureObj *fut, PyObject *msg,
void *Py_UNUSED(ignored))
{
if (msg == NULL) {
PyErr_SetString(PyExc_AttributeError, "cannot delete attribute");
return -1;
}
Py_INCREF(msg);
Py_XSETREF(fut->fut_cancel_msg, msg);
return 0;
}
static PyObject *
FutureObj_get_state(FutureObj *fut, void *Py_UNUSED(ignored))
{
@ -1422,7 +1476,10 @@ static PyMethodDef FutureType_methods[] = {
{"_exception", (getter)FutureObj_get_exception, NULL, NULL}, \
{"_log_traceback", (getter)FutureObj_get_log_traceback, \
(setter)FutureObj_set_log_traceback, NULL}, \
{"_source_traceback", (getter)FutureObj_get_source_traceback, NULL, NULL},
{"_source_traceback", (getter)FutureObj_get_source_traceback, \
NULL, NULL}, \
{"_cancel_message", (getter)FutureObj_get_cancel_message, \
(setter)FutureObj_set_cancel_message, NULL},
static PyGetSetDef FutureType_getsetlist[] = {
FUTURE_COMMON_GETSETLIST
@ -2189,6 +2246,8 @@ _asyncio_Task__repr_info_impl(TaskObj *self)
/*[clinic input]
_asyncio.Task.cancel
msg: object = None
Request that this task cancel itself.
This arranges for a CancelledError to be thrown into the
@ -2210,8 +2269,8 @@ was not called).
[clinic start generated code]*/
static PyObject *
_asyncio_Task_cancel_impl(TaskObj *self)
/*[clinic end generated code: output=6bfc0479da9d5757 input=13f9bf496695cb52]*/
_asyncio_Task_cancel_impl(TaskObj *self, PyObject *msg)
/*[clinic end generated code: output=c66b60d41c74f9f1 input=f4ff8e8ffc5f1c00]*/
{
self->task_log_tb = 0;
@ -2223,7 +2282,8 @@ _asyncio_Task_cancel_impl(TaskObj *self)
PyObject *res;
int is_true;
res = _PyObject_CallMethodIdNoArgs(self->task_fut_waiter, &PyId_cancel);
res = _PyObject_CallMethodIdOneArg(self->task_fut_waiter,
&PyId_cancel, msg);
if (res == NULL) {
return NULL;
}
@ -2240,6 +2300,8 @@ _asyncio_Task_cancel_impl(TaskObj *self)
}
self->task_must_cancel = 1;
Py_XINCREF(msg);
Py_XSETREF(self->task_cancel_msg, msg);
Py_RETURN_TRUE;
}
@ -2623,7 +2685,8 @@ task_step_impl(TaskObj *task, PyObject *exc)
if (!exc) {
/* exc was not a CancelledError */
exc = PyObject_CallNoArgs(asyncio_CancelledError);
exc = create_cancelled_error(task->task_cancel_msg);
if (!exc) {
goto fail;
}
@ -2672,7 +2735,7 @@ task_step_impl(TaskObj *task, PyObject *exc)
if (task->task_must_cancel) {
// Task is cancelled right before coro stops.
task->task_must_cancel = 0;
res = future_cancel((FutureObj*)task);
res = future_cancel((FutureObj*)task, task->task_cancel_msg);
}
else {
res = future_set_result((FutureObj*)task, o);
@ -2689,8 +2752,26 @@ task_step_impl(TaskObj *task, PyObject *exc)
if (PyErr_ExceptionMatches(asyncio_CancelledError)) {
/* CancelledError */
PyErr_Clear();
return future_cancel((FutureObj*)task);
PyErr_Fetch(&et, &ev, &tb);
PyObject *cancel_msg;
if (ev != NULL && PyExceptionInstance_Check(ev)) {
PyObject *exc_args = ((PyBaseExceptionObject*)ev)->args;
Py_ssize_t size = PyTuple_GET_SIZE(exc_args);
if (size > 0) {
cancel_msg = PyTuple_GET_ITEM(exc_args, 0);
} else {
cancel_msg = NULL;
}
} else {
cancel_msg = ev;
}
Py_DECREF(et);
Py_XDECREF(tb);
Py_XDECREF(ev);
return future_cancel((FutureObj*)task, cancel_msg);
}
/* Some other exception; pop it and call Task.set_exception() */
@ -2770,7 +2851,8 @@ task_step_impl(TaskObj *task, PyObject *exc)
if (task->task_must_cancel) {
PyObject *r;
int is_true;
r = _PyObject_CallMethodIdNoArgs(result, &PyId_cancel);
r = _PyObject_CallMethodIdOneArg(result, &PyId_cancel,
task->task_cancel_msg);
if (r == NULL) {
return NULL;
}
@ -2861,7 +2943,8 @@ task_step_impl(TaskObj *task, PyObject *exc)
if (task->task_must_cancel) {
PyObject *r;
int is_true;
r = _PyObject_CallMethodIdNoArgs(result, &PyId_cancel);
r = _PyObject_CallMethodIdOneArg(result, &PyId_cancel,
task->task_cancel_msg);
if (r == NULL) {
return NULL;
}

View File

@ -174,7 +174,7 @@ PyDoc_STRVAR(_asyncio_Future_remove_done_callback__doc__,
{"remove_done_callback", (PyCFunction)_asyncio_Future_remove_done_callback, METH_O, _asyncio_Future_remove_done_callback__doc__},
PyDoc_STRVAR(_asyncio_Future_cancel__doc__,
"cancel($self, /)\n"
"cancel($self, /, msg=None)\n"
"--\n"
"\n"
"Cancel the future and schedule callbacks.\n"
@ -184,15 +184,34 @@ PyDoc_STRVAR(_asyncio_Future_cancel__doc__,
"return True.");
#define _ASYNCIO_FUTURE_CANCEL_METHODDEF \
{"cancel", (PyCFunction)_asyncio_Future_cancel, METH_NOARGS, _asyncio_Future_cancel__doc__},
{"cancel", (PyCFunction)(void(*)(void))_asyncio_Future_cancel, METH_FASTCALL|METH_KEYWORDS, _asyncio_Future_cancel__doc__},
static PyObject *
_asyncio_Future_cancel_impl(FutureObj *self);
_asyncio_Future_cancel_impl(FutureObj *self, PyObject *msg);
static PyObject *
_asyncio_Future_cancel(FutureObj *self, PyObject *Py_UNUSED(ignored))
_asyncio_Future_cancel(FutureObj *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
return _asyncio_Future_cancel_impl(self);
PyObject *return_value = NULL;
static const char * const _keywords[] = {"msg", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "cancel", 0};
PyObject *argsbuf[1];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
PyObject *msg = Py_None;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf);
if (!args) {
goto exit;
}
if (!noptargs) {
goto skip_optional_pos;
}
msg = args[0];
skip_optional_pos:
return_value = _asyncio_Future_cancel_impl(self, msg);
exit:
return return_value;
}
PyDoc_STRVAR(_asyncio_Future_cancelled__doc__,
@ -413,7 +432,7 @@ _asyncio_Task__repr_info(TaskObj *self, PyObject *Py_UNUSED(ignored))
}
PyDoc_STRVAR(_asyncio_Task_cancel__doc__,
"cancel($self, /)\n"
"cancel($self, /, msg=None)\n"
"--\n"
"\n"
"Request that this task cancel itself.\n"
@ -436,15 +455,34 @@ PyDoc_STRVAR(_asyncio_Task_cancel__doc__,
"was not called).");
#define _ASYNCIO_TASK_CANCEL_METHODDEF \
{"cancel", (PyCFunction)_asyncio_Task_cancel, METH_NOARGS, _asyncio_Task_cancel__doc__},
{"cancel", (PyCFunction)(void(*)(void))_asyncio_Task_cancel, METH_FASTCALL|METH_KEYWORDS, _asyncio_Task_cancel__doc__},
static PyObject *
_asyncio_Task_cancel_impl(TaskObj *self);
_asyncio_Task_cancel_impl(TaskObj *self, PyObject *msg);
static PyObject *
_asyncio_Task_cancel(TaskObj *self, PyObject *Py_UNUSED(ignored))
_asyncio_Task_cancel(TaskObj *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
return _asyncio_Task_cancel_impl(self);
PyObject *return_value = NULL;
static const char * const _keywords[] = {"msg", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "cancel", 0};
PyObject *argsbuf[1];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
PyObject *msg = Py_None;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf);
if (!args) {
goto exit;
}
if (!noptargs) {
goto skip_optional_pos;
}
msg = args[0];
skip_optional_pos:
return_value = _asyncio_Task_cancel_impl(self, msg);
exit:
return return_value;
}
PyDoc_STRVAR(_asyncio_Task_get_stack__doc__,
@ -832,4 +870,4 @@ _asyncio__leave_task(PyObject *module, PyObject *const *args, Py_ssize_t nargs,
exit:
return return_value;
}
/*[clinic end generated code: output=585ba1f8de5b4103 input=a9049054013a1b77]*/
/*[clinic end generated code: output=6ed4cfda8fc516ad input=a9049054013a1b77]*/