mirror of
				https://github.com/astral-sh/ruff.git
				synced 2025-10-31 12:05:57 +00:00 
			
		
		
		
	 eb02aa5676
			
		
	
	
		eb02aa5676
		
			
		
	
	
	
	
		
			
			## Summary Add support for `async for` loops and async iterables. part of https://github.com/astral-sh/ty/issues/151 ## Ecosystem impact ```diff - boostedblob/listing.py:445:54: warning[unused-ignore-comment] Unused blanket `type: ignore` directive ``` This is correct. We now find a true positive in the `# type: ignore`'d code. All of the other ecosystem hits are of the type ```diff trio (https://github.com/python-trio/trio) + src/trio/_core/_tests/test_guest_mode.py:532:24: error[not-iterable] Object of type `MemorySendChannel[int] | MemoryReceiveChannel[int]` may not be iterable ``` The message is correct, because only `MemoryReceiveChannel` has an `__aiter__` method, but `MemorySendChannel` does not. What's not correct is our inferred type here. It should be `MemoryReceiveChannel[int]`, not the union of the two. This is due to missing unpacking support for tuple subclasses, which @AlexWaygood is working on. I don't think this should block merging this PR, because those wrong types are already there, without this PR. ## Test Plan New Markdown tests and snapshot tests for diagnostics.
		
			
				
	
	
	
	
		
			3.1 KiB
		
	
	
	
	
	
	
	
			
		
		
	
	
			3.1 KiB
		
	
	
	
	
	
	
	
Comprehensions
Basic comprehensions
class IntIterator:
    def __next__(self) -> int:
        return 42
class IntIterable:
    def __iter__(self) -> IntIterator:
        return IntIterator()
# revealed: int
[reveal_type(x) for x in IntIterable()]
class IteratorOfIterables:
    def __next__(self) -> IntIterable:
        return IntIterable()
class IterableOfIterables:
    def __iter__(self) -> IteratorOfIterables:
        return IteratorOfIterables()
# revealed: tuple[int, IntIterable]
[reveal_type((x, y)) for y in IterableOfIterables() for x in y]
# revealed: int
{reveal_type(x): 0 for x in IntIterable()}
# revealed: int
{0: reveal_type(x) for x in IntIterable()}
Nested comprehension
class IntIterator:
    def __next__(self) -> int:
        return 42
class IntIterable:
    def __iter__(self) -> IntIterator:
        return IntIterator()
# revealed: tuple[int, int]
[[reveal_type((x, y)) for x in IntIterable()] for y in IntIterable()]
Comprehension referencing outer comprehension
class IntIterator:
    def __next__(self) -> int:
        return 42
class IntIterable:
    def __iter__(self) -> IntIterator:
        return IntIterator()
class IteratorOfIterables:
    def __next__(self) -> IntIterable:
        return IntIterable()
class IterableOfIterables:
    def __iter__(self) -> IteratorOfIterables:
        return IteratorOfIterables()
# revealed: tuple[int, IntIterable]
[[reveal_type((x, y)) for x in y] for y in IterableOfIterables()]
Comprehension with unbound iterable
Iterating over an unbound iterable yields Unknown:
# error: [unresolved-reference] "Name `x` used when not defined"
# revealed: Unknown
[reveal_type(z) for z in x]
class IntIterator:
    def __next__(self) -> int:
        return 42
class IntIterable:
    def __iter__(self) -> IntIterator:
        return IntIterator()
# error: [not-iterable] "Object of type `int` is not iterable"
# revealed: tuple[int, Unknown]
[reveal_type((x, z)) for x in IntIterable() for z in x]
Starred expressions
Starred expressions must be iterable
class NotIterable: ...
class Iterator:
    def __next__(self) -> int:
        return 42
class Iterable:
    def __iter__(self) -> Iterator:
        return Iterator()
# This is fine:
x = [*Iterable()]
# error: [not-iterable] "Object of type `NotIterable` is not iterable"
y = [*NotIterable()]
Async comprehensions
Basic
class AsyncIterator:
    async def __anext__(self) -> int:
        return 42
class AsyncIterable:
    def __aiter__(self) -> AsyncIterator:
        return AsyncIterator()
async def _():
    # revealed: int
    [reveal_type(x) async for x in AsyncIterable()]
Invalid async comprehension
This tests that we understand that async comprehensions do not work according to the synchronous
iteration protocol
class Iterator:
    def __next__(self) -> int:
        return 42
class Iterable:
    def __iter__(self) -> Iterator:
        return Iterator()
async def _():
    # error: [not-iterable] "Object of type `Iterable` is not async-iterable"
    # revealed: Unknown
    [reveal_type(x) async for x in Iterable()]