bpo-31708: Allow async generator expressions in synchronous functions (#3905)
This commit is contained in:
parent
faa135acbf
commit
b8ab9d3fc8
|
@ -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:
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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]
|
||||
""",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Allow use of asynchronous generator expressions in synchronous functions.
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue