bpo-31708: Allow async generator expressions in synchronous functions… · python/cpython@b8ab9d3 (original) (raw)

File tree

5 files changed

lines changed

5 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -326,14 +326,16 @@ range(10) for y in bar(x))``.
326 326 The parentheses can be omitted on calls with only one argument. See section
327 327 :ref:`calls` for details.
328 328
329 -Since Python 3.6, if the generator appears in an :keyword:`async def` function,
330 -then :keyword:`async for` clauses and :keyword:`await` expressions are permitted
331 -as with an asynchronous comprehension. If a generator expression
332 -contains either :keyword:`async for` clauses or :keyword:`await` expressions
333 -it is called an :dfn:`asynchronous generator expression`.
334 -An asynchronous generator expression yields a new asynchronous
335 -generator object, which is an asynchronous iterator
336 -(see :ref:`async-iterators`).
329 +If a generator expression contains either :keyword:`async for`
330 +clauses or :keyword:`await` expressions it is called an
331 +:dfn:`asynchronous generator expression`. An asynchronous generator
332 +expression returns a new asynchronous generator object,
333 +which is an asynchronous iterator (see :ref:`async-iterators`).
334 +
335 +.. versionchanged:: 3.7
336 + Prior to Python 3.7, asynchronous generator expressions could
337 + only appear in :keyword:`async def` coroutines. Starting
338 + with 3.7, any function can use asynchronous generator expressions.
337 339
338 340 .. _yieldexpr:
339 341
Original file line number Diff line number Diff line change
@@ -1037,5 +1037,37 @@ async def wait():
1037 1037 t.cancel()
1038 1038 self.loop.run_until_complete(asyncio.sleep(0.1, loop=self.loop))
1039 1039
1040 +def test_async_gen_expression_01(self):
1041 +async def arange(n):
1042 +for i in range(n):
1043 +await asyncio.sleep(0.01, loop=self.loop)
1044 +yield i
1045 +
1046 +def make_arange(n):
1047 +# This syntax is legal starting with Python 3.7
1048 +return (i * 2 async for i in arange(n))
1049 +
1050 +async def run():
1051 +return [i async for i in make_arange(10)]
1052 +
1053 +res = self.loop.run_until_complete(run())
1054 +self.assertEqual(res, [i * 2 for i in range(10)])
1055 +
1056 +def test_async_gen_expression_02(self):
1057 +async def wrap(n):
1058 +await asyncio.sleep(0.01, loop=self.loop)
1059 +return n
1060 +
1061 +def make_arange(n):
1062 +# This syntax is legal starting with Python 3.7
1063 +return (i * 2 for i in range(n) if await wrap(i))
1064 +
1065 +async def run():
1066 +return [i async for i in make_arange(10)]
1067 +
1068 +res = self.loop.run_until_complete(run())
1069 +self.assertEqual(res, [i * 2 for i in range(1, 10)])
1070 +
1071 +
1040 1072 if __name__ == "__main__":
1041 1073 unittest.main()
Original file line number Diff line number Diff line change
@@ -149,6 +149,14 @@ def bar():
149 149 [i async for i in els]
150 150 """,
151 151
152 +"""def bar():
153 + {i: i async for i in els}
154 + """,
155 +
156 +"""def bar():
157 + {i async for i in els}
158 + """,
159 +
152 160 """def bar():
153 161 [await i for i in els]
154 162 """,
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1 +Allow use of asynchronous generator expressions in synchronous functions.
Original file line number Diff line number Diff line change
@@ -3974,7 +3974,7 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type,
3974 3974
3975 3975 is_async_generator = c->u->u_ste->ste_coroutine;
3976 3976
3977 -if (is_async_generator && !is_async_function) {
3977 +if (is_async_generator && !is_async_function && type != COMP_GENEXP) {
3978 3978 if (e->lineno > c->u->u_lineno) {
3979 3979 c->u->u_lineno = e->lineno;
3980 3980 c->u->u_lineno_set = 0;