From b8ab9d3fc816f85f4d6dbef12b7414e6dc10e4dd Mon Sep 17 00:00:00 2001
From: Yury Selivanov <yury@magic.io>
Date: Fri, 6 Oct 2017 02:58:28 -0400
Subject: bpo-31708: Allow async generator expressions in synchronous functions
 (#3905)

---
 Doc/reference/expressions.rst                      | 18 ++++++------
 Lib/test/test_asyncgen.py                          | 32 ++++++++++++++++++++++
 Lib/test/test_coroutines.py                        |  8 ++++++
 .../2017-10-06-02-10-48.bpo-31708.66CCVU.rst       |  1 +
 Python/compile.c                                   |  2 +-
 5 files changed, 52 insertions(+), 9 deletions(-)
 create mode 100644 Misc/NEWS.d/next/Core and Builtins/2017-10-06-02-10-48.bpo-31708.66CCVU.rst

diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst
index 094b928..1cff8a5 100644
--- a/Doc/reference/expressions.rst
+++ b/Doc/reference/expressions.rst
@@ -326,14 +326,16 @@ range(10) for y in bar(x))``.
 The parentheses can be omitted on calls with only one argument.  See section
 :ref:`calls` for details.
 
-Since Python 3.6, if the generator appears in an :keyword:`async def` function,
-then :keyword:`async for` clauses and :keyword:`await` expressions are permitted
-as with an asynchronous comprehension.  If a generator expression
-contains either :keyword:`async for` clauses or :keyword:`await` expressions
-it is called an :dfn:`asynchronous generator expression`.
-An asynchronous generator expression yields a new asynchronous
-generator object, which is an asynchronous iterator
-(see :ref:`async-iterators`).
+If a generator expression contains either :keyword:`async for`
+clauses or :keyword:`await` expressions it is called an
+:dfn:`asynchronous generator expression`.  An asynchronous generator
+expression returns a new asynchronous generator object,
+which is an asynchronous iterator (see :ref:`async-iterators`).
+
+.. versionchanged:: 3.7
+   Prior to Python 3.7, asynchronous generator expressions could
+   only appear in :keyword:`async def` coroutines.  Starting
+   with 3.7, any function can use asynchronous generator expressions.
 
 .. _yieldexpr:
 
diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py
index 8c69d2b..5a36423 100644
--- a/Lib/test/test_asyncgen.py
+++ b/Lib/test/test_asyncgen.py
@@ -1037,5 +1037,37 @@ class AsyncGenAsyncioTest(unittest.TestCase):
         t.cancel()
         self.loop.run_until_complete(asyncio.sleep(0.1, loop=self.loop))
 
+    def test_async_gen_expression_01(self):
+        async def arange(n):
+            for i in range(n):
+                await asyncio.sleep(0.01, loop=self.loop)
+                yield i
+
+        def make_arange(n):
+            # This syntax is legal starting with Python 3.7
+            return (i * 2 async for i in arange(n))
+
+        async def run():
+            return [i async for i in make_arange(10)]
+
+        res = self.loop.run_until_complete(run())
+        self.assertEqual(res, [i * 2 for i in range(10)])
+
+    def test_async_gen_expression_02(self):
+        async def wrap(n):
+            await asyncio.sleep(0.01, loop=self.loop)
+            return n
+
+        def make_arange(n):
+            # This syntax is legal starting with Python 3.7
+            return (i * 2 for i in range(n) if await wrap(i))
+
+        async def run():
+            return [i async for i in make_arange(10)]
+
+        res = self.loop.run_until_complete(run())
+        self.assertEqual(res, [i * 2 for i in range(1, 10)])
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index 0803517..d7d38a3 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -150,6 +150,14 @@ class AsyncBadSyntaxTest(unittest.TestCase):
             """,
 
             """def bar():
+                 {i: i async for i in els}
+            """,
+
+            """def bar():
+                 {i async for i in els}
+            """,
+
+            """def bar():
                  [await i for i in els]
             """,
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-10-06-02-10-48.bpo-31708.66CCVU.rst b/Misc/NEWS.d/next/Core and Builtins/2017-10-06-02-10-48.bpo-31708.66CCVU.rst
new file mode 100644
index 0000000..f732fee
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2017-10-06-02-10-48.bpo-31708.66CCVU.rst	
@@ -0,0 +1 @@
+Allow use of asynchronous generator expressions in synchronous functions.
diff --git a/Python/compile.c b/Python/compile.c
index 431e753..58a708c 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -3974,7 +3974,7 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type,
 
     is_async_generator = c->u->u_ste->ste_coroutine;
 
-    if (is_async_generator && !is_async_function) {
+    if (is_async_generator && !is_async_function && type != COMP_GENEXP) {
         if (e->lineno > c->u->u_lineno) {
             c->u->u_lineno = e->lineno;
             c->u->u_lineno_set = 0;
-- 
cgit v0.12

