]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-96348: Deprecate the 3-arg signature of coroutine.throw and generator.throw (GH...
authorOfey Chan <ofey206@gmail.com>
Fri, 30 Sep 2022 08:43:02 +0000 (16:43 +0800)
committerGitHub <noreply@github.com>
Fri, 30 Sep 2022 08:43:02 +0000 (09:43 +0100)
14 files changed:
Doc/reference/datamodel.rst
Doc/reference/expressions.rst
Doc/whatsnew/3.12.rst
Lib/contextlib.py
Lib/test/test_asyncgen.py
Lib/test/test_asyncio/test_futures.py
Lib/test/test_coroutines.py
Lib/test/test_generators.py
Lib/test/test_types.py
Misc/ACKS
Misc/NEWS.d/next/Core and Builtins/2022-08-31-18-46-13.gh-issue-96348.xzCoTP.rst [new file with mode: 0644]
Modules/_asynciomodule.c
Objects/genobject.c
Objects/iterobject.c

index 758f3aef3ee34d02fd1172e8bbf2da040b7f42d4..c93269ab04b64f99863d009f17b6f63b93ab3349 100644 (file)
@@ -2996,6 +2996,11 @@ generators, coroutines do not directly support iteration.
    above.  If the exception is not caught in the coroutine, it propagates
    back to the caller.
 
+   .. versionchanged:: 3.12
+
+      The second signature \(type\[, value\[, traceback\]\]\) is deprecated and
+      may be removed in a future version of Python.
+
 .. method:: coroutine.close()
 
    Causes the coroutine to clean itself up and exit.  If the coroutine
index 6d23e473cdcd41dc1eb024a6ac0f233e99e70a7a..a6ca55dafe536520510847059944e9de30709d59 100644 (file)
@@ -582,6 +582,11 @@ is already executing raises a :exc:`ValueError` exception.
    :attr:`~BaseException.__traceback__` attribute stored in *value* may
    be cleared.
 
+   .. versionchanged:: 3.12
+
+      The second signature \(type\[, value\[, traceback\]\]\) is deprecated and
+      may be removed in a future version of Python.
+
 .. index:: exception: GeneratorExit
 
 
@@ -738,7 +743,8 @@ which are used to control the execution of a generator function.
    because there is no yield expression that could receive the value.
 
 
-.. coroutinemethod:: agen.athrow(type[, value[, traceback]])
+.. coroutinemethod:: agen.athrow(value)
+                     agen.athrow(type[, value[, traceback]])
 
    Returns an awaitable that raises an exception of type ``type`` at the point
    where the asynchronous generator was paused, and returns the next value
@@ -750,6 +756,11 @@ which are used to control the execution of a generator function.
    raises a different exception, then when the awaitable is run that exception
    propagates to the caller of the awaitable.
 
+   .. versionchanged:: 3.12
+
+      The second signature \(type\[, value\[, traceback\]\]\) is deprecated and
+      may be removed in a future version of Python.
+
 .. index:: exception: GeneratorExit
 
 
index 2a31160decc163aa95e28138a1afcca2a65fc07a..e14a2bd0133b8ac66b149d977270cc0941d9d2b1 100644 (file)
@@ -182,6 +182,11 @@ Deprecated
   and tailor them to your needs.
   (Contributed by Erlend E. Aasland in :gh:`90016`.)
 
+* The 3-arg signatures (type, value, traceback) of :meth:`~coroutine.throw`,
+  :meth:`~generator.throw` and :meth:`~agen.athrow` are deprecated and
+  may be removed in a future version of Python. Use the single-arg versions
+  of these functions instead. (Contributed by Ofey Chan in :gh:`89874`.)
+
 
 Pending Removal in Python 3.13
 ------------------------------
index 625bb33b12d5fd17638934215fbe8b7cd0bffe2d..d5822219b3e25b356b94ca0eb9f2cdba188f1e0e 100644 (file)
@@ -152,7 +152,7 @@ class _GeneratorContextManager(
                 # tell if we get the same exception back
                 value = typ()
             try:
-                self.gen.throw(typ, value, traceback)
+                self.gen.throw(value)
             except StopIteration as exc:
                 # Suppress StopIteration *unless* it's the same exception that
                 # was passed to throw().  This prevents a StopIteration
@@ -219,7 +219,7 @@ class _AsyncGeneratorContextManager(
                 # tell if we get the same exception back
                 value = typ()
             try:
-                await self.gen.athrow(typ, value, traceback)
+                await self.gen.athrow(value)
             except StopAsyncIteration as exc:
                 # Suppress StopIteration *unless* it's the same exception that
                 # was passed to throw().  This prevents a StopIteration
index fb22f411c2e2967f7674759c52cdbe176c897e29..f6184c0cab469464101d309fa60e4dbc04aa81aa 100644 (file)
@@ -2,6 +2,7 @@ import inspect
 import types
 import unittest
 import contextlib
+import warnings
 
 from test.support.import_helper import import_module
 from test.support import gc_collect, requires_working_socket
@@ -377,6 +378,13 @@ class AsyncGenTest(unittest.TestCase):
 
         self.compare_generators(sync_gen_wrapper(), async_gen_wrapper())
 
+    def test_async_gen_3_arg_deprecation_warning(self):
+        async def gen():
+            yield 123
+
+        with self.assertWarns(DeprecationWarning):
+            gen().athrow(GeneratorExit, GeneratorExit(), None)
+
     def test_async_gen_api_01(self):
         async def gen():
             yield 123
@@ -650,7 +658,7 @@ class AsyncGenAsyncioTest(unittest.TestCase):
             agen = agenfn()
             with contextlib.closing(anext(agen, "default").__await__()) as g:
                 self.assertEqual(g.send(None), 1)
-                self.assertEqual(g.throw(MyError, MyError(), None), 2)
+                self.assertEqual(g.throw(MyError()), 2)
                 try:
                     g.send(None)
                 except StopIteration as e:
@@ -663,9 +671,9 @@ class AsyncGenAsyncioTest(unittest.TestCase):
             agen = agenfn()
             with contextlib.closing(anext(agen, "default").__await__()) as g:
                 self.assertEqual(g.send(None), 1)
-                self.assertEqual(g.throw(MyError, MyError(), None), 2)
+                self.assertEqual(g.throw(MyError()), 2)
                 with self.assertRaises(MyError):
-                    g.throw(MyError, MyError(), None)
+                    g.throw(MyError())
 
         def test3(anext):
             agen = agenfn()
@@ -692,9 +700,9 @@ class AsyncGenAsyncioTest(unittest.TestCase):
             agen = agenfn()
             with contextlib.closing(anext(agen, "default").__await__()) as g:
                 self.assertEqual(g.send(None), 10)
-                self.assertEqual(g.throw(MyError, MyError(), None), 20)
+                self.assertEqual(g.throw(MyError()), 20)
                 with self.assertRaisesRegex(MyError, 'val'):
-                    g.throw(MyError, MyError('val'), None)
+                    g.throw(MyError('val'))
 
         def test5(anext):
             @types.coroutine
@@ -713,7 +721,7 @@ class AsyncGenAsyncioTest(unittest.TestCase):
             with contextlib.closing(anext(agen, "default").__await__()) as g:
                 self.assertEqual(g.send(None), 10)
                 with self.assertRaisesRegex(StopIteration, 'default'):
-                    g.throw(MyError, MyError(), None)
+                    g.throw(MyError())
 
         def test6(anext):
             @types.coroutine
@@ -728,7 +736,7 @@ class AsyncGenAsyncioTest(unittest.TestCase):
             agen = agenfn()
             with contextlib.closing(anext(agen, "default").__await__()) as g:
                 with self.assertRaises(MyError):
-                    g.throw(MyError, MyError(), None)
+                    g.throw(MyError())
 
         def run_test(test):
             with self.subTest('pure-Python anext()'):
index f4a46ec90a16fe7e30be27ce203a32cd32c3d93e..11d4273930804f66ab5cdec039dc1a8cc9b816ca 100644 (file)
@@ -10,6 +10,7 @@ from unittest import mock
 from types import GenericAlias
 import asyncio
 from asyncio import futures
+import warnings
 from test.test_asyncio import utils as test_utils
 from test import support
 
@@ -619,10 +620,14 @@ class BaseFutureTests:
     def test_future_iter_throw(self):
         fut = self._new_future(loop=self.loop)
         fi = iter(fut)
-        self.assertRaises(TypeError, fi.throw,
-                          Exception, Exception("elephant"), 32)
-        self.assertRaises(TypeError, fi.throw,
-                          Exception("elephant"), Exception("elephant"))
+        with self.assertWarns(DeprecationWarning):
+            self.assertRaises(Exception, fi.throw, Exception, Exception("zebra"), None)
+        with warnings.catch_warnings():
+            warnings.filterwarnings("ignore", category=DeprecationWarning)
+            self.assertRaises(TypeError, fi.throw,
+                            Exception, Exception("elephant"), 32)
+            self.assertRaises(TypeError, fi.throw,
+                            Exception("elephant"), Exception("elephant"))
         self.assertRaises(TypeError, fi.throw, list)
 
     def test_future_del_collect(self):
index 8fff2d47c10fd5665ee2a5c9bca4f659c536de3c..9a2279d353ffa41ce207f8dab9297a6a02c0bd6d 100644 (file)
@@ -709,9 +709,16 @@ class CoroutineTest(unittest.TestCase):
         aw = coro.__await__()
         next(aw)
         with self.assertRaises(ZeroDivisionError):
-            aw.throw(ZeroDivisionError, None, None)
+            aw.throw(ZeroDivisionError())
         self.assertEqual(N, 102)
 
+        coro = foo()
+        aw = coro.__await__()
+        next(aw)
+        with self.assertRaises(ZeroDivisionError):
+            with self.assertWarns(DeprecationWarning):
+                aw.throw(ZeroDivisionError, ZeroDivisionError(), None)
+
     def test_func_11(self):
         async def func(): pass
         coro = func()
index e5aa7da1e0df13b8a666759cca944bf2082e08f4..fb2d9ced0633f16f99a8ef2afe3dbfd3c442a77a 100644 (file)
@@ -342,6 +342,15 @@ class ExceptionTest(unittest.TestCase):
         with self.assertRaises(StopIteration):
             gen.throw(E)
 
+    def test_gen_3_arg_deprecation_warning(self):
+        def g():
+            yield 42
+
+        gen = g()
+        with self.assertWarns(DeprecationWarning):
+            with self.assertRaises(TypeError):
+                gen.throw(TypeError, TypeError(24), None)
+
     def test_stopiteration_error(self):
         # See also PEP 479.
 
@@ -2113,6 +2122,12 @@ caught ValueError ()
 >>> g.throw(ValueError("xyz"))  # value only
 caught ValueError (xyz)
 
+>>> import warnings
+>>> warnings.filterwarnings("ignore", category=DeprecationWarning)
+
+# Filter DeprecationWarning: regarding the (type, val, tb) signature of throw().
+# Deprecation warnings are re-enabled below.
+
 >>> g.throw(ValueError, ValueError(1))   # value+matching type
 caught ValueError (1)
 
@@ -2181,6 +2196,12 @@ Traceback (most recent call last):
   ...
 ValueError: 7
 
+>>> warnings.filters.pop(0)
+('ignore', None, <class 'DeprecationWarning'>, None, 0)
+
+# Re-enable DeprecationWarning: the (type, val, tb) exception representation is deprecated,
+#                               and may be removed in a future version of Python.
+
 Plain "raise" inside a generator should preserve the traceback (#13188).
 The traceback should have 3 levels:
 - g.throw()
index f00da0a758d46f654239e043cf6d13380e90fc34..af095632a36fcb73a8cd1914ca7da229180c6b17 100644 (file)
@@ -2072,7 +2072,7 @@ class CoroutineTests(unittest.TestCase):
         wrapper = foo()
         wrapper.send(None)
         with self.assertRaisesRegex(Exception, 'ham'):
-            wrapper.throw(Exception, Exception('ham'))
+            wrapper.throw(Exception('ham'))
 
         # decorate foo second time
         foo = types.coroutine(foo)
index 0edea9219d05ef4b7b544eea679cde3d8961d4c3..fc0e745e28ceab1372ce718300733b8cdca8c742 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -297,6 +297,7 @@ Michael Cetrulo
 Dave Chambers
 Pascal Chambon
 Nicholas Chammas
+Ofey Chan
 John Chandler
 Hye-Shik Chang
 Jeffrey Chang
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-08-31-18-46-13.gh-issue-96348.xzCoTP.rst b/Misc/NEWS.d/next/Core and Builtins/2022-08-31-18-46-13.gh-issue-96348.xzCoTP.rst
new file mode 100644 (file)
index 0000000..5d3bd17
--- /dev/null
@@ -0,0 +1,2 @@
+Emit a DeprecationWarning when :meth:`~generator.throw`, :meth:`~coroutine.throw` or :meth:`~agen.athrow`
+are called with more than one argument.
index 9d2f83bf6c73c1a830197586e57a0706e56f1d62..5a5881b873e245c9e3aa3739fca3557ff697c0d6 100644 (file)
@@ -1668,6 +1668,14 @@ FutureIter_throw(futureiterobject *self, PyObject *const *args, Py_ssize_t nargs
     if (!_PyArg_CheckPositional("throw", nargs, 1, 3)) {
         return NULL;
     }
+    if (nargs > 1) {
+        if (PyErr_WarnEx(PyExc_DeprecationWarning,
+                            "the (type, exc, tb) signature of throw() is deprecated, "
+                            "use the single-arg signature instead.",
+                            1) < 0) {
+            return NULL;
+        }
+    }
 
     type = args[0];
     if (nargs == 3) {
index da4afecc69c8c1f65b8f84dee40cf982a8879a8d..ad4fbed6d8d57973ea4b70a4dd692eaf2171baad 100644 (file)
@@ -418,7 +418,9 @@ PyDoc_STRVAR(throw_doc,
 throw(type[,value[,tb]])\n\
 \n\
 Raise exception in generator, return next yielded value or raise\n\
-StopIteration.");
+StopIteration.\n\
+the (type, val, tb) signature is deprecated, \n\
+and may be removed in a future version of Python.");
 
 static PyObject *
 _gen_throw(PyGenObject *gen, int close_on_genexit,
@@ -559,6 +561,14 @@ gen_throw(PyGenObject *gen, PyObject *const *args, Py_ssize_t nargs)
     if (!_PyArg_CheckPositional("throw", nargs, 1, 3)) {
         return NULL;
     }
+    if (nargs > 1) {
+        if (PyErr_WarnEx(PyExc_DeprecationWarning,
+                            "the (type, exc, tb) signature of throw() is deprecated, "
+                            "use the single-arg signature instead.",
+                            1) < 0) {
+            return NULL;
+        }
+    }
     typ = args[0];
     if (nargs == 3) {
         val = args[1];
@@ -1147,7 +1157,10 @@ PyDoc_STRVAR(coro_throw_doc,
 throw(type[,value[,traceback]])\n\
 \n\
 Raise exception in coroutine, return next iterated value or raise\n\
-StopIteration.");
+StopIteration.\n\
+the (type, val, tb) signature is deprecated, \n\
+and may be removed in a future version of Python.");
+
 
 PyDoc_STRVAR(coro_close_doc,
 "close() -> raise GeneratorExit inside coroutine.");
@@ -1500,6 +1513,14 @@ async_gen_aclose(PyAsyncGenObject *o, PyObject *arg)
 static PyObject *
 async_gen_athrow(PyAsyncGenObject *o, PyObject *args)
 {
+    if (PyTuple_GET_SIZE(args) > 1) {
+        if (PyErr_WarnEx(PyExc_DeprecationWarning,
+                            "the (type, exc, tb) signature of athrow() is deprecated, "
+                            "use the single-arg signature instead.",
+                            1) < 0) {
+            return NULL;
+        }
+    }
     if (async_gen_init_hooks(o)) {
         return NULL;
     }
@@ -1537,7 +1558,12 @@ PyDoc_STRVAR(async_asend_doc,
 "asend(v) -> send 'v' in generator.");
 
 PyDoc_STRVAR(async_athrow_doc,
-"athrow(typ[,val[,tb]]) -> raise exception in generator.");
+"athrow(value)\n\
+athrow(type[,value[,tb]])\n\
+\n\
+raise exception in generator.\n\
+the (type, val, tb) signature is deprecated, \n\
+and may be removed in a future version of Python.");
 
 static PyMethodDef async_gen_methods[] = {
     {"asend", (PyCFunction)async_gen_asend, METH_O, async_asend_doc},
index 1732a037600c9e9a5fbfa64d203a728545c267dd..62c36146d64f690923d42b4080836690fb64b7f2 100644 (file)
@@ -428,8 +428,13 @@ return next yielded value or raise StopIteration.");
 
 
 PyDoc_STRVAR(throw_doc,
-"throw(typ[,val[,tb]]) -> raise exception in the wrapped iterator,\n\
-return next yielded value or raise StopIteration.");
+"throw(value)\n\
+throw(typ[,val[,tb]])\n\
+\n\
+raise exception in the wrapped iterator, return next yielded value\n\
+or raise StopIteration.\n\
+the (type, val, tb) signature is deprecated, \n\
+and may be removed in a future version of Python.");
 
 
 PyDoc_STRVAR(close_doc,