Commit b8ab9d3f authored by Yury Selivanov's avatar Yury Selivanov Committed by GitHub

bpo-31708: Allow async generator expressions in synchronous functions (#3905)

parent faa135ac
...@@ -326,14 +326,16 @@ range(10) for y in bar(x))``. ...@@ -326,14 +326,16 @@ range(10) for y in bar(x))``.
The parentheses can be omitted on calls with only one argument. See section The parentheses can be omitted on calls with only one argument. See section
:ref:`calls` for details. :ref:`calls` for details.
Since Python 3.6, if the generator appears in an :keyword:`async def` function, If a generator expression contains either :keyword:`async for`
then :keyword:`async for` clauses and :keyword:`await` expressions are permitted clauses or :keyword:`await` expressions it is called an
as with an asynchronous comprehension. If a generator expression :dfn:`asynchronous generator expression`. An asynchronous generator
contains either :keyword:`async for` clauses or :keyword:`await` expressions expression returns a new asynchronous generator object,
it is called an :dfn:`asynchronous generator expression`. which is an asynchronous iterator (see :ref:`async-iterators`).
An asynchronous generator expression yields a new asynchronous
generator object, which is an asynchronous iterator .. versionchanged:: 3.7
(see :ref:`async-iterators`). 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: .. _yieldexpr:
......
...@@ -1037,5 +1037,37 @@ class AsyncGenAsyncioTest(unittest.TestCase): ...@@ -1037,5 +1037,37 @@ class AsyncGenAsyncioTest(unittest.TestCase):
t.cancel() t.cancel()
self.loop.run_until_complete(asyncio.sleep(0.1, loop=self.loop)) 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__": if __name__ == "__main__":
unittest.main() unittest.main()
...@@ -149,6 +149,14 @@ class AsyncBadSyntaxTest(unittest.TestCase): ...@@ -149,6 +149,14 @@ class AsyncBadSyntaxTest(unittest.TestCase):
[i async for i in els] [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(): """def bar():
[await i for i in els] [await i for i in els]
""", """,
......
Allow use of asynchronous generator expressions in synchronous functions.
...@@ -3974,7 +3974,7 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type, ...@@ -3974,7 +3974,7 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type,
is_async_generator = c->u->u_ste->ste_coroutine; 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) { if (e->lineno > c->u->u_lineno) {
c->u->u_lineno = e->lineno; c->u->u_lineno = e->lineno;
c->u->u_lineno_set = 0; c->u->u_lineno_set = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment