stop-iteration-return / R1708ΒΆ
Message emitted:
Do not raise StopIteration in generator, use return statement instead
Description:
According to PEP479, the raise of StopIteration to end the loop of a generator may lead to hard to find bugs. This PEP specify that raise StopIteration has to be replaced by a simple return statement
Problematic code:
fruit_generator.py
:
def fruit_generator():
for fruit in ["apple", "banana"]:
yield fruit
raise StopIteration # [stop-iteration-return]
two_fruit_generator.py
:
def two_fruits_generator(fruits):
for fruit in fruits:
yield fruit, next(fruits) # [stop-iteration-return]
two_good_fruit_generator.py
:
def two_good_fruits_generator(fruits):
for fruit in fruits:
if not fruit.is_tasty():
continue
while True:
next_fruit = next(fruits) # [stop-iteration-return]
if next_fruit.is_tasty():
yield fruit, next_fruit
break
Correct code:
fruit_generator.py
:
def fruit_generator():
"""The example is simple enough you don't need an explicit return."""
for fruit in ["apple", "banana"]:
yield fruit
two_fruit_generator.py
:
def two_fruits_generator(fruits):
"""Catching the StopIteration."""
for fruit in fruits:
try:
yield fruit, next(fruits)
except StopIteration:
print("Sorry there is only one fruit left.")
yield fruit, None
two_good_fruit_generator.py
:
def two_good_fruits_generator(fruits):
"""A return can be used to end the iterator early, but not a StopIteration."""
for fruit in fruits:
if not fruit.is_tasty():
continue
while True:
next_fruit = next(fruits, None)
if next_fruit is None:
print("Sorry there is only one fruit left.")
yield fruit, None
# We reached the end of the 'fruits' generator but raising a
# StopIteration instead of returning would create a RuntimeError
return
if next_fruit.is_tasty():
yield fruit, next_fruit
break
Additional details:
It's possible to give a default value to next
or catch the StopIteration
,
or return directly. A StopIteration
cannot be propagated from a generator.
Related links:
Created by the refactoring checker.