python / mypy

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

Async generators are detected as synchronous generators erroneously #17482

Open abrahammurciano opened 2 days ago

abrahammurciano commented 2 days ago

Bug Report

Asynchronous generator comprehensions such as (1 for f in futures if await f) are detected by mypy as regular synchronous generators.

I found a similar issue https://github.com/python/mypy/issues/12924 that has has been resolved by https://github.com/python/mypy/pull/12925, where expressions like (await f for f in futures) were being detected as regular synchronous generators instead of async ones. It looks like the fix didn't take into consideration that the await could be in the condition of the comprehension.

To Reproduce

https://mypy-play.net/?mypy=latest&python=3.12&gist=80e893739ab1a8f98fd786da84b7e209

import asyncio
from typing import Awaitable, AsyncGenerator

async def foo(*coros: Awaitable[None]) -> None:
    gen = (False for f in coros if not await f)
    # Revealed type is "typing.Generator[builtins.bool, None, None]"
    # Runtime type is 'async_generator'
    reveal_type(gen)
    return await anext(gen)

asyncio.run(foo(asyncio.sleep(1), asyncio.sleep(2)))

Expected Behavior

Mypy should reveal the type as typing.AsyncGenerator.

Actual Behavior

Mypy reveals the type as typing.Generator

Your Environment

abrahammurciano commented 2 days ago

@jhance looking at your pull request, you only check the left experssion for await:

if any(e.is_async) or has_await_expression(e.left_expr):

but I believe the right experssion would have to be checked as well, is that right?

if any(e.is_async) or has_await_expression(e.left_expr) or any(has_await_expression(cond) for condlist in condlists for cond in condlist):