diff --git a/Src/IronPython/Compiler/Ast/FunctionDefinition.cs b/Src/IronPython/Compiler/Ast/FunctionDefinition.cs
index c621c6576..c9e9cd9fc 100644
--- a/Src/IronPython/Compiler/Ast/FunctionDefinition.cs
+++ b/Src/IronPython/Compiler/Ast/FunctionDefinition.cs
@@ -124,6 +124,8 @@ internal override int KwOnlyArgCount {
///
public bool IsGenerator { get; set; }
+ internal bool GeneratorStop { get; set; }
+
///
/// Called by parser to mark that this function can set sys.exc_info().
/// An alternative technique would be to just walk the body after the parse and look for a except block.
@@ -181,6 +183,10 @@ internal override FunctionAttributes Flags {
fa |= FunctionAttributes.Generator;
}
+ if (GeneratorStop) {
+ fa |= FunctionAttributes.GeneratorStop;
+ }
+
return fa;
}
}
diff --git a/Src/IronPython/Compiler/Ast/PythonAst.cs b/Src/IronPython/Compiler/Ast/PythonAst.cs
index b357d96d9..4f94217ff 100644
--- a/Src/IronPython/Compiler/Ast/PythonAst.cs
+++ b/Src/IronPython/Compiler/Ast/PythonAst.cs
@@ -317,6 +317,12 @@ internal override Microsoft.Scripting.Ast.LightLambdaExpression GetLambda() {
#region Public API
+ internal bool GeneratorStop {
+ get {
+ return (_languageFeatures & ModuleOptions.GeneratorStop) != 0;
+ }
+ }
+
public Statement Body {
get { return _body; }
}
diff --git a/Src/IronPython/Compiler/Parser.cs b/Src/IronPython/Compiler/Parser.cs
index 74d2a24d2..3ad159ded 100644
--- a/Src/IronPython/Compiler/Parser.cs
+++ b/Src/IronPython/Compiler/Parser.cs
@@ -583,6 +583,7 @@ private Expression ParseYieldExpression() {
FunctionDefinition current = CurrentFunction;
if (current != null) {
current.IsGenerator = true;
+ current.GeneratorStop = GeneratorStop;
}
var start = GetStart();
@@ -889,6 +890,9 @@ private FromImportStatement ParseFromImportStmt() {
// Ignored in Python 3
} else if (name == "unicode_literals") {
// Ignored in Python 3
+ } else if (name == "generator_stop") {
+ // New in 3.5, mandatory in 3.7
+ _languageFeatures |= ModuleOptions.GeneratorStop;
} else if (name == "nested_scopes") {
} else if (name == "generators") {
} else {
@@ -2500,6 +2504,7 @@ private Expression ParseGeneratorExpression(Expression expr) {
Parameter parameter = new Parameter("__gen_$_parm__", 0);
FunctionDefinition func = new FunctionDefinition(fname, new Parameter[] { parameter }, root);
func.IsGenerator = true;
+ func.GeneratorStop = GeneratorStop;
func.SetLoc(_globalParent, root.StartIndex, GetEnd());
func.HeaderIndex = root.EndIndex;
@@ -3159,6 +3164,8 @@ private Token EatEndOfInput() {
return PythonOps.BadSourceEncodingError(message, lineNum, _sourceUnit.Path);
}
+ private bool GeneratorStop => (_languageFeatures & ModuleOptions.GeneratorStop) == ModuleOptions.GeneratorStop;
+
private void StartParsing() {
if (_parsingStarted)
throw new InvalidOperationException("Parsing already started. Use Restart to start again.");
diff --git a/Src/IronPython/Compiler/PythonCompilerOptions.cs b/Src/IronPython/Compiler/PythonCompilerOptions.cs
index 8279367d1..645ee7491 100644
--- a/Src/IronPython/Compiler/PythonCompilerOptions.cs
+++ b/Src/IronPython/Compiler/PythonCompilerOptions.cs
@@ -55,6 +55,16 @@ public int[] InitialIndent {
}
}
+ internal bool GeneratorStop {
+ get {
+ return (_module & ModuleOptions.GeneratorStop) != 0;
+ }
+ set {
+ if (value) _module |= ModuleOptions.GeneratorStop;
+ else _module &= ~ModuleOptions.GeneratorStop;
+ }
+ }
+
public bool Verbatim {
get {
return (_module & ModuleOptions.Verbatim) != 0;
diff --git a/Src/IronPython/Modules/Builtin.cs b/Src/IronPython/Modules/Builtin.cs
index 4dfe24e0e..e28dc7440 100644
--- a/Src/IronPython/Modules/Builtin.cs
+++ b/Src/IronPython/Modules/Builtin.cs
@@ -1642,6 +1642,9 @@ internal static PythonCompilerOptions GetRuntimeGeneratedCodeCompilerOptions(Cod
if ((cflags & CompileFlags.CO_FUTURE_UNICODE_LITERALS) != 0) {
// Ignored since Python 3
}
+ if ((cflags & CompileFlags.CO_FUTURE_GENERATOR_STOP) != 0) {
+ langFeat |= ModuleOptions.GeneratorStop;
+ }
pco.Module |= langFeat;
// The options created this way never creates
@@ -1662,7 +1665,7 @@ private static CompileFlags GetCompilerFlags(int flags) {
CompileFlags cflags = (CompileFlags)flags;
if ((cflags & ~(CompileFlags.CO_NESTED | CompileFlags.CO_GENERATOR_ALLOWED | CompileFlags.CO_FUTURE_DIVISION | CompileFlags.CO_DONT_IMPLY_DEDENT |
CompileFlags.CO_FUTURE_ABSOLUTE_IMPORT | CompileFlags.CO_FUTURE_WITH_STATEMENT | CompileFlags.CO_FUTURE_PRINT_FUNCTION |
- CompileFlags.CO_FUTURE_UNICODE_LITERALS | CompileFlags.CO_FUTURE_BARRY_AS_BDFL)) != 0) {
+ CompileFlags.CO_FUTURE_UNICODE_LITERALS | CompileFlags.CO_FUTURE_BARRY_AS_BDFL | CompileFlags.CO_FUTURE_GENERATOR_STOP)) != 0) {
throw PythonOps.ValueError("unrecognized flags");
}
diff --git a/Src/IronPython/Runtime/CompileFlags.cs b/Src/IronPython/Runtime/CompileFlags.cs
index f87efd724..b50bc0d75 100644
--- a/Src/IronPython/Runtime/CompileFlags.cs
+++ b/Src/IronPython/Runtime/CompileFlags.cs
@@ -7,14 +7,15 @@
namespace IronPython.Runtime {
[Flags]
public enum CompileFlags {
- CO_NESTED = 0x0010, // nested_scopes
- CO_DONT_IMPLY_DEDENT = 0x0200, // report errors if statement isn't dedented.
- CO_GENERATOR_ALLOWED = 0x1000, // generators
- CO_FUTURE_DIVISION = 0x2000, // division
- CO_FUTURE_ABSOLUTE_IMPORT = 0x4000, // absolute imports by default
+ CO_NESTED = 0x0010, // nested_scopes
+ CO_DONT_IMPLY_DEDENT = 0x0200, // report errors if statement isn't dedented
+ CO_GENERATOR_ALLOWED = 0x1000, // generators
+ CO_FUTURE_DIVISION = 0x2000, // division
+ CO_FUTURE_ABSOLUTE_IMPORT = 0x4000, // perform absolute imports by default
CO_FUTURE_WITH_STATEMENT = 0x8000, // with statement
CO_FUTURE_PRINT_FUNCTION = 0x10000, // print function
CO_FUTURE_UNICODE_LITERALS = 0x20000, // default unicode literals
- CO_FUTURE_BARRY_AS_BDFL = 0x40000, //
+ CO_FUTURE_BARRY_AS_BDFL = 0x40000, //
+ CO_FUTURE_GENERATOR_STOP = 0x80000, // StopIteration becomes RuntimeError in generators
}
}
diff --git a/Src/IronPython/Runtime/FunctionAttributes.cs b/Src/IronPython/Runtime/FunctionAttributes.cs
index c55d8c154..6ffc0c85b 100644
--- a/Src/IronPython/Runtime/FunctionAttributes.cs
+++ b/Src/IronPython/Runtime/FunctionAttributes.cs
@@ -29,5 +29,6 @@ public enum FunctionAttributes {
/// IronPython specific: Set if the function includes a try/finally block.
///
ContainsTryFinally = 0x8000,
+ GeneratorStop = 0x80000, // TODO: delete me in 3.7
}
}
diff --git a/Src/IronPython/Runtime/Generator.cs b/Src/IronPython/Runtime/Generator.cs
index 813068419..231721a5e 100644
--- a/Src/IronPython/Runtime/Generator.cs
+++ b/Src/IronPython/Runtime/Generator.cs
@@ -427,6 +427,14 @@ private object NextWorker() {
FinalValue = CurrentValue;
CurrentValue = OperationFailed.Value;
}
+ } catch (StopIterationException e) {
+ if (GeneratorStop) {
+ var stopIterationError = e.GetPythonException();
+ throw PythonExceptions.CreatePythonThrowable(PythonExceptions.RuntimeError, "generator raised StopIteration")
+ .CreateClrExceptionWithCause(stopIterationError, stopIterationError, true);
+ } else {
+ throw;
+ }
} finally {
// A generator restores the sys.exc_info() status after each yield point.
PythonOps.CurrentExceptionState = _state.PrevException;
@@ -561,6 +569,8 @@ internal bool ContainsTryFinally {
}
}
+ private bool GeneratorStop => (_function.Flags & FunctionAttributes.GeneratorStop) != 0;
+
#endregion
#region ICodeFormattable Members
diff --git a/Src/IronPython/Runtime/ModuleOptions.cs b/Src/IronPython/Runtime/ModuleOptions.cs
index e5f28910b..9d051028c 100644
--- a/Src/IronPython/Runtime/ModuleOptions.cs
+++ b/Src/IronPython/Runtime/ModuleOptions.cs
@@ -53,6 +53,7 @@ public enum ModuleOptions {
///
/// Generated code should support light exceptions
///
- LightThrow = 0x8000
+ LightThrow = 0x8000,
+ GeneratorStop = 0x10000,
}
}
diff --git a/Src/StdLib/Lib/__future__.py b/Src/StdLib/Lib/__future__.py
index 3b2d5ecb9..63b2be352 100644
--- a/Src/StdLib/Lib/__future__.py
+++ b/Src/StdLib/Lib/__future__.py
@@ -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 @@ def __repr__(self):
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/Src/StdLib/Lib/test/test_pep479.py b/Src/StdLib/Lib/test/test_pep479.py
new file mode 100644
index 000000000..bc235ceb0
--- /dev/null
+++ b/Src/StdLib/Lib/test/test_pep479.py
@@ -0,0 +1,34 @@
+from __future__ import generator_stop
+
+import unittest
+
+
+class TestPEP479(unittest.TestCase):
+ def test_stopiteration_wrapping(self):
+ def f():
+ raise StopIteration
+ def g():
+ yield f()
+ with self.assertRaisesRegex(RuntimeError,
+ "generator raised StopIteration"):
+ next(g())
+
+ def test_stopiteration_wrapping_context(self):
+ def f():
+ raise StopIteration
+ def g():
+ yield f()
+
+ try:
+ next(g())
+ except RuntimeError as exc:
+ self.assertIs(type(exc.__cause__), StopIteration)
+ self.assertIs(type(exc.__context__), StopIteration)
+ self.assertTrue(exc.__suppress_context__)
+ else:
+ self.fail('__cause__, __context__, or __suppress_context__ '
+ 'were not properly set')
+
+
+if __name__ == '__main__':
+ unittest.main()