Reputation: 111
I'm using Python 3.7.4 and this block of code (MWE):
import asyncio
async def foo(x):
await asyncio.sleep(1)
return [i for i in range(10)]
async def big_foo():
dict_of_lists = {'A': [i for i in range(10)], 'B': [i for i in range(10)]}
return {
key: [
item
for list_item in dict_of_lists[key]
for item in await foo(list_item)
]
for key in ['A', 'B']
}
Throws the error:
File "filename.py", line 13
item
^
SyntaxError: asynchronous comprehension outside of an asynchronous function
The error message doesn't help a lot because it is indeed inside an asynchronous function.
I solved the issue by defining explicit for
loops instead of a comprehension, like this:
async def big_foo():
dict_of_lists = {'A': [i for i in range(10)], 'B': [i for i in range(10)]}
var = {}
for key in ['A', 'B']:
var[key] = []
for list_item in dict_of_lists[key]:
for item in await foo(list_item):
var[key].append(item)
return var
It bugs me that removing the dictionary outer loop also removes the error.
This works (it doesn't accomplish what I need obviously):
async def other_foo():
dict_of_lists = {'A': [i for i in range(10)]}
return [
item
for list_item in dict_of_lists['A']
for item in await foo(list_item)
]
Upvotes: 4
Views: 2993
Reputation: 93
This bug should have been fixed in Python 3.11.0
Asynchronous comprehensions are now allowed inside comprehensions in asynchronous functions. Outer comprehensions implicitly become asynchronous in this case. (Contributed by Serhiy Storchaka in bpo-33346.)
Upvotes: 1
Reputation: 111
I have found the answer. Quoting a co-worker:
The interior comprehension is asynchronous. The exterior one isn't. The function is asynchronous. The error is triggered because you are defining as an asynchronous comprehension inside of a non-asynchronous context - the error message is indeed wrong and it's a know bug.
https://bugs.python.org/issue33346
I fixed it by setting an
async for
in the exterior comprehension. Like this:
async def big_foo():
dict_of_lists = {'A': [i for i in range(10)], 'B': [i for i in range(10)]}
return {
key: [
item
for list_item in dict_of_lists[key]
for item in await foo(list_item)
]
async for key in ['A', 'B']
}
While this removes the mentioned SyntaxError
it introduces a TypeError: 'async for' requires an object with __aiter__ method, got tuple
. So the solution would be to define a wraper for it, but it looks more like a hack than a proper solution.
Upvotes: 5