mirror of https://github.com/python/cpython
gh-96348: Deprecate the 3-arg signature of coroutine.throw and generator.throw (GH-96428)
This commit is contained in:
parent
9a11ed8e50
commit
83a3de4e06
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
------------------------------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()'):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -297,6 +297,7 @@ Michael Cetrulo
|
|||
Dave Chambers
|
||||
Pascal Chambon
|
||||
Nicholas Chammas
|
||||
Ofey Chan
|
||||
John Chandler
|
||||
Hye-Shik Chang
|
||||
Jeffrey Chang
|
||||
|
|
|
@ -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.
|
|
@ -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) {
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue