summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_asyncgen.py30
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2020-02-11-23-59-07.bpo-39606.a72Sxc.rst2
-rw-r--r--Objects/genobject.c15
3 files changed, 41 insertions, 6 deletions
diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py
index 24b20be..fb6321d 100644
--- a/Lib/test/test_asyncgen.py
+++ b/Lib/test/test_asyncgen.py
@@ -1128,7 +1128,7 @@ class AsyncGenAsyncioTest(unittest.TestCase):
self.assertEqual([], messages)
- def test_async_gen_await_anext_twice(self):
+ def test_async_gen_await_same_anext_coro_twice(self):
async def async_iterate():
yield 1
yield 2
@@ -1147,7 +1147,7 @@ class AsyncGenAsyncioTest(unittest.TestCase):
self.loop.run_until_complete(run())
- def test_async_gen_await_aclose_twice(self):
+ def test_async_gen_await_same_aclose_coro_twice(self):
async def async_iterate():
yield 1
yield 2
@@ -1164,6 +1164,32 @@ class AsyncGenAsyncioTest(unittest.TestCase):
self.loop.run_until_complete(run())
+ def test_async_gen_aclose_twice_with_different_coros(self):
+ # Regression test for https://bugs.python.org/issue39606
+ async def async_iterate():
+ yield 1
+ yield 2
+
+ async def run():
+ it = async_iterate()
+ await it.aclose()
+ await it.aclose()
+
+ self.loop.run_until_complete(run())
+
+ def test_async_gen_aclose_after_exhaustion(self):
+ # Regression test for https://bugs.python.org/issue39606
+ async def async_iterate():
+ yield 1
+ yield 2
+
+ async def run():
+ it = async_iterate()
+ async for _ in it:
+ pass
+ await it.aclose()
+
+ self.loop.run_until_complete(run())
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-02-11-23-59-07.bpo-39606.a72Sxc.rst b/Misc/NEWS.d/next/Core and Builtins/2020-02-11-23-59-07.bpo-39606.a72Sxc.rst
new file mode 100644
index 0000000..b7cbe4e
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2020-02-11-23-59-07.bpo-39606.a72Sxc.rst
@@ -0,0 +1,2 @@
+Fix regression caused by fix for bpo-39386, that prevented calling
+``aclose`` on an async generator that had already been closed or exhausted.
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 72aa872..2c06bdc 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -1812,16 +1812,22 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
PyFrameObject *f = gen->gi_frame;
PyObject *retval;
- if (f == NULL || f->f_stacktop == NULL ||
- o->agt_state == AWAITABLE_STATE_CLOSED) {
+ if (o->agt_state == AWAITABLE_STATE_CLOSED) {
PyErr_SetString(
PyExc_RuntimeError,
"cannot reuse already awaited aclose()/athrow()");
return NULL;
}
+ if (f == NULL || f->f_stacktop == NULL) {
+ o->agt_state = AWAITABLE_STATE_CLOSED;
+ PyErr_SetNone(PyExc_StopIteration);
+ return NULL;
+ }
+
if (o->agt_state == AWAITABLE_STATE_INIT) {
if (o->agt_gen->ag_running_async) {
+ o->agt_state = AWAITABLE_STATE_CLOSED;
if (o->agt_args == NULL) {
PyErr_SetString(
PyExc_RuntimeError,
@@ -1893,7 +1899,6 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
/* aclose() mode */
if (retval) {
if (_PyAsyncGenWrappedValue_CheckExact(retval)) {
- o->agt_gen->ag_running_async = 0;
Py_DECREF(retval);
goto yield_close;
}
@@ -1908,16 +1913,17 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
yield_close:
o->agt_gen->ag_running_async = 0;
+ o->agt_state = AWAITABLE_STATE_CLOSED;
PyErr_SetString(
PyExc_RuntimeError, ASYNC_GEN_IGNORED_EXIT_MSG);
return NULL;
check_error:
o->agt_gen->ag_running_async = 0;
+ o->agt_state = AWAITABLE_STATE_CLOSED;
if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration) ||
PyErr_ExceptionMatches(PyExc_GeneratorExit))
{
- o->agt_state = AWAITABLE_STATE_CLOSED;
if (o->agt_args == NULL) {
/* when aclose() is called we don't want to propagate
StopAsyncIteration or GeneratorExit; just raise
@@ -1951,6 +1957,7 @@ async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *args)
/* aclose() mode */
if (retval && _PyAsyncGenWrappedValue_CheckExact(retval)) {
o->agt_gen->ag_running_async = 0;
+ o->agt_state = AWAITABLE_STATE_CLOSED;
Py_DECREF(retval);
PyErr_SetString(PyExc_RuntimeError, ASYNC_GEN_IGNORED_EXIT_MSG);
return NULL;