Merge 3.4 (generator)
This commit is contained in:
commit
13a1c6022b
|
@ -89,6 +89,115 @@ class GeneratorTest(unittest.TestCase):
|
||||||
"GeneratorTest.test_name.<locals>.<genexpr>")
|
"GeneratorTest.test_name.<locals>.<genexpr>")
|
||||||
|
|
||||||
|
|
||||||
|
class ExceptionTest(unittest.TestCase):
|
||||||
|
# Tests for the issue #23353: check that the currently handled exception
|
||||||
|
# is correctly saved/restored in PyEval_EvalFrameEx().
|
||||||
|
|
||||||
|
def test_except_throw(self):
|
||||||
|
def store_raise_exc_generator():
|
||||||
|
try:
|
||||||
|
self.assertEqual(sys.exc_info()[0], None)
|
||||||
|
yield
|
||||||
|
except Exception as exc:
|
||||||
|
# exception raised by gen.throw(exc)
|
||||||
|
self.assertEqual(sys.exc_info()[0], ValueError)
|
||||||
|
self.assertIsNone(exc.__context__)
|
||||||
|
yield
|
||||||
|
|
||||||
|
# ensure that the exception is not lost
|
||||||
|
self.assertEqual(sys.exc_info()[0], ValueError)
|
||||||
|
yield
|
||||||
|
|
||||||
|
# we should be able to raise back the ValueError
|
||||||
|
raise
|
||||||
|
|
||||||
|
make = store_raise_exc_generator()
|
||||||
|
next(make)
|
||||||
|
|
||||||
|
try:
|
||||||
|
raise ValueError()
|
||||||
|
except Exception as exc:
|
||||||
|
try:
|
||||||
|
make.throw(exc)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
next(make)
|
||||||
|
with self.assertRaises(ValueError) as cm:
|
||||||
|
next(make)
|
||||||
|
self.assertIsNone(cm.exception.__context__)
|
||||||
|
|
||||||
|
self.assertEqual(sys.exc_info(), (None, None, None))
|
||||||
|
|
||||||
|
def test_except_next(self):
|
||||||
|
def gen():
|
||||||
|
self.assertEqual(sys.exc_info()[0], ValueError)
|
||||||
|
yield "done"
|
||||||
|
|
||||||
|
g = gen()
|
||||||
|
try:
|
||||||
|
raise ValueError
|
||||||
|
except Exception:
|
||||||
|
self.assertEqual(next(g), "done")
|
||||||
|
self.assertEqual(sys.exc_info(), (None, None, None))
|
||||||
|
|
||||||
|
def test_except_gen_except(self):
|
||||||
|
def gen():
|
||||||
|
try:
|
||||||
|
self.assertEqual(sys.exc_info()[0], None)
|
||||||
|
yield
|
||||||
|
# we are called from "except ValueError:", TypeError must
|
||||||
|
# inherit ValueError in its context
|
||||||
|
raise TypeError()
|
||||||
|
except TypeError as exc:
|
||||||
|
self.assertEqual(sys.exc_info()[0], TypeError)
|
||||||
|
self.assertEqual(type(exc.__context__), ValueError)
|
||||||
|
# here we are still called from the "except ValueError:"
|
||||||
|
self.assertEqual(sys.exc_info()[0], ValueError)
|
||||||
|
yield
|
||||||
|
self.assertIsNone(sys.exc_info()[0])
|
||||||
|
yield "done"
|
||||||
|
|
||||||
|
g = gen()
|
||||||
|
next(g)
|
||||||
|
try:
|
||||||
|
raise ValueError
|
||||||
|
except Exception:
|
||||||
|
next(g)
|
||||||
|
|
||||||
|
self.assertEqual(next(g), "done")
|
||||||
|
self.assertEqual(sys.exc_info(), (None, None, None))
|
||||||
|
|
||||||
|
def test_except_throw_exception_context(self):
|
||||||
|
def gen():
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
self.assertEqual(sys.exc_info()[0], None)
|
||||||
|
yield
|
||||||
|
except ValueError:
|
||||||
|
# we are called from "except ValueError:"
|
||||||
|
self.assertEqual(sys.exc_info()[0], ValueError)
|
||||||
|
raise TypeError()
|
||||||
|
except Exception as exc:
|
||||||
|
self.assertEqual(sys.exc_info()[0], TypeError)
|
||||||
|
self.assertEqual(type(exc.__context__), ValueError)
|
||||||
|
# we are still called from "except ValueError:"
|
||||||
|
self.assertEqual(sys.exc_info()[0], ValueError)
|
||||||
|
yield
|
||||||
|
self.assertIsNone(sys.exc_info()[0])
|
||||||
|
yield "done"
|
||||||
|
|
||||||
|
g = gen()
|
||||||
|
next(g)
|
||||||
|
try:
|
||||||
|
raise ValueError
|
||||||
|
except Exception as exc:
|
||||||
|
g.throw(exc)
|
||||||
|
|
||||||
|
self.assertEqual(next(g), "done")
|
||||||
|
self.assertEqual(sys.exc_info(), (None, None, None))
|
||||||
|
|
||||||
|
|
||||||
tutorial_tests = """
|
tutorial_tests = """
|
||||||
Let's try a simple generator:
|
Let's try a simple generator:
|
||||||
|
|
||||||
|
|
|
@ -226,6 +226,12 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #23353: Fix the exception handling of generators in
|
||||||
|
PyEval_EvalFrameEx(). At entry, save or swap the exception state even if
|
||||||
|
PyEval_EvalFrameEx() is called with throwflag=0. At exit, the exception state
|
||||||
|
is now always restored or swapped, not only if why is WHY_YIELD or
|
||||||
|
WHY_RETURN. Patch co-written with Antoine Pitrou.
|
||||||
|
|
||||||
- Issue #14099: Restored support of writing ZIP files to tellable but
|
- Issue #14099: Restored support of writing ZIP files to tellable but
|
||||||
non-seekable streams.
|
non-seekable streams.
|
||||||
|
|
||||||
|
|
|
@ -1189,8 +1189,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
||||||
f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */
|
f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */
|
||||||
f->f_executing = 1;
|
f->f_executing = 1;
|
||||||
|
|
||||||
if (co->co_flags & CO_GENERATOR && !throwflag) {
|
if (co->co_flags & CO_GENERATOR) {
|
||||||
if (f->f_exc_type != NULL && f->f_exc_type != Py_None) {
|
if (!throwflag && f->f_exc_type != NULL && f->f_exc_type != Py_None) {
|
||||||
/* We were in an except handler when we left,
|
/* We were in an except handler when we left,
|
||||||
restore the exception state which was put aside
|
restore the exception state which was put aside
|
||||||
(see YIELD_VALUE). */
|
(see YIELD_VALUE). */
|
||||||
|
@ -3196,7 +3196,8 @@ fast_block_end:
|
||||||
|| (retval == NULL && PyErr_Occurred()));
|
|| (retval == NULL && PyErr_Occurred()));
|
||||||
|
|
||||||
fast_yield:
|
fast_yield:
|
||||||
if (co->co_flags & CO_GENERATOR && (why == WHY_YIELD || why == WHY_RETURN)) {
|
if (co->co_flags & CO_GENERATOR) {
|
||||||
|
|
||||||
/* The purpose of this block is to put aside the generator's exception
|
/* The purpose of this block is to put aside the generator's exception
|
||||||
state and restore that of the calling frame. If the current
|
state and restore that of the calling frame. If the current
|
||||||
exception state is from the caller, we clear the exception values
|
exception state is from the caller, we clear the exception values
|
||||||
|
|
Loading…
Reference in New Issue