Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-96348: Deprecate the 3-arg signature of coroutine.throw, generator.throw and agen.athrow #96428

Merged
merged 30 commits into from
Sep 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b388f9a
feat: Deprecate gen.throw(typ, val, tb)
ofey404 Aug 30, 2022
713d77a
Update Objects/genobject.c
ofey404 Aug 31, 2022
fcfb65e
fix clear cases
ofey404 Sep 1, 2022
073aa62
Update Misc/NEWS.d/next/Core and Builtins/2022-08-31-18-46-13.gh-issu…
ofey404 Sep 3, 2022
1c6ed30
Filter remaining warnings.
ofey404 Sep 3, 2022
4881855
Add deprecated info in documentation.
ofey404 Sep 6, 2022
0158816
Use versionchanged instead of deprecated.
ofey404 Sep 7, 2022
8152b7c
Update Doc/reference/expressions.rst
ofey404 Sep 9, 2022
670d8f8
more docs and what's new entry
ofey404 Sep 9, 2022
0c20b35
Edit what's new.
ofey404 Sep 10, 2022
ba9a87c
Update Objects/genobject.c
ofey404 Sep 13, 2022
7f1dd7f
Update Objects/genobject.c
ofey404 Sep 13, 2022
0b16455
Update Lib/test/test_generators.py
ofey404 Sep 13, 2022
e204cc6
Update Objects/genobject.c
ofey404 Sep 13, 2022
0873dc3
Reversed agen.athrow() documentation, and what's new with method link.
ofey404 Sep 20, 2022
4a1539c
Apply suggestions from code review
ofey404 Sep 22, 2022
85b67cb
Add DeprecationWarning, doc and what's new to agen.athrow
ofey404 Sep 22, 2022
22bb65b
Update acks, and doc of anextawaitable_throw
ofey404 Sep 22, 2022
7b786e4
Add test_async_gen_3_arg_deprecation_warning.
ofey404 Sep 24, 2022
198645a
Apply formatting suggestions from code review
ofey404 Sep 25, 2022
528031c
patchcheck.py passed
ofey404 Sep 25, 2022
53c74db
Merge branch 'main' into ofey404/deprecate-get-throw
ofey404 Sep 25, 2022
93db966
Update Misc/NEWS.d/next/Core and Builtins/2022-08-31-18-46-13.gh-issu…
ofey404 Sep 25, 2022
0e56eb9
assert that deprecation warning is emitted
iritkatriel Sep 25, 2022
8738273
stage, exploring how to capture deprecation warning rather than supre…
ofey404 Sep 28, 2022
31b2e9f
Revert "stage, exploring how to capture deprecation warning rather th…
ofey404 Sep 28, 2022
8b701d6
Merge pull request #6 from iritkatriel/pr96428
ofey404 Sep 28, 2022
70e1f28
Merge branch 'main' into ofey404/deprecate-get-throw
iritkatriel Sep 28, 2022
ed2e83a
fix test
iritkatriel Sep 28, 2022
03fc6e0
Add a warning in FutureIter_throw
ofey404 Sep 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

@merwok merwok Sep 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future PRs, not that code should generally be marked up like

      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
Expand Down
13 changes: 12 additions & 1 deletion Doc/reference/expressions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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
Expand All @@ -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.
iritkatriel marked this conversation as resolved.
Show resolved Hide resolved

.. index:: exception: GeneratorExit


Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
------------------------------
Expand Down
4 changes: 2 additions & 2 deletions Lib/contextlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def __exit__(self, typ, value, traceback):
# 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
Expand Down Expand Up @@ -219,7 +219,7 @@ async def __aexit__(self, typ, value, traceback):
# 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
Expand Down
22 changes: 15 additions & 7 deletions Lib/test/test_asyncgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
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
Expand Down Expand Up @@ -377,6 +378,13 @@ async def async_gen_wrapper():

self.compare_generators(sync_gen_wrapper(), async_gen_wrapper())

def test_async_gen_3_arg_deprecation_warning(self):
async def gen():
yield 123
ofey404 marked this conversation as resolved.
Show resolved Hide resolved

with self.assertWarns(DeprecationWarning):
gen().athrow(GeneratorExit, GeneratorExit(), None)

def test_async_gen_api_01(self):
async def gen():
yield 123
Expand Down Expand Up @@ -650,7 +658,7 @@ def test1(anext):
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:
Expand All @@ -663,9 +671,9 @@ def test2(anext):
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()
Expand All @@ -692,9 +700,9 @@ async def 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
Expand All @@ -713,7 +721,7 @@ async def agenfn():
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
Expand All @@ -728,7 +736,7 @@ async def agenfn():
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()'):
Expand Down
13 changes: 9 additions & 4 deletions Lib/test/test_asyncio/test_futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
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

Expand Down Expand Up @@ -619,10 +620,14 @@ def test_future_stop_iteration_args(self):
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):
Expand Down
9 changes: 8 additions & 1 deletion Lib/test/test_coroutines.py
Original file line number Diff line number Diff line change
Expand Up @@ -709,9 +709,16 @@ async def foo():
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()
Expand Down
21 changes: 21 additions & 0 deletions Lib/test/test_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,15 @@ def generator():
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.

Expand Down Expand Up @@ -2113,6 +2122,12 @@ def printsolution(self, x):
>>> 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)

Expand Down Expand Up @@ -2181,6 +2196,12 @@ def printsolution(self, x):
...
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()
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2072,7 +2072,7 @@ def foo(): return gen
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)
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ Michael Cetrulo
Dave Chambers
Pascal Chambon
Nicholas Chammas
Ofey Chan
John Chandler
Hye-Shik Chang
Jeffrey Chang
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
8 changes: 8 additions & 0 deletions Modules/_asynciomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
32 changes: 29 additions & 3 deletions Objects/genobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
iritkatriel marked this conversation as resolved.
Show resolved Hide resolved

static PyObject *
_gen_throw(PyGenObject *gen, int close_on_genexit,
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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.");
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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},
Expand Down
9 changes: 7 additions & 2 deletions Objects/iterobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down