This rule raises an issue when a for loop only yields each element of an iterable and could be replaced by a yield from statement.

Why is this an issue?

Introduced by PEP 380, the yield from statement delegates the iteration to another iterable.

def generator():
    yield from range(10)

Rewriting a for loop whose body is a single yield of the loop variable into yield from has several benefits:

How to fix it

Replace the for loop with a yield from statement over the same iterable.

Code examples

Noncompliant code example

def read_events(source):
    for event in parse_events(source):  # Noncompliant: this loop only relays the parsed events
        yield event

Compliant solution

def read_events(source):
    yield from parse_events(source)

Pitfalls

yield from is not allowed in asynchronous generators. An async for loop that yields each element cannot be rewritten this way, and this rule does not raise an issue on such loops.

async def relay(async_iterable):
    async for value in async_iterable:
        yield value

The two forms are equivalent as long as the sub-iterable is only iterated over. They diverge once the caller drives the outer generator directly:

The sub-generator’s return value also becomes the value of the yield from expression instead of being discarded. Check how the sub-iterable is consumed before rewriting a loop that delegates to a generator.

Resources

Documentation

Standards