diff -r 84d7ec21cc43 Lib/difflib.py --- a/Lib/difflib.py Thu May 21 20:15:40 2015 +0300 +++ b/Lib/difflib.py Thu May 21 14:28:34 2015 -0400 @@ -1582,7 +1582,10 @@ while True: # Collecting lines of text until we have a from/to pair while (len(fromlines)==0 or len(tolines)==0): - from_line, to_line, found_diff = next(line_iterator) + try: + from_line, to_line, found_diff = next(line_iterator) + except StopIteration: + return if from_line is not None: fromlines.append((from_line,found_diff)) if to_line is not None: @@ -1609,7 +1612,10 @@ index, contextLines = 0, [None]*(context) found_diff = False while(found_diff is False): - from_line, to_line, found_diff = next(line_pair_iterator) + try: + from_line, to_line, found_diff = next(line_pair_iterator) + except StopIteration: + return i = index % context contextLines[i] = (from_line, to_line, found_diff) index += 1 diff -r 84d7ec21cc43 Lib/test/test_contextlib.py --- a/Lib/test/test_contextlib.py Thu May 21 20:15:40 2015 +0300 +++ b/Lib/test/test_contextlib.py Thu May 21 14:28:34 2015 -0400 @@ -4,6 +4,7 @@ import sys import tempfile import unittest +import warnings from contextlib import * # Tests __all__ from test import support try: @@ -89,8 +90,11 @@ def woohoo(): yield try: - with woohoo(): - raise stop_exc + with warnings.catch_warnings(): + warnings.simplefilter( + 'ignore', category=PendingDeprecationWarning) + with woohoo(): + raise stop_exc except Exception as ex: self.assertIs(ex, stop_exc) else: diff -r 84d7ec21cc43 Lib/test/test_generators.py --- a/Lib/test/test_generators.py Thu May 21 20:15:40 2015 +0300 +++ b/Lib/test/test_generators.py Thu May 21 14:28:34 2015 -0400 @@ -1,6 +1,7 @@ import gc import sys import unittest +import warnings import weakref from test import support @@ -217,6 +218,44 @@ self.assertEqual(next(g), "done") self.assertEqual(sys.exc_info(), (None, None, None)) + def test_stopiteration_warning(self): + # See also PEP 479. + + def gen(): + raise StopIteration + yield + + with self.assertRaises(StopIteration), warnings.catch_warnings(): + warnings.simplefilter('ignore', category=PendingDeprecationWarning) + next(gen()) + + with self.assertRaisesRegex(PendingDeprecationWarning, + "generator .* raised StopIteration"), \ + warnings.catch_warnings(): + + warnings.simplefilter('error') + next(gen()) + + + def test_tutorial_stopiteration(self): + # Raise StopIteration" stops the generator too: + + def f(): + yield 1 + raise StopIteration + yield 2 # never reached + + g = f() + self.assertEqual(next(g), 1) + + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=PendingDeprecationWarning) + + with self.assertRaises(StopIteration): + next(g) + with self.assertRaises(StopIteration): + next(g) + tutorial_tests = """ Let's try a simple generator: @@ -263,26 +302,7 @@ File "", line 1, in ? StopIteration -"raise StopIteration" stops the generator too: - - >>> def f(): - ... yield 1 - ... raise StopIteration - ... yield 2 # never reached - ... - >>> g = f() - >>> next(g) - 1 - >>> next(g) - Traceback (most recent call last): - File "", line 1, in ? - StopIteration - >>> next(g) - Traceback (most recent call last): - File "", line 1, in ? - StopIteration - -However, they are not exactly equivalent: +However, "return" and StopIteration are not exactly equivalent: >>> def g1(): ... try: diff -r 84d7ec21cc43 Lib/test/test_with.py --- a/Lib/test/test_with.py Thu May 21 20:15:40 2015 +0300 +++ b/Lib/test/test_with.py Thu May 21 14:28:34 2015 -0400 @@ -6,6 +6,7 @@ import sys import unittest +import warnings from collections import deque from contextlib import _GeneratorContextManager, contextmanager @@ -454,7 +455,9 @@ with cm(): raise StopIteration("from with") - self.assertRaises(StopIteration, shouldThrow) + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=PendingDeprecationWarning) + self.assertRaises(StopIteration, shouldThrow) def testRaisedStopIteration2(self): # From bug 1462485 @@ -481,7 +484,9 @@ with cm(): raise next(iter([])) - self.assertRaises(StopIteration, shouldThrow) + with warnings.catch_warnings(): + warnings.simplefilter('ignore', category=PendingDeprecationWarning) + self.assertRaises(StopIteration, shouldThrow) def testRaisedGeneratorExit1(self): # From bug 1462485 diff -r 84d7ec21cc43 Objects/genobject.c --- a/Objects/genobject.c Thu May 21 20:15:40 2015 +0300 +++ b/Objects/genobject.c Thu May 21 14:28:34 2015 -0400 @@ -143,13 +143,12 @@ } Py_CLEAR(result); } - else if (!result) { + else if (!result && PyErr_ExceptionMatches(PyExc_StopIteration)) { /* Check for __future__ generator_stop and conditionally turn * a leaking StopIteration into RuntimeError (with its cause * set appropriately). */ - if ((((PyCodeObject *)gen->gi_code)->co_flags & + if (((PyCodeObject *)gen->gi_code)->co_flags & (CO_FUTURE_GENERATOR_STOP | CO_COROUTINE | CO_ITERABLE_COROUTINE)) - && PyErr_ExceptionMatches(PyExc_StopIteration)) { PyObject *exc, *val, *val2, *tb; PyErr_Fetch(&exc, &val, &tb); @@ -167,6 +166,24 @@ PyException_SetContext(val2, val); PyErr_Restore(exc, val2, tb); } + else { + PyObject *exc, *val, *tb; + + /* Pop the exception before issuing a warning. */ + PyErr_Fetch(&exc, &val, &tb); + + if (PyErr_WarnFormat(PyExc_PendingDeprecationWarning, 1, + "generator '%.50S' raised StopIteration", + gen->gi_qualname)) { + /* Warning was converted to an error. */ + Py_XDECREF(exc); + Py_XDECREF(val); + Py_XDECREF(tb); + } + else { + PyErr_Restore(exc, val, tb); + } + } } if (!result || f->f_stacktop == NULL) {