For-in loops, yield from and iterable unpacking only work with iterable objects. In order to be iterable, an object should have either an __iter__ method or a __getitem__ method implementing the Sequence semantic.

Note also that iterating over an asynchronous iterable, i.e. an object having the __aiter__ method, requires the use of async for ... in instead of for ... in.

This rule raises an issue when a non iterable object is used in a for-in loop, in a yield from or when it is unpacked.

Noncompliant Code Example

class Empty:
    pass

empty = Empty()

for a in empty:  # Noncompliant
    print(a)

a, b, c = empty  # Noncompliant

print(*empty)  # Noncompliant

[1, 2, 3, *empty]  # Noncompliant

# yield from
def generator():
    yield from Empty()  # Noncompliant

# async generators
async def async_generator():
    yield 1

a, *rest = async_generator()  # Noncompliant
for a in async_generator():  # Noncompliant; "async" is missing before "for"
    print(a)

Compliant Solution

class MyIterable:
    def __init__(self, values):
        self._values = values

    def __iter__(self):
        return iter(self._values)

my_iterable = MyIterable(range(10))

for a in my_iterable:
    print(a)

a, b, *c = my_iterable

print(*my_iterable)

[1, 2, 3, *my_iterable]

# yield from
def generator():
    yield from subgenerator()

def subgenerator():
    yield 1

# async generators
async def async_generator():
    yield 1

async for a in async_generator():
    print(a)

See