From b8ab9d3fc816f85f4d6dbef12b7414e6dc10e4dd Mon Sep 17 00:00:00 2001 From: Yury Selivanov 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