diff options
author | Yury Selivanov <yselivanov@sprymix.com> | 2015-05-09 15:44:30 (GMT) |
---|---|---|
committer | Yury Selivanov <yselivanov@sprymix.com> | 2015-05-09 15:44:30 (GMT) |
commit | 8170e8c0d12cb9414f3a3d3ca81a447b4afc5f26 (patch) | |
tree | d6353155110b49bf7953fd0d7d3f808512e8feb1 /Lib | |
parent | bd60e8dece89440ebdc80a19b2217d5ba2515124 (diff) | |
download | cpython-8170e8c0d12cb9414f3a3d3ca81a447b4afc5f26.zip cpython-8170e8c0d12cb9414f3a3d3ca81a447b4afc5f26.tar.gz cpython-8170e8c0d12cb9414f3a3d3ca81a447b4afc5f26.tar.bz2 |
PEP 479: Change StopIteration handling inside generators.
Closes issue #22906.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/__future__.py | 6 | ||||
-rw-r--r-- | Lib/contextlib.py | 11 | ||||
-rw-r--r-- | Lib/difflib.py | 3 | ||||
-rw-r--r-- | Lib/test/test_contextlib.py | 34 |
4 files changed, 50 insertions, 4 deletions
diff --git a/Lib/__future__.py b/Lib/__future__.py index 3b2d5ec..63b2be3 100644 --- a/Lib/__future__.py +++ b/Lib/__future__.py @@ -56,6 +56,7 @@ all_feature_names = [ "print_function", "unicode_literals", "barry_as_FLUFL", + "generator_stop", ] __all__ = ["all_feature_names"] + all_feature_names @@ -72,6 +73,7 @@ CO_FUTURE_WITH_STATEMENT = 0x8000 # with statement 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 @@ unicode_literals = _Feature((2, 6, 0, "alpha", 2), 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, "beta", 1), + (3, 7, 0, "alpha", 0), + CO_FUTURE_GENERATOR_STOP) diff --git a/Lib/contextlib.py b/Lib/contextlib.py index 2fbc90c..379c251 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -77,10 +77,17 @@ class _GeneratorContextManager(ContextDecorator): self.gen.throw(type, value, traceback) raise RuntimeError("generator didn't stop after throw()") except StopIteration as exc: - # Suppress the exception *unless* it's the same exception that + # Suppress StopIteration *unless* it's the same exception that # was passed to throw(). This prevents a StopIteration - # raised inside the "with" statement from being suppressed + # raised inside the "with" statement from being suppressed. return exc is not value + except RuntimeError as exc: + # Likewise, avoid suppressing if a StopIteration exception + # was passed to throw() and later wrapped into a RuntimeError + # (see PEP 479). + if exc.__cause__ 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 --git a/Lib/difflib.py b/Lib/difflib.py index 96fd9ab..aa98436 100644 --- a/Lib/difflib.py +++ b/Lib/difflib.py @@ -1596,8 +1596,7 @@ def _mdiff(fromlines, tolines, context=None, linejunk=None, # 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: diff --git a/Lib/test/test_contextlib.py b/Lib/test/test_contextlib.py index c52066b..a5d68a9 100644 --- a/Lib/test/test_contextlib.py +++ b/Lib/test/test_contextlib.py @@ -83,6 +83,40 @@ class ContextManagerTestCase(unittest.TestCase): raise ZeroDivisionError(999) self.assertEqual(state, [1, 42, 999]) + def test_contextmanager_except_stopiter(self): + stop_exc = StopIteration('spam') + @contextmanager + def woohoo(): + yield + try: + with woohoo(): + raise stop_exc + except Exception as ex: + self.assertIs(ex, stop_exc) + else: + self.fail('StopIteration was suppressed') + + def test_contextmanager_except_pep479(self): + code = """\ +from __future__ import generator_stop +from contextlib import contextmanager +@contextmanager +def woohoo(): + yield +""" + locals = {} + exec(code, locals, locals) + woohoo = locals['woohoo'] + + stop_exc = StopIteration('spam') + try: + with woohoo(): + raise stop_exc + except Exception as ex: + self.assertIs(ex, stop_exc) + else: + self.fail('StopIteration was suppressed') + def _create_contextmanager_attribs(self): def attribs(**kw): def decorate(func): |