Commit b4383a54 authored by Stefan Behnel's avatar Stefan Behnel

implement PEP 479: replace accidental StopIteration exceptions that exit the...

implement PEP 479: replace accidental StopIteration exceptions that exit the generator body by RuntimeError
parent 1a1261a1
......@@ -20,6 +20,10 @@ Features added
* Anonymous C tuple types can be declared as (ctype1, ctype2, ...).
* PEP 479: turn accidental StopIteration exceptions that exit generators
into a RuntimeError, activated with future import "generator_stop".
See http://legacy.python.org/dev/peps/pep-0479/
Bugs fixed
----------
......
......@@ -10,5 +10,6 @@ print_function = _get_feature("print_function")
absolute_import = _get_feature("absolute_import")
nested_scopes = _get_feature("nested_scopes") # dummy
generators = _get_feature("generators") # dummy
generator_stop = _get_feature("generator_stop")
del _get_feature
......@@ -25,6 +25,7 @@ from .Symtab import (ModuleScope, LocalScope, ClosureScope,
StructOrUnionScope, PyClassScope, CppClassScope, TemplateScope)
from .Code import UtilityCode
from .StringEncoding import EncodedString, escape_byte_string, split_string_literal
from . import Future
from . import Options
from . import DebugFlags
......@@ -4028,6 +4029,11 @@ class GeneratorBodyDefNode(DefNode):
if not self.body.is_terminator:
code.put_goto(code.return_label)
code.put_label(code.error_label)
if Future.generator_stop in env.global_scope().context.future_directives:
# PEP 479: turn accidental StopIteration exceptions into a RuntimeError
code.globalstate.use_utility_code(UtilityCode.load_cached("pep479", "Generator.c"))
code.putln("if (unlikely(PyErr_ExceptionMatches(PyExc_StopIteration))) "
"__Pyx_Generator_Replace_StopIteration();")
for cname, type in code.funcstate.all_managed_temps():
code.put_xdecref(cname, type)
code.put_add_traceback(self.entry.qualified_name)
......
......@@ -20,6 +20,26 @@ static CYTHON_INLINE PyObject* __Pyx_Generator_Yield_From(__pyx_GeneratorObject
return NULL;
}
//////////////////// pep479.proto ////////////////////
static void __Pyx_Generator_Replace_StopIteration(void); /*proto*/
//////////////////// pep479 ////////////////////
//@requires: Exceptions.c::GetException
static void __Pyx_Generator_Replace_StopIteration(void) {
PyObject *exc, *val, *tb;
// Chain exceptions by moving StopIteration to exc_info before creating the RuntimeError.
// In Py2.x, no chaining happens, but the exception still stays visible in exc_info.
__Pyx_GetException(&exc, &val, &tb);
Py_XDECREF(exc);
Py_XDECREF(val);
Py_XDECREF(tb);
PyErr_SetString(PyExc_RuntimeError, "generator raised StopIteration");
}
//////////////////// Generator.proto ////////////////////
#define __Pyx_Generator_USED
#include <structmember.h>
......
# mode: run
# tag: generators, pep479
from __future__ import generator_stop
import sys
if sys.version_info[0] >= 3:
# additionally test exception chaining
__doc__ = u"""
>>> g = test_raise_StopIteration_value()
>>> next(g)
1
>>> try: next(g)
... except RuntimeError as exc:
... print(type(exc.__context__) is StopIteration or type(exc.__context__), exc.__context__)
... else:
... print("NOT RAISED!")
True huhu
"""
def test_raise_StopIteration():
"""
>>> g = test_raise_StopIteration()
>>> next(g)
1
>>> next(g)
Traceback (most recent call last):
RuntimeError: generator raised StopIteration
"""
yield 1
raise StopIteration
def test_raise_StopIteration_value():
"""
>>> g = test_raise_StopIteration_value()
>>> next(g)
1
>>> next(g)
Traceback (most recent call last):
RuntimeError: generator raised StopIteration
"""
yield 1
raise StopIteration('huhu')
def test_return():
"""
>>> g = test_return()
>>> next(g)
1
>>> next(g)
Traceback (most recent call last):
StopIteration
"""
yield 1
return
def test_return_value():
"""
>>> g = test_return_value()
>>> next(g)
1
>>> next(g)
Traceback (most recent call last):
StopIteration: 2
"""
yield 1
return 2
def test_propagate_StopIteration(it):
"""
>>> results = []
>>> for x in test_propagate_StopIteration(iter([])):
... results.append(x)
Traceback (most recent call last):
RuntimeError: generator raised StopIteration
>>> results
[]
>>> for x in test_propagate_StopIteration(iter([1, 2])):
... results.append(x)
Traceback (most recent call last):
RuntimeError: generator raised StopIteration
>>> results
[1, 2]
"""
while True:
yield next(it)
def test_catch_StopIteration(it):
"""
>>> for x in test_catch_StopIteration(iter([])):
... print(x)
>>> for x in test_catch_StopIteration(iter([1, 2])):
... print(x)
1
2
"""
try:
while True:
yield next(it)
except StopIteration:
pass
else:
print("NOT RAISED!")
def test_yield_from(it):
"""
>>> for x in test_yield_from(iter([])):
... print(x)
>>> for x in test_yield_from(iter([1, 2])):
... print(x)
1
2
"""
yield from it
def test_yield_from_gen():
"""
>>> for x in test_yield_from_gen():
... print(x)
1
RETURN: 2
"""
x = yield from test_return_value()
print("RETURN: %s" % x)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment