diff -r 20a5a56ce090 Doc/howto/functional.rst --- a/Doc/howto/functional.rst Wed Jan 07 13:14:47 2015 +1000 +++ b/Doc/howto/functional.rst Fri Jan 09 01:46:58 2015 +1100 @@ -481,10 +481,10 @@ You could equally write ``for i in generate_ints(5)``, or ``a,b,c = generate_ints(3)``. -Inside a generator function, ``return value`` is semantically equivalent to -``raise StopIteration(value)``. If no value is returned or the bottom of the -function is reached, the procession of values ends and the generator cannot -return any further values. +Inside a generator function, ``return value`` causes ``StopIteration(value)`` +to be raised from the :meth:`~generator.__next__` method. Once this happens, or +the bottom of the function is reached, the procession of values ends and the +generator cannot yield any further values. You could achieve the effect of generators manually by writing your own class and storing all the local variables of the generator as instance variables. For diff -r 20a5a56ce090 Doc/library/__future__.rst --- a/Doc/library/__future__.rst Wed Jan 07 13:14:47 2015 +1000 +++ b/Doc/library/__future__.rst Fri Jan 09 01:46:58 2015 +1100 @@ -87,6 +87,9 @@ | unicode_literals | 2.6.0a2 | 3.0 | :pep:`3112`: | | | | | *Bytes literals in Python 3000* | +------------------+-------------+--------------+---------------------------------------------+ +| generator_stop | 3.5.0a1 | 3.7 | :pep:`479`: | +| | | | *StopIteration handling inside generators* | ++------------------+-------------+--------------+---------------------------------------------+ .. seealso:: diff -r 20a5a56ce090 Doc/library/exceptions.rst --- a/Doc/library/exceptions.rst Wed Jan 07 13:14:47 2015 +1000 +++ b/Doc/library/exceptions.rst Fri Jan 09 01:46:58 2015 +1100 @@ -310,10 +310,18 @@ raised, and the value returned by the function is used as the :attr:`value` parameter to the constructor of the exception. + If a generator function created in the presence of a ``from __future__ + import generator_stop`` directive raises :exc:`StopIteration`, it will be + converted into a :exc:`RuntimeError` (retaining the :exc:`StopIteration` + as the new exception's cause). + .. versionchanged:: 3.3 Added ``value`` attribute and the ability for generator functions to use it to return a value. + .. versionchanged:: 3.5 + Introduced the RuntimeError transformation. + .. exception:: SyntaxError Raised when the parser encounters a syntax error. This may occur in an diff -r 20a5a56ce090 Doc/reference/expressions.rst --- a/Doc/reference/expressions.rst Wed Jan 07 13:14:47 2015 +1000 +++ b/Doc/reference/expressions.rst Fri Jan 09 01:46:58 2015 +1100 @@ -442,8 +442,7 @@ .. method:: generator.close() Raises a :exc:`GeneratorExit` at the point where the generator function was - paused. If the generator function then raises :exc:`StopIteration` (by - exiting normally, or due to already being closed) or :exc:`GeneratorExit` (by + paused. If the generator function then exits, or raises :exc:`GeneratorExit` (by not catching the exception), close returns to its caller. If the generator yields a value, a :exc:`RuntimeError` is raised. If the generator raises any other exception, it is propagated to the caller. :meth:`close` does nothing diff -r 20a5a56ce090 Include/code.h --- a/Include/code.h Wed Jan 07 13:14:47 2015 +1000 +++ b/Include/code.h Fri Jan 09 01:46:58 2015 +1100 @@ -62,6 +62,7 @@ #define CO_FUTURE_UNICODE_LITERALS 0x20000 #define CO_FUTURE_BARRY_AS_BDFL 0x40000 +#define CO_FUTURE_GENERATOR_STOP 0x80000 /* This value is found in the co_cell2arg array when the associated cell variable does not correspond to an argument. The maximum number of diff -r 20a5a56ce090 Include/compile.h --- a/Include/compile.h Wed Jan 07 13:14:47 2015 +1000 +++ b/Include/compile.h Fri Jan 09 01:46:58 2015 +1100 @@ -27,6 +27,7 @@ #define FUTURE_PRINT_FUNCTION "print_function" #define FUTURE_UNICODE_LITERALS "unicode_literals" #define FUTURE_BARRY_AS_BDFL "barry_as_FLUFL" +#define FUTURE_GENERATOR_STOP "generator_stop" struct _mod; /* Declare the existence of this type */ #define PyAST_Compile(mod, s, f, ar) PyAST_CompileEx(mod, s, f, -1, ar) diff -r 20a5a56ce090 Include/pythonrun.h --- a/Include/pythonrun.h Wed Jan 07 13:14:47 2015 +1000 +++ b/Include/pythonrun.h Fri Jan 09 01:46:58 2015 +1100 @@ -9,7 +9,8 @@ #define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | \ CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | \ - CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL) + CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | \ + CO_FUTURE_GENERATOR_STOP) #define PyCF_MASK_OBSOLETE (CO_NESTED) #define PyCF_SOURCE_IS_UTF8 0x0100 #define PyCF_DONT_IMPLY_DEDENT 0x0200 diff -r 20a5a56ce090 Lib/__future__.py --- a/Lib/__future__.py Wed Jan 07 13:14:47 2015 +1000 +++ b/Lib/__future__.py Fri Jan 09 01:46:58 2015 +1100 @@ -56,6 +56,7 @@ "print_function", "unicode_literals", "barry_as_FLUFL", + "generator_stop", ] __all__ = ["all_feature_names"] + all_feature_names @@ -72,6 +73,7 @@ CO_FUTURE_PRINT_FUNCTION = 0x10000 # print function CO_FUTURE_UNICODE_LITERALS = 0x20000 # unicode string literals CO_FUTURE_BARRY_AS_BDFL = 0x40000 +CO_FUTURE_GENERATOR_STOP = 0x80000 # StopIteration becomes RuntimeError in generators class _Feature: def __init__(self, optionalRelease, mandatoryRelease, compiler_flag): @@ -132,3 +134,7 @@ barry_as_FLUFL = _Feature((3, 1, 0, "alpha", 2), (3, 9, 0, "alpha", 0), CO_FUTURE_BARRY_AS_BDFL) + +generator_stop = _Feature((3, 5, 0, "alpha", 1), + (3, 7, 0, "alpha", 0), + CO_FUTURE_GENERATOR_STOP) diff -r 20a5a56ce090 Lib/contextlib.py --- a/Lib/contextlib.py Wed Jan 07 13:14:47 2015 +1000 +++ b/Lib/contextlib.py Fri Jan 09 01:46:58 2015 +1100 @@ -81,6 +81,11 @@ # was passed to throw(). This prevents a StopIteration # raised inside the "with" statement from being suppressed return exc is not value + except RuntimeError as exc: + # Likewise, suppress if that StopException got wrapped up. + if exc.__context__ is value: + return False + raise except: # only re-raise if it's *not* the exception that was # passed to throw(), because __exit__() must not raise diff -r 20a5a56ce090 Lib/difflib.py --- a/Lib/difflib.py Wed Jan 07 13:14:47 2015 +1000 +++ b/Lib/difflib.py Fri Jan 09 01:46:58 2015 +1100 @@ -1516,6 +1516,7 @@ yield from_line,to_line,True def _line_pair_iterator(): + try: """Yields from/to lines of text with a change indication. This function is an iterator. It itself pulls lines from the line @@ -1542,19 +1543,21 @@ from_line, fromDiff = fromlines.pop(0) to_line, to_diff = tolines.pop(0) yield (from_line,to_line,fromDiff or to_diff) + except StopIteration: + pass # Stop when done # Handle case where user does not want context differencing, just yield # them up without doing anything else with them. line_pair_iterator = _line_pair_iterator() if context is None: - while True: - yield next(line_pair_iterator) + yield from line_pair_iterator # Handle case where user wants context differencing. We must do some # storage of lines until we know for sure that they are to be yielded. else: context += 1 lines_to_write = 0 - while True: + try: + while True: # Store lines up until we find a difference, note use of a # circular queue because we only need to keep around what # we need for context. @@ -1588,6 +1591,8 @@ else: lines_to_write -= 1 yield from_line, to_line, found_diff + except StopIteration: + return # When the helper terminates, end. _file_template = """ diff -r 20a5a56ce090 Objects/genobject.c --- a/Objects/genobject.c Wed Jan 07 13:14:47 2015 +1000 +++ b/Objects/genobject.c Fri Jan 09 01:46:58 2015 +1100 @@ -130,6 +130,27 @@ } Py_CLEAR(result); } + else if (!result) { + if (PyErr_ExceptionMatches(PyExc_StopIteration) + /* Check for __future__ generator_stop and conditionally turn a leaking + StopIteration into RuntimeError (with its cause set appropriately). */ + && (((PyCodeObject *)gen->gi_code)->co_flags & CO_FUTURE_GENERATOR_STOP)) { + PyObject *exc, *val, *val2, *tb; + PyErr_Fetch(&exc, &val, &tb); + PyErr_NormalizeException(&exc, &val, &tb); + if (tb != NULL) { + PyException_SetTraceback(val, tb); + } + Py_DECREF(exc); + Py_XDECREF(tb); + PyErr_SetString(PyExc_RuntimeError, + "generator raised StopIteration"); + PyErr_Fetch(&exc, &val2, &tb); + PyErr_NormalizeException(&exc, &val2, &tb); + PyException_SetCause(val2, val); + PyErr_Restore(exc, val2, tb); + } + } if (!result || f->f_stacktop == NULL) { /* generator can't be rerun, so release the frame */ diff -r 20a5a56ce090 Python/future.c --- a/Python/future.c Wed Jan 07 13:14:47 2015 +1000 +++ b/Python/future.c Fri Jan 09 01:46:58 2015 +1100 @@ -40,6 +40,8 @@ continue; } else if (strcmp(feature, FUTURE_BARRY_AS_BDFL) == 0) { ff->ff_features |= CO_FUTURE_BARRY_AS_BDFL; + } else if (strcmp(feature, FUTURE_GENERATOR_STOP) == 0) { + ff->ff_features |= CO_FUTURE_GENERATOR_STOP; } else if (strcmp(feature, "braces") == 0) { PyErr_SetString(PyExc_SyntaxError, "not a chance");