1

Problem

Cannot catch a StopIteration raised from within a iterator.

Example

from typing import (
    Generator,
    Iterable,
    List
)
import sys
import pathlib
from itertools import islice

import numpy as np


def take(n: int, iterable: Iterable) -> List:
    taken = list(islice(iterable, 0, n))
    if len(taken) > 0:
        return taken
    else:
        print("Raising StopIteration")
        raise StopIteration("Nothing to take")     # <----- Raise StopIteration


def file_line_stream(path: str) -> Generator[str, None, None]:
    if not pathlib.Path(path).is_file():
        raise FileNotFoundError(f"file {path} does not exist or non file")
    try:
        _file = pathlib.Path(path)
        with _file.open() as f:
            for line in f:
                yield line.rstrip()
    except (IOError, FileNotFoundError) as e:
        raise RuntimeError("Read file failure") from e


def get_sentences(path_to_file, num_sentences):
    counter = 0
    stream = file_line_stream(path_to_file)
    try:
        while True:
            _lines = take(num_sentences, stream)
            print(f"Count {counter}:  yielding [{_lines[0]}]\n")
            yield np.array(_lines)
            counter += 1
    finally:
        stream.close()


def test():
    path_to_input = "test_has_two_lines.txt"
    generator = get_sentences(path_to_file=path_to_input, num_sentences=100)
    for i in range(10):
        # ----------------------------------------------------------------------
        # Want to catch the StopIteration raised from within the generator.
        # ----------------------------------------------------------------------
        try:
            next(generator)
        except StopIteration as e:
            print("Caught at StopIteration except")
            generator.close()

        except Exception as e:      # <--- Raised StopIteration will get caught here
            print("Caught at catch-all except: %s", sys.exc_info()[0])
            generator.close()
            raise e


if __name__ == '__main__':
    test()

Result

Count 0:  yielding [previous monthly <unk> of the major market index futures and standard & poor 's <unk> index options have produced spectacular volatility]

Raising StopIteration
Caught at catch-all except: %s <class 'RuntimeError'>
Traceback (most recent call last):
  File "test_050_word2vec.py", line 39, in get_sentences
    _lines = take(num_sentences, stream)
  File "test_050_word2vec.py", line 19, in take
    raise StopIteration("Nothing to take")     # <----- Raise StopIteration
StopIteration: Nothing to take

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "test_050_word2vec.py", line 67, in <module>
    test()
  File "test_050_word2vec.py", line 63, in test
    raise e
  File "test_050_word2vec.py", line 55, in test
    next(generator)
RuntimeError: generator raised StopIteration    <----- Converted into RuntimeError

1 Answer 1

1

Cause

Changed in version 3.7: Enable PEP 479 for all code by default: a StopIteration error raised in a generator is transformed into a RuntimeError.

Reason

Currently, StopIteration raised accidentally inside a generator function will be interpreted as the end of the iteration by the loop construct driving the generator.

StopIteration serves a very specific purpose (to allow a next method to indicate iteration is complete), and reusing it for other purposes will cause problems, as you've seen.

The conversion to RuntimeError here is saving you; if Python hadn't done that, the generator would have silently stopped iterating (StopIteration is swallowed silently, causing iteration to end without propagating the exception; you'd never catch it anyway).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.