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

This commit is contained in:
Yury Selivanov 2017-10-06 02:58:28 -04:00 committed by GitHub
parent faa135acbf
commit b8ab9d3fc8
5 changed files with 52 additions and 9 deletions

View 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:

View 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()

View 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]
""",

View File

@ -0,0 +1 @@
Allow use of asynchronous generator expressions in synchronous functions.

View 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;