bpo-31033: Add a msg argument to Future.cancel() and Task.cancel() (GH-19979)
This commit is contained in:
parent
fe1176e882
commit
1ce5841eca
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Add a ``msg`` argument to :meth:`Future.cancel` and :meth:`Task.cancel`.
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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]*/
|
||||
|
|
Loading…
Reference in New Issue