[Python-checkins] bpo-39386: Prevent double awaiting of async iterator (GH-18081)

Miss Islington (bot) webhook-mailer at python.org
Mon Jan 20 18:07:59 EST 2020


https://github.com/python/cpython/commit/b76d5e9ee64b8ac861e856d8e41289c0977c75e1
commit: b76d5e9ee64b8ac861e856d8e41289c0977c75e1
branch: 3.7
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2020-01-20T15:07:54-08:00
summary:

bpo-39386: Prevent double awaiting of async iterator (GH-18081)

(cherry picked from commit a96e06db77dcbd3433d39761ddb4615d7d96284a)

Co-authored-by: Andrew Svetlov <andrew.svetlov at gmail.com>

files:
A Misc/NEWS.d/next/Core and Builtins/2020-01-20-21-40-57.bpo-39386.ULqD8t.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 cca6d58417ac4..426d5d1161423 100644
--- a/Lib/test/test_asyncgen.py
+++ b/Lib/test/test_asyncgen.py
@@ -1117,6 +1117,42 @@ def exception_handler(loop, context):
 
         self.assertEqual([], messages)
 
+    def test_async_gen_await_anext_twice(self):
+        async def async_iterate():
+            yield 1
+            yield 2
+
+        async def run():
+            it = async_iterate()
+            nxt = it.__anext__()
+            await nxt
+            with self.assertRaisesRegex(
+                    RuntimeError,
+                    r"cannot reuse already awaited __anext__\(\)/asend\(\)"
+            ):
+                await nxt
+
+            await it.aclose()  # prevent unfinished iterator warning
+
+        self.loop.run_until_complete(run())
+
+    def test_async_gen_await_aclose_twice(self):
+        async def async_iterate():
+            yield 1
+            yield 2
+
+        async def run():
+            it = async_iterate()
+            nxt = it.aclose()
+            await nxt
+            with self.assertRaisesRegex(
+                    RuntimeError,
+                    r"cannot reuse already awaited aclose\(\)/athrow\(\)"
+            ):
+                await nxt
+
+        self.loop.run_until_complete(run())
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-01-20-21-40-57.bpo-39386.ULqD8t.rst b/Misc/NEWS.d/next/Core and Builtins/2020-01-20-21-40-57.bpo-39386.ULqD8t.rst
new file mode 100644
index 0000000000000..f24e1f4e8a183
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2020-01-20-21-40-57.bpo-39386.ULqD8t.rst	
@@ -0,0 +1 @@
+Prevent double awaiting of async iterator.
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 7fc7a1f2aeb3d..a42169f82e4d7 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -1533,7 +1533,9 @@ async_gen_asend_send(PyAsyncGenASend *o, PyObject *arg)
     PyObject *result;
 
     if (o->ags_state == AWAITABLE_STATE_CLOSED) {
-        PyErr_SetNone(PyExc_StopIteration);
+        PyErr_SetString(
+            PyExc_RuntimeError,
+            "cannot reuse already awaited __anext__()/asend()");
         return NULL;
     }
 
@@ -1568,7 +1570,9 @@ async_gen_asend_throw(PyAsyncGenASend *o, PyObject *args)
     PyObject *result;
 
     if (o->ags_state == AWAITABLE_STATE_CLOSED) {
-        PyErr_SetNone(PyExc_StopIteration);
+        PyErr_SetString(
+            PyExc_RuntimeError,
+            "cannot reuse already awaited __anext__()/asend()");
         return NULL;
     }
 
@@ -1802,7 +1806,9 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
 
     if (f == NULL || f->f_stacktop == NULL ||
             o->agt_state == AWAITABLE_STATE_CLOSED) {
-        PyErr_SetNone(PyExc_StopIteration);
+        PyErr_SetString(
+            PyExc_RuntimeError,
+            "cannot reuse already awaited aclose()/athrow()");
         return NULL;
     }
 
@@ -1906,7 +1912,9 @@ async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *args)
     PyObject *retval;
 
     if (o->agt_state == AWAITABLE_STATE_CLOSED) {
-        PyErr_SetNone(PyExc_StopIteration);
+        PyErr_SetString(
+            PyExc_RuntimeError,
+            "cannot reuse already awaited aclose()/athrow()");
         return NULL;
     }
 



More information about the Python-checkins mailing list