[Python-checkins] bpo-33786: Fix asynchronous generators to handle GeneratorExit in athrow() (GH-7467) (GH-7507)

Yury Selivanov webhook-mailer at python.org
Thu Jun 7 21:32:50 EDT 2018


https://github.com/python/cpython/commit/8de73d5a6914cfe55c23b0ad829cd2ba8954bc2e
commit: 8de73d5a6914cfe55c23b0ad829cd2ba8954bc2e
branch: 3.6
author: Yury Selivanov <yury at magic.io>
committer: GitHub <noreply at github.com>
date: 2018-06-07T21:32:43-04:00
summary:

bpo-33786: Fix asynchronous generators to handle GeneratorExit in athrow() (GH-7467) (GH-7507)

files:
A Misc/NEWS.d/next/Core and Builtins/2018-06-06-23-24-40.bpo-33786.lBvT8z.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 34ab8a04eee2..45cc2754a98a 100644
--- a/Lib/test/test_asyncgen.py
+++ b/Lib/test/test_asyncgen.py
@@ -111,6 +111,31 @@ def sync_iterate(g):
                     res.append(str(type(ex)))
             return res
 
+        def async_iterate(g):
+            res = []
+            while True:
+                an = g.__anext__()
+                try:
+                    while True:
+                        try:
+                            an.__next__()
+                        except StopIteration as ex:
+                            if ex.args:
+                                res.append(ex.args[0])
+                                break
+                            else:
+                                res.append('EMPTY StopIteration')
+                                break
+                        except StopAsyncIteration:
+                            raise
+                        except Exception as ex:
+                            res.append(str(type(ex)))
+                            break
+                except StopAsyncIteration:
+                    res.append('STOP')
+                    break
+            return res
+
         def async_iterate(g):
             res = []
             while True:
@@ -300,6 +325,37 @@ def test_async_gen_exception_10(self):
                                     "non-None value .* async generator"):
             gen().__anext__().send(100)
 
+    def test_async_gen_exception_11(self):
+        def sync_gen():
+            yield 10
+            yield 20
+
+        def sync_gen_wrapper():
+            yield 1
+            sg = sync_gen()
+            sg.send(None)
+            try:
+                sg.throw(GeneratorExit())
+            except GeneratorExit:
+                yield 2
+            yield 3
+
+        async def async_gen():
+            yield 10
+            yield 20
+
+        async def async_gen_wrapper():
+            yield 1
+            asg = async_gen()
+            await asg.asend(None)
+            try:
+                await asg.athrow(GeneratorExit())
+            except GeneratorExit:
+                yield 2
+            yield 3
+
+        self.compare_generators(sync_gen_wrapper(), async_gen_wrapper())
+
     def test_async_gen_api_01(self):
         async def gen():
             yield 123
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-06-06-23-24-40.bpo-33786.lBvT8z.rst b/Misc/NEWS.d/next/Core and Builtins/2018-06-06-23-24-40.bpo-33786.lBvT8z.rst
new file mode 100644
index 000000000000..57deefe339b5
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-06-06-23-24-40.bpo-33786.lBvT8z.rst	
@@ -0,0 +1 @@
+Fix asynchronous generators to handle GeneratorExit in athrow() correctly
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 1c29e296afe2..f226dbebaf4a 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -1939,21 +1939,20 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
     return NULL;
 
 check_error:
-    if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) {
+    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; just raise StopIteration, signalling
-               that 'aclose()' is done. */
+               StopAsyncIteration or GeneratorExit; just raise
+               StopIteration, signalling that this 'aclose()' await
+               is done.
+            */
             PyErr_Clear();
             PyErr_SetNone(PyExc_StopIteration);
         }
     }
-    else if (PyErr_ExceptionMatches(PyExc_GeneratorExit)) {
-        o->agt_state = AWAITABLE_STATE_CLOSED;
-        PyErr_Clear();          /* ignore these errors */
-        PyErr_SetNone(PyExc_StopIteration);
-    }
     return NULL;
 }
 



More information about the Python-checkins mailing list