From 4af42e2b28021c6c4fe68f5a0f6c2193a51c3261 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 11 Dec 2014 11:02:14 -0800 Subject: [PATCH] More clarifications in response to Oscar Benjamin. --- pep-0479.txt | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/pep-0479.txt b/pep-0479.txt index f45584094..a0467357a 100644 --- a/pep-0479.txt +++ b/pep-0479.txt @@ -43,13 +43,16 @@ The interaction of generators and ``StopIteration`` is currently somewhat surprising, and can conceal obscure bugs. An unexpected exception should not result in subtly altered behaviour, but should cause a noisy and easily-debugged traceback. Currently, -``StopIteration`` can be absorbed by the generator construct. +``StopIteration`` raised accidentally inside a generator function will +be interpreted as the end of the iteration by the loop construct +driving the generator. The main goal of the proposal is to ease debugging in the situation where an unguarded ``next()`` call (perhaps several stack frames deep) raises ``StopIteration`` and causes the iteration controlled by the -generator to terminate silently. (When another exception is raised, a -traceback is printed pinpointing the cause of the problem.) +generator to terminate silently. (Whereas, when some other exception +is raised, a traceback is printed pinpointing the cause of the +problem.) This is particularly pernicious in combination with the ``yield from`` construct of PEP 380 [1]_, as it breaks the abstraction that a @@ -352,7 +355,9 @@ iterators: a generator object is still an iterator, and not all iterators are generators. Generators have additional methods that iterators don't have, like ``send`` and ``throw``. All this is unchanged. Nothing changes for generator users -- only authors of -generator functions may have to learn something new. +generator functions may have to learn something new. (This includes +authors of generator expressions that depend on early termination of +the iteration by a ``StopIteration`` raised in a condition.) An iterator is an object with a ``__next__`` method. Like many other special methods, it may either return a value, or raise a specific @@ -489,6 +494,10 @@ block of code which reliably works on multiple versions of Python. (Using a dedicated exception type, perhaps subclassing ``ValueError``, would help this; however, all code would still need to be rewritten.) +Note that calling ``next(it, default)`` catches ``StopIteration`` and +substitutes the given default value; this feature is often useful to +avoid a ``try/except`` block. + Sub-proposal: decorator to explicitly request current behaviour --------------------------------------------------------------- @@ -556,6 +565,17 @@ their return paths, they too could turn unexpected exceptions into ``RuntimeError``; the fact that they cannot should not preclude generators from doing so. +Why not fix all __next__() methods? +----------------------------------- + +When implementing a regular ``__next__()`` method, the only way to +indicate the end of the iteration is to raise ``StopIteration``. So +catching ``StopIteration`` here and converting it to ``RuntimeError`` +would defeat the purpose. This is a reminder of the special status of +generator functions: in a generator function, raising +``StopIteration`` is redundant since the iteration can be terminated +by a simple ``return``. + References ==========