]> granicus.if.org Git - python/commitdiff
bpo-31708: Allow async generator expressions in synchronous functions (#3905)
authorYury Selivanov <yury@magic.io>
Fri, 6 Oct 2017 06:58:28 +0000 (02:58 -0400)
committerGitHub <noreply@github.com>
Fri, 6 Oct 2017 06:58:28 +0000 (02:58 -0400)
Doc/reference/expressions.rst
Lib/test/test_asyncgen.py
Lib/test/test_coroutines.py
Misc/NEWS.d/next/Core and Builtins/2017-10-06-02-10-48.bpo-31708.66CCVU.rst [new file with mode: 0644]
Python/compile.c

index 094b92841e219f640ecaa6d0e7393c88c27331ce..1cff8a52df959d555d8174e1c00e1a2198d2bcc7 100644 (file)
@@ -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:
 
index 8c69d2bf45d0fb4e3825e4a35514b9516295676f..5a36423dc97afa3e8e22ea50f390243d14d823b6 100644 (file)
@@ -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()
index 08035173d9987625ac0c85d4e7f48c3e1426a117..d7d38a3bf9dd8b11b89c31b36fa88976b38dce6c 100644 (file)
@@ -149,6 +149,14 @@ class AsyncBadSyntaxTest(unittest.TestCase):
                  [i async for i in els]
             """,
 
+            """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 (file)
index 0000000..f732fee
--- /dev/null
@@ -0,0 +1 @@
+Allow use of asynchronous generator expressions in synchronous functions.
index 431e7531e84eeccdc289c628352af8a5a931b4a4..58a708ce23eb02f2b9f795abc65b890ea3d51dc2 100644 (file)
@@ -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;