Skip to content
6 changes: 6 additions & 0 deletions Lib/asyncio/futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import contextvars
import logging
import sys
import warnings
from types import GenericAlias

from . import base_futures
Expand Down Expand Up @@ -153,6 +154,11 @@ def cancel(self, msg=None):
change the future's state to cancelled, schedule the callbacks and
return True.
"""
if msg is not None:
warnings.warn("Passing 'msg' argument to Future.cancel() "
"is deprecated since Python 3.11, and "
"scheduled for removal in Python 3.14.",
DeprecationWarning, stacklevel=2)
self.__log_traceback = False
if self._state != _PENDING:
return False
Expand Down
5 changes: 5 additions & 0 deletions Lib/asyncio/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ def cancel(self, msg=None):

This also increases the task's count of cancellation requests.
"""
if msg is not None:
warnings.warn("Passing 'msg' argument to Task.cancel() "
"is deprecated since Python 3.11, and "
"scheduled for removal in Python 3.14.",
DeprecationWarning, stacklevel=2)
self._log_traceback = False
if self.done():
return False
Expand Down
12 changes: 10 additions & 2 deletions Lib/test/test_asyncio/test_futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,14 +228,22 @@ def test_future_cancel_message_getter(self):
self.assertTrue(hasattr(f, '_cancel_message'))
self.assertEqual(f._cancel_message, None)

f.cancel('my message')
with self.assertWarnsRegex(
DeprecationWarning,
"Passing 'msg' argument"
):
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')
with self.assertWarnsRegex(
DeprecationWarning,
"Passing 'msg' argument"
):
f.cancel('my message')
f._cancel_message = 'my new message'
self.assertEqual(f._cancel_message, 'my new message')

Expand Down
12 changes: 10 additions & 2 deletions Lib/test/test_asyncio/test_taskgroups.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,11 @@ async def runner():
await asyncio.sleep(0.1)

self.assertFalse(r.done())
r.cancel("test")
with self.assertWarnsRegex(
DeprecationWarning,
"Passing 'msg' argument"
):
r.cancel("test")
with self.assertRaises(asyncio.CancelledError) as cm:
await r

Expand Down Expand Up @@ -252,7 +256,11 @@ async def runner():
await asyncio.sleep(0.1)

self.assertFalse(r.done())
r.cancel("test")
with self.assertWarnsRegex(
DeprecationWarning,
"Passing 'msg' argument"
):
r.cancel("test")
with self.assertRaises(asyncio.CancelledError) as cm:
await r

Expand Down
54 changes: 47 additions & 7 deletions Lib/test/test_asyncio/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,11 @@ async def coro():
self.assertTrue(hasattr(t, '_cancel_message'))
self.assertEqual(t._cancel_message, None)

t.cancel('my message')
with self.assertWarnsRegex(
DeprecationWarning,
"Passing 'msg' argument"
):
t.cancel('my message')
self.assertEqual(t._cancel_message, 'my message')

with self.assertRaises(asyncio.CancelledError) as cm:
Expand All @@ -133,7 +137,11 @@ def test_task_cancel_message_setter(self):
async def coro():
pass
t = self.new_task(self.loop, coro())
t.cancel('my message')
with self.assertWarnsRegex(
DeprecationWarning,
"Passing 'msg' argument"
):
t.cancel('my message')
t._cancel_message = 'my new message'
self.assertEqual(t._cancel_message, 'my new message')

Expand Down Expand Up @@ -590,7 +598,14 @@ async def sleep():
async def coro():
task = self.new_task(loop, sleep())
await asyncio.sleep(0)
task.cancel(*cancel_args)
if cancel_args not in ((), (None,)):
with self.assertWarnsRegex(
DeprecationWarning,
"Passing 'msg' argument"
):
task.cancel(*cancel_args)
else:
task.cancel(*cancel_args)
done, pending = await asyncio.wait([task])
task.result()

Expand Down Expand Up @@ -624,7 +639,14 @@ async def sleep():
async def coro():
task = self.new_task(loop, sleep())
await asyncio.sleep(0)
task.cancel(*cancel_args)
if cancel_args not in ((), (None,)):
with self.assertWarnsRegex(
DeprecationWarning,
"Passing 'msg' argument"
):
task.cancel(*cancel_args)
else:
task.cancel(*cancel_args)
done, pending = await asyncio.wait([task])
task.exception()

Expand All @@ -647,10 +669,17 @@ async def sleep():
fut.set_result(None)
await asyncio.sleep(10)

def cancel(task, msg):
with self.assertWarnsRegex(
DeprecationWarning,
"Passing 'msg' argument"
):
task.cancel(msg)

async def coro():
inner_task = self.new_task(loop, sleep())
await fut
loop.call_soon(inner_task.cancel, 'msg')
loop.call_soon(cancel, inner_task, 'msg')
try:
await inner_task
except asyncio.CancelledError as ex:
Expand All @@ -676,7 +705,11 @@ async def sleep():
async def coro():
task = self.new_task(loop, sleep())
# We deliberately leave out the sleep here.
task.cancel('my message')
with self.assertWarnsRegex(
DeprecationWarning,
"Passing 'msg' argument"
):
task.cancel('my message')
done, pending = await asyncio.wait([task])
task.exception()

Expand Down Expand Up @@ -2326,7 +2359,14 @@ async def test():
async def main():
qwe = self.new_task(loop, test())
await asyncio.sleep(0.2)
qwe.cancel(*cancel_args)
if cancel_args not in ((), (None,)):
with self.assertWarnsRegex(
DeprecationWarning,
"Passing 'msg' argument"
):
qwe.cancel(*cancel_args)
else:
qwe.cancel(*cancel_args)
await qwe

try:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Deprecate passing a message into :meth:`asyncio.Future.cancel` and
:meth:`asyncio.Task.cancel`
20 changes: 20 additions & 0 deletions Modules/_asynciomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,16 @@ static PyObject *
_asyncio_Future_cancel_impl(FutureObj *self, PyObject *msg)
/*[clinic end generated code: output=3edebbc668e5aba3 input=925eb545251f2c5a]*/
{
if (msg != Py_None) {
if (PyErr_WarnEx(PyExc_DeprecationWarning,
"Passing 'msg' argument to Future.cancel() "
"is deprecated since Python 3.11, and "
"scheduled for removal in Python 3.14.",
2))
{
return NULL;
}
}
ENSURE_FUTURE_ALIVE(self)
return future_cancel(self, msg);
}
Expand Down Expand Up @@ -2199,6 +2209,16 @@ static PyObject *
_asyncio_Task_cancel_impl(TaskObj *self, PyObject *msg)
/*[clinic end generated code: output=c66b60d41c74f9f1 input=7bb51bf25974c783]*/
{
if (msg != Py_None) {
if (PyErr_WarnEx(PyExc_DeprecationWarning,
"Passing 'msg' argument to Task.cancel() "
"is deprecated since Python 3.11, and "
"scheduled for removal in Python 3.14.",
2))
{
return NULL;
}
}
self->task_log_tb = 0;

if (self->task_state != STATE_PENDING) {
Expand Down