This rule raises an issue when StopIteration is explicitly raised within a generator function, that is, a function containing yield.

Why is this an issue?

In Python, generators are special functions that use the yield keyword to produce a sequence of values lazily. They implement the iterator protocol and are used to create iterators in a concise way.

Traditionally, raising StopIteration was the standard way to signal that an iterator had no more values to produce. However, this could lead to subtle bugs when StopIteration exceptions from nested iterators or function calls accidentally propagated up and terminated the generator unexpectedly.

To address this issue, PEP 479 changed the behavior in Python 3.7+. Now, when StopIteration is raised inside a generator function, Python automatically converts it to a RuntimeError. This conversion happens at the point where the exception would leave the generator.

This means code that explicitly raises StopIteration in a generator will fail with a RuntimeError at runtime in Python 3.7 and later versions. The error message will indicate that the StopIteration exception was raised inside a generator.

The proper way to terminate a generator is to use a return statement, optionally with a value. When a generator executes a return, Python automatically raises StopIteration behind the scenes, but does so in a way that is compatible with PEP 479.

What is the potential impact?

When StopIteration is raised inside a generator in Python 3.7+, it causes a RuntimeError at runtime. This can lead to:

How to fix it

Replace the raise StopIteration statement with a return statement. In generators, return is the proper way to signal completion. If you need to return a value when the generator completes, you can use return value, though note that this value is not yielded but becomes the exception value of the StopIteration that is automatically raised.

Code examples

Noncompliant code example

def my_generator():
    yield 1
    yield 2
    raise StopIteration  # Noncompliant

Compliant solution

def my_generator():
    yield 1
    yield 2
    return  # Properly terminates the generator

Resources

Documentation