summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorYury Selivanov <yselivanov@sprymix.com>2015-05-09 15:44:30 (GMT)
committerYury Selivanov <yselivanov@sprymix.com>2015-05-09 15:44:30 (GMT)
commit8170e8c0d12cb9414f3a3d3ca81a447b4afc5f26 (patch)
treed6353155110b49bf7953fd0d7d3f808512e8feb1 /Lib
parentbd60e8dece89440ebdc80a19b2217d5ba2515124 (diff)
downloadcpython-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__.py6
-rw-r--r--Lib/contextlib.py11
-rw-r--r--Lib/difflib.py3
-rw-r--r--Lib/test/test_contextlib.py34
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):