python-trio / flake8-async

Highly opinionated linter for Trio code
https://flake8-async.readthedocs.io
MIT License
17 stars 2 forks source link

TRIO91X: Support comprehensions #144

Closed jakkdl closed 1 year ago

jakkdl commented 1 year ago

Comprehensions are currently completely invisible to 91X, so these are currently misbehaving:

async def foo_comprehension_1():
    [... for x in range(10) if await foo()]

# should error
async def foo_comprehension_2():
    [await foo() for x in range(10) if bar()]

# should not error
async def foo_comprehension_3():  # error: 0, "exit", Statement("function definition", lineno)
    [... async for x in bar()]

One cannot yield or return inside a comprehension though, so will require separate logic from loops - but it should be much simpler. I'm pretty sure an async generator or for cannot be guaranteed to iterate at least once either, even with liberal use of starred expressions and the like.

https://peps.python.org/pep-0530/ https://libcst.readthedocs.io/en/latest/nodes.html#comprehensions

Zac-HD commented 1 year ago

I'm pretty sure an async generator or for cannot be guaranteed to iterate at least once either, even with liberal use of starred expressions and the like.

Because it might be cancelled in the first checkpoint, right? So while not iterating, it does at least have a checkpoint...

jakkdl commented 1 year ago

Ah, I meant in the sense of "does it make sense to call iter_guaranteed_once", but I didn't come up with any async expression that can be statically determined to always iter (unless you start listing library functions). But I now realize that it doesn't matter anyway since async for checkpoints on entry/exit anyway, and with no yields in the body, it will never matter whether it loops or not. So async for comprehensions can just be treated as a checkpoint, no matter the content. (non-async comprehensions with awaits in them still need some logic though)