[Python-checkins] bpo-46829: Deprecate passing a message into Future.cancel() and Task.cancel() (GH-31840)
gvanrossum
webhook-mailer at python.org
Wed Mar 23 11:43:16 EDT 2022
https://github.com/python/cpython/commit/0360e9f34659e7d7f3dae021b82f78452db8c714
commit: 0360e9f34659e7d7f3dae021b82f78452db8c714
branch: main
author: Andrew Svetlov <andrew.svetlov at gmail.com>
committer: gvanrossum <gvanrossum at gmail.com>
date: 2022-03-23T08:43:05-07:00
summary:
bpo-46829: Deprecate passing a message into Future.cancel() and Task.cancel() (GH-31840)
After a long deliberation we ended up feeling that the message argument for Future.cancel(), added in 3.9, was a bad idea, so we're deprecating it in 3.11 and plan to remove it in 3.13.
files:
A Misc/NEWS.d/next/Core and Builtins/2022-03-12-21-07-21.bpo-46829.cpGoPV.rst
M Doc/library/asyncio-future.rst
M Doc/library/asyncio-task.rst
M Lib/asyncio/futures.py
M Lib/asyncio/tasks.py
M Lib/test/test_asyncio/test_futures.py
M Lib/test/test_asyncio/test_taskgroups.py
M Lib/test/test_asyncio/test_tasks.py
M Modules/_asynciomodule.c
diff --git a/Doc/library/asyncio-future.rst b/Doc/library/asyncio-future.rst
index 7426e8291e142..f74f2e6f8935e 100644
--- a/Doc/library/asyncio-future.rst
+++ b/Doc/library/asyncio-future.rst
@@ -196,6 +196,11 @@ Future Object
.. versionchanged:: 3.9
Added the *msg* parameter.
+ .. deprecated-removed:: 3.11 3.14
+ *msg* parameter is ambiguous when multiple :meth:`cancel`
+ are called with different cancellation messages.
+ The argument will be removed.
+
.. method:: exception()
Return the exception that was set on this Future.
@@ -276,3 +281,8 @@ the Future has a result::
- :meth:`asyncio.Future.cancel` accepts an optional ``msg`` argument,
but :func:`concurrent.futures.cancel` does not.
+
+ .. deprecated-removed:: 3.11 3.14
+ *msg* parameter is ambiguous when multiple :meth:`cancel`
+ are called with different cancellation messages.
+ The argument will be removed.
diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst
index 294f5ab2b22f9..21a4cb5820bd1 100644
--- a/Doc/library/asyncio-task.rst
+++ b/Doc/library/asyncio-task.rst
@@ -810,8 +810,10 @@ Task Object
.. versionchanged:: 3.9
Added the *msg* parameter.
- .. versionchanged:: 3.11
- The ``msg`` parameter is propagated from cancelled task to its awaiter.
+ .. deprecated-removed:: 3.11 3.14
+ *msg* parameter is ambiguous when multiple :meth:`cancel`
+ are called with different cancellation messages.
+ The argument will be removed.
.. _asyncio_example_task_cancel:
diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py
index bfd4ee926f028..08c79e7b5109b 100644
--- a/Lib/asyncio/futures.py
+++ b/Lib/asyncio/futures.py
@@ -8,6 +8,7 @@
import contextvars
import logging
import sys
+import warnings
from types import GenericAlias
from . import base_futures
@@ -150,6 +151,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
diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py
index e876f8d002ce4..27fe58da15136 100644
--- a/Lib/asyncio/tasks.py
+++ b/Lib/asyncio/tasks.py
@@ -207,6 +207,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
diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py
index 84d7d45af949e..cf677f6a95115 100644
--- a/Lib/test/test_asyncio/test_futures.py
+++ b/Lib/test/test_asyncio/test_futures.py
@@ -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')
diff --git a/Lib/test/test_asyncio/test_taskgroups.py b/Lib/test/test_asyncio/test_taskgroups.py
index dea5d6de52420..69369a6100a8f 100644
--- a/Lib/test/test_asyncio/test_taskgroups.py
+++ b/Lib/test/test_asyncio/test_taskgroups.py
@@ -191,12 +191,10 @@ async def runner():
await asyncio.sleep(0.1)
self.assertFalse(r.done())
- r.cancel("test")
+ r.cancel()
with self.assertRaises(asyncio.CancelledError) as cm:
await r
- self.assertEqual(cm.exception.args, ('test',))
-
self.assertEqual(NUM, 5)
async def test_taskgroup_07(self):
@@ -253,12 +251,10 @@ async def runner():
await asyncio.sleep(0.1)
self.assertFalse(r.done())
- r.cancel("test")
+ r.cancel()
with self.assertRaises(asyncio.CancelledError) as cm:
await r
- self.assertEqual(cm.exception.args, ('test',))
-
async def test_taskgroup_09(self):
t1 = t2 = None
diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py
index b86646ee398ae..8df1957bbe9e7 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -113,7 +113,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:
@@ -125,7 +129,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')
@@ -582,7 +590,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()
@@ -616,7 +631,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()
@@ -639,10 +661,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:
@@ -668,7 +697,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()
@@ -2029,7 +2062,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:
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-12-21-07-21.bpo-46829.cpGoPV.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-12-21-07-21.bpo-46829.cpGoPV.rst
new file mode 100644
index 0000000000000..9d260f5b1dddb
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-12-21-07-21.bpo-46829.cpGoPV.rst
@@ -0,0 +1,2 @@
+Deprecate passing a message into :meth:`asyncio.Future.cancel` and
+:meth:`asyncio.Task.cancel`
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index c3e9cb2fa2a30..5b8555e74b375 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -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);
}
@@ -2176,6 +2186,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) {
More information about the Python-checkins
mailing list