python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.51k stars 2.83k forks source link

await-not-async missed & false alarms with genexp and list comprehensions #18124

Open jakkdl opened 1 week ago

jakkdl commented 1 week ago

Bug Report

await-not-async does not warn on async for in list and dict comprehensions, and incorrectly does give a warning for await inside GeneratorExp.elt. For list & dict comprehensions you should error on any await or async for, but for generator expressions it's only the generator that should be checked.

To Reproduce

def foo():
    ...

def should_not_error():
    (await x for x in foo())

def should_error():
    [x async for x in foo()]
    {k: v async for k, v in foo()}

def correctly_errors():
    (x for x in await foo())
    [await x for x in foo()]

def correctly_does_not_error():
    (x async for x in foo())

Expected Behavior

It should error on 8, 9, 12 and 13

Actual Behavior

$ mypy foofoo.py
foofoo.py:5: error: "await" outside coroutine ("async def")  [await-not-async]
foofoo.py:12: error: "await" outside coroutine ("async def")  [await-not-async]
foofoo.py:13: error: "await" outside coroutine ("async def")  [await-not-async]
Found 3 errors in 1 file (checked 1 source file)

Your Environment

$ mypy --version
mypy 1.13.0 (compiled: yes)

also see https://github.com/astral-sh/ruff/issues/14167 that had the exact same issues

josetapadas commented 5 days ago

I can try to pick this one up if it helps :)

brianschubert commented 5 days ago

@josetapadas Please, feel free! You can check out CONTRIBUTING.md and the Developer Guides for contributing guidelines and tips on where to start. Thanks!

josetapadas commented 4 days ago

hi there @brianschubert , I've opened https://github.com/python/mypy/pull/18152 to try and address this issue.

(So this is my first attempt to contribute to this great project so any guidance on a mistakeful approach would be very appreciated! :see_no_evil:)

I've attempted to as well use a file like the one in the example to verify the fix:

╰─❯ cat -n foo.py
     1  def foo():
     2      yield 0
     3
     4
     5  def lol():
     6      (await x for x in foo())
     7      [x async for x in foo()]
     8      {k: v async for k, v in foo()}
     9      (x for x in await foo())
    10      [await x for x in foo()]
    11      (x async for x in foo())

to verify if the errors were in the proper place:

╰─❯ rm -rf .mypy_cache && mypy foo.py --show-traceback
foo.py:7: error: "async for" outside async function  [syntax]
foo.py:8: error: "async for" outside async function  [syntax]
foo.py:9: error: "await" outside coroutine ("async def")  [await-not-async]
foo.py:10: error: "await" outside coroutine ("async def")  [await-not-async]
Found 4 errors in 1 file (checked 1 source file)