]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-46829: Deprecate passing a message into Future.cancel() and Task.cancel() (GH...
authorAndrew Svetlov <andrew.svetlov@gmail.com>
Wed, 23 Mar 2022 15:43:05 +0000 (17:43 +0200)
committerGitHub <noreply@github.com>
Wed, 23 Mar 2022 15:43:05 +0000 (08:43 -0700)
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.

Doc/library/asyncio-future.rst
Doc/library/asyncio-task.rst
Lib/asyncio/futures.py
Lib/asyncio/tasks.py
Lib/test/test_asyncio/test_futures.py
Lib/test/test_asyncio/test_taskgroups.py
Lib/test/test_asyncio/test_tasks.py
Misc/NEWS.d/next/Core and Builtins/2022-03-12-21-07-21.bpo-46829.cpGoPV.rst [new file with mode: 0644]
Modules/_asynciomodule.c

index 7426e8291e1424a3142fdbd15f2a15893df36d53..f74f2e6f8935eaded0ae17f5609b7dd5ab109b4e 100644 (file)
@@ -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.
index 294f5ab2b22f9d197f12b2077a4c4923e1d516dd..21a4cb5820bd1c90f045b232cda244c2fa0ef3ab 100644 (file)
@@ -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:
 
index bfd4ee926f0287f4e53aad9f8df3b4bfc219c3d4..08c79e7b5109bfa55cb76f35ebd3bb72f65d8ed4 100644 (file)
@@ -8,6 +8,7 @@ import concurrent.futures
 import contextvars
 import logging
 import sys
+import warnings
 from types import GenericAlias
 
 from . import base_futures
@@ -150,6 +151,11 @@ class Future:
         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
index e876f8d002ce4fd0dba6459f91184d158069a373..27fe58da15136a0a04e52a23dab22942e593bd31 100644 (file)
@@ -207,6 +207,11 @@ class Task(futures._PyFuture):  # Inherit Python Task implementation
 
         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
index 84d7d45af949ef6a63720632a47fd184f1c3d660..cf677f6a9511554d57c8c1db1c0276fad030609e 100644 (file)
@@ -228,14 +228,22 @@ class BaseFutureTests:
         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')
 
index dea5d6de5242045fac1ec9f826c08da92df64c0c..69369a6100a8fd148ff4e51e99aa61c6836d83d6 100644 (file)
@@ -191,12 +191,10 @@ class TestTaskGroup(unittest.IsolatedAsyncioTestCase):
         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 @@ class TestTaskGroup(unittest.IsolatedAsyncioTestCase):
         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
index b86646ee398ae3e4460887c3f3c5998a7d965c4b..8df1957bbe9e7a3d14dcfc2b6d946cb2da68af21 100644 (file)
@@ -113,7 +113,11 @@ class BaseTaskTests:
         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 @@ class BaseTaskTests:
         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 @@ class BaseTaskTests:
                 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 @@ class BaseTaskTests:
                 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 @@ class BaseTaskTests:
             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 @@ class BaseTaskTests:
         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 @@ class BaseTaskTests:
                 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 (file)
index 0000000..9d260f5
--- /dev/null
@@ -0,0 +1,2 @@
+Deprecate passing a message into :meth:`asyncio.Future.cancel` and
+:meth:`asyncio.Task.cancel`
index c3e9cb2fa2a3039149a75b1240a131030a2c109c..5b8555e74b375179d6c9dd207c81c48fc5ad6e03 100644 (file)
@@ -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) {