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
: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
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
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
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
------------------------------
# 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
# 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
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
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
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:
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()
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
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
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()'):
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
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):
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()
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.
>>> 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)
...
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()
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)
Dave Chambers
Pascal Chambon
Nicholas Chammas
+Ofey Chan
John Chandler
Hye-Shik Chang
Jeffrey Chang
--- /dev/null
+Emit a DeprecationWarning when :meth:`~generator.throw`, :meth:`~coroutine.throw` or :meth:`~agen.athrow`
+are called with more than one argument.
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) {
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,
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];
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.");
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;
}
"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},
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,