Handle StopIteration at an iterator consumer boundary
Handle StopIteration at an iterator consumer boundary
To handle StopIteration at an iterator consumer boundary, call next(iterator, MISSING) with a private sentinel and test identity with is. The two-argument form of next returns its default only when the iterator is exhausted, so it avoids allowing StopIteration from an ordinary read to escape the consumer.
The failure branch intentionally consumes iter([10]) twice with one-argument next. The first read produces 10; the second raises StopIteration. The corrected read_once function supplies MISSING as the default and translates that boundary condition into the explicit result "exhausted". It also consumes iter([None]): the output is value:None, not exhausted, because None is a legitimate stored value and is not the sentinel.
Keep the boundary narrow. This technique handles exhaustion from the specific iterator passed to next; it does not convert StopIteration raised elsewhere in a larger operation into normal completion. A sentinel should be an object that cannot be confused with a valid payload, and callers should decide whether returning a marker, None, or a domain-specific result best fits their API.
The optional-default form of next is documented in current Python 3 and has been available throughout Python 3. See the built-in next documentation.
AI assistance disclosure: This article was drafted with AI assistance and checked using the synthetic example shown below.
MISSING = object()
def read_once(iterator):
value = next(iterator, MISSING)
if value is MISSING:
return "exhausted"
return f"value:{value!r}"
unsafe = iter([10])
print(f"unsafe-first:{next(unsafe)}")
try:
next(unsafe)
except StopIteration:
print("unsafe-second:StopIteration")
safe = iter([10])
first = read_once(safe)
second = read_once(safe)
none_value = read_once(iter([None]))
assert (first, second, none_value) == ("value:10", "exhausted", "value:None")
print(f"safe-first:{first}")
print(f"safe-second:{second}")
print(f"stored-none:{none_value}")
unsafe-first:10
unsafe-second:StopIteration
safe-first:value:10
safe-second:exhausted
stored-none:value:None