-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: refactor sem analysis of the await-not-async on generators and list comprehension #18152
base: master
Are you sure you want to change the base?
Changes from all commits
d3b2d9d
ed2937a
c97bce7
ada4720
43fc65a
e3004a1
e7ef1f6
0c82fb9
8e8298e
0fee8d6
95c5730
48148ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1013,13 +1013,15 @@ async def foo(x: int) -> int: ... | |
|
||
# These are allowed in some cases: | ||
top_level = await foo(1) # E: "await" outside function [top-level-await] | ||
crasher = [await foo(x) for x in [1, 2, 3]] # E: "await" outside function [top-level-await] | ||
crasher = [await foo(x) for x in [1, 2, 3]] # E: "await" outside coroutine ("async def") [await-not-async] \ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More explicit tests would be really helpful here. Consider at least checking all of the following in different scopes (module level, non-async function body and async function body at least; the former two should produce the same errors and async function should allow all of those): (await x for x in xs) # OK
[await x for x in xs] # E
{await x for x in xs} # E
{0: await x for x in xs} # E
{await x: 0 for x in xs} # E
(x async for x in xs) # OK
[x async for x in xs] # E
{x async for x in xs} # E
{x: 0 async for x in xs} # E
(x for x in await xs) # E
[x for x in await xs] # E
{x for x in await xs} # E
{x: 0 for x in await xs} # E There's already |
||
# E: "await" outside function [top-level-await] | ||
|
||
def bad() -> None: | ||
# These are always critical / syntax issues: | ||
y = [await foo(x) for x in [1, 2, 3]] # E: "await" outside coroutine ("async def") [await-not-async] | ||
async def good() -> None: | ||
y = [await foo(x) for x in [1, 2, 3]] # OK | ||
|
||
josetapadas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
[builtins fixtures/async_await.pyi] | ||
[typing fixtures/typing-async.pyi] | ||
|
||
|
@@ -1080,3 +1082,24 @@ class Launcher(P): | |
|
||
[builtins fixtures/async_await.pyi] | ||
[typing fixtures/typing-async.pyi] | ||
|
||
[case testAwaitOutsideCoroutine] | ||
def foo(): | ||
yield 0 | ||
|
||
def bar(): | ||
(await x for x in foo()) | ||
|
||
(await x for x in xs) # OK | ||
[await x for x in xs] # E: "await" outside coroutine ("async def") | ||
{await x for x in xs} # E: "await" outside coroutine ("async def") | ||
{0: await x for x in xs} # E: "await" outside coroutine ("async def") | ||
{await x: 0 for x in xs} # E: "await" outside coroutine ("async def") | ||
|
||
(x for x in await xs) # E: "await" outside coroutine ("async def") | ||
[x for x in await xs] # E: "await" outside coroutine ("async def") | ||
{x for x in await xs} # E: "await" outside coroutine ("async def") | ||
{x: 0 for x in await xs} # E: "await" outside coroutine ("async def") | ||
|
||
[builtins fixtures/async_await.pyi] | ||
[typing fixtures/typing-async.pyi] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really like the use of
codes.SYNTAX
vscodes.AWAIT_NOT_ASYNC
here. Prior to this PRAWAIT_NOT_ASYNC
was only used once.As far as I understand, the purpose of using a special code was to support "specific" environments: e.g. IPython allows top-level
await
s, so it should be easy to disable such a check when checking IPython snippets.I see two possible resolutions here:
codes.AWAIT_NOT_ASYNC
can be safely removed altogether and replaced withcodes.SYNTAX
not self.is_func_scope()
), producecodes.AWAIT_NOT_ASYNC
, otherwise producecodes.SYNTAX
. This can also be extracted into a helper likedef require_async_scope(self, message)
. This logic is close to whatvisit_await_expr
was doing before.I somewhat prefer 2 as that isn't that much work to do, but can live with 1 either.
Disclaimer: please note I'm not a
mypy
maintainer and just try to help sometimes. You'd better ask for a maintainer's opinion here: @brianschubert do you have any preference?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OTOH 2 can also be misleading and deserves an explicit mention in the docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the review and explanation, I understand your point so thank you for clarifying it. Will then wait for any guidance on how to proceed on any of these two options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is an important enough problem to change our stance on allowing top-level awaits, so you're probably safer to go ahead with option 2 for now. A bare
await
outside async has always been a syntax error in python, so whatever reason the original PR had for not making that usecodes.SYNTAX
should apply here as well.