Mailing List Archive

[3.12] GH-117894: prevent aclose()/athrow() being re-used after StopIteration (GH-117851) (GH-118226)
https://github.com/python/cpython/commit/fc1732ce3f26af5d082adac03ee73ef4814a5feb
commit: fc1732ce3f26af5d082adac03ee73ef4814a5feb
branch: 3.12
author: Thomas Grainger <tagrain@gmail.com>
committer: encukou <encukou@gmail.com>
date: 2024-04-25T09:13:47+02:00
summary:

[3.12] GH-117894: prevent aclose()/athrow() being re-used after StopIteration (GH-117851) (GH-118226)

(cherry picked from commit 7d369d471cf2b067c4d795d70b75201c48b46f5b)

files:
A Misc/NEWS.d/next/Core and Builtins/2024-04-15-13-53-59.gh-issue-117894.8LpZ6m.rst
M Lib/test/test_asyncgen.py
M Objects/genobject.c

diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py
index 9a9343781cd40c..ee7e3968b00b26 100644
--- a/Lib/test/test_asyncgen.py
+++ b/Lib/test/test_asyncgen.py
@@ -395,7 +395,11 @@ async def gen():
yield 123

with self.assertWarns(DeprecationWarning):
- gen().athrow(GeneratorExit, GeneratorExit(), None)
+ x = gen().athrow(GeneratorExit, GeneratorExit(), None)
+ with self.assertRaises(GeneratorExit):
+ x.send(None)
+ del x
+ gc_collect()

def test_async_gen_api_01(self):
async def gen():
@@ -1653,6 +1657,62 @@ async def run():

self.loop.run_until_complete(run())

+ def test_async_gen_throw_same_aclose_coro_twice(self):
+ async def async_iterate():
+ yield 1
+ yield 2
+
+ it = async_iterate()
+ nxt = it.aclose()
+ with self.assertRaises(StopIteration):
+ nxt.throw(GeneratorExit)
+
+ with self.assertRaisesRegex(
+ RuntimeError,
+ r"cannot reuse already awaited aclose\(\)/athrow\(\)"
+ ):
+ nxt.throw(GeneratorExit)
+
+ def test_async_gen_throw_custom_same_aclose_coro_twice(self):
+ async def async_iterate():
+ yield 1
+ yield 2
+
+ it = async_iterate()
+
+ class MyException(Exception):
+ pass
+
+ nxt = it.aclose()
+ with self.assertRaises(MyException):
+ nxt.throw(MyException)
+
+ with self.assertRaisesRegex(
+ RuntimeError,
+ r"cannot reuse already awaited aclose\(\)/athrow\(\)"
+ ):
+ nxt.throw(MyException)
+
+ def test_async_gen_throw_custom_same_athrow_coro_twice(self):
+ async def async_iterate():
+ yield 1
+ yield 2
+
+ it = async_iterate()
+
+ class MyException(Exception):
+ pass
+
+ nxt = it.athrow(MyException)
+ with self.assertRaises(MyException):
+ nxt.throw(MyException)
+
+ with self.assertRaisesRegex(
+ RuntimeError,
+ r"cannot reuse already awaited aclose\(\)/athrow\(\)"
+ ):
+ nxt.throw(MyException)
+
def test_async_gen_aclose_twice_with_different_coros(self):
# Regression test for https://bugs.python.org/issue39606
async def async_iterate():
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-15-13-53-59.gh-issue-117894.8LpZ6m.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-15-13-53-59.gh-issue-117894.8LpZ6m.rst
new file mode 100644
index 00000000000000..bd32500a54ee21
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-15-13-53-59.gh-issue-117894.8LpZ6m.rst
@@ -0,0 +1 @@
+Prevent ``agen.aclose()`` objects being re-used after ``.throw()``.
diff --git a/Objects/genobject.c b/Objects/genobject.c
index bc58409c181360..3fc2ac083d1c15 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -2223,7 +2223,11 @@ async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *const *args, Py_ssize_t na

retval = gen_throw((PyGenObject*)o->agt_gen, args, nargs);
if (o->agt_args) {
- return async_gen_unwrap_value(o->agt_gen, retval);
+ retval = async_gen_unwrap_value(o->agt_gen, retval);
+ if (retval == NULL) {
+ o->agt_state = AWAITABLE_STATE_CLOSED;
+ }
+ return retval;
} else {
/* aclose() mode */
if (retval && _PyAsyncGenWrappedValue_CheckExact(retval)) {
@@ -2233,6 +2237,9 @@ async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *const *args, Py_ssize_t na
PyErr_SetString(PyExc_RuntimeError, ASYNC_GEN_IGNORED_EXIT_MSG);
return NULL;
}
+ if (retval == NULL) {
+ o->agt_state = AWAITABLE_STATE_CLOSED;
+ }
if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration) ||
PyErr_ExceptionMatches(PyExc_GeneratorExit))
{

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-leave@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: list-python-checkins@lists.gossamer-threads.com