Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Src/IronPython/Compiler/Ast/FunctionDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ internal override int KwOnlyArgCount {
/// </summary>
public bool IsGenerator { get; set; }

internal bool GeneratorStop { get; set; }

/// <summary>
/// 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.
Expand Down Expand Up @@ -181,6 +183,10 @@ internal override FunctionAttributes Flags {
fa |= FunctionAttributes.Generator;
}

if (GeneratorStop) {
fa |= FunctionAttributes.GeneratorStop;
}

return fa;
}
}
Expand Down
6 changes: 6 additions & 0 deletions Src/IronPython/Compiler/Ast/PythonAst.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
}
Expand Down
7 changes: 7 additions & 0 deletions Src/IronPython/Compiler/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ private Expression ParseYieldExpression() {
FunctionDefinition current = CurrentFunction;
if (current != null) {
current.IsGenerator = true;
current.GeneratorStop = GeneratorStop;
}

var start = GetStart();
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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.");
Expand Down
10 changes: 10 additions & 0 deletions Src/IronPython/Compiler/PythonCompilerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 4 additions & 1 deletion Src/IronPython/Modules/Builtin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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");
}

Expand Down
13 changes: 7 additions & 6 deletions Src/IronPython/Runtime/CompileFlags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
1 change: 1 addition & 0 deletions Src/IronPython/Runtime/FunctionAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ public enum FunctionAttributes {
/// IronPython specific: Set if the function includes a try/finally block.
/// </summary>
ContainsTryFinally = 0x8000,
GeneratorStop = 0x80000, // TODO: delete me in 3.7
}
}
10 changes: 10 additions & 0 deletions Src/IronPython/Runtime/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -561,6 +569,8 @@ internal bool ContainsTryFinally {
}
}

private bool GeneratorStop => (_function.Flags & FunctionAttributes.GeneratorStop) != 0;

#endregion

#region ICodeFormattable Members
Expand Down
3 changes: 2 additions & 1 deletion Src/IronPython/Runtime/ModuleOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public enum ModuleOptions {
/// <summary>
/// Generated code should support light exceptions
/// </summary>
LightThrow = 0x8000
LightThrow = 0x8000,
GeneratorStop = 0x10000,
}
}
6 changes: 6 additions & 0 deletions Src/StdLib/Lib/__future__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"print_function",
"unicode_literals",
"barry_as_FLUFL",
"generator_stop",
]

__all__ = ["all_feature_names"] + all_feature_names
Expand All @@ -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):
Expand Down Expand Up @@ -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)
34 changes: 34 additions & 0 deletions Src/StdLib/Lib/test/test_pep479.py
Original file line number Diff line number Diff line change
@@ -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()