Commit ac913413 authored by Benjamin Peterson's avatar Benjamin Peterson

never retain a generator's caller's exception state on the generator after a yield/return

This requires some trickery to properly save the exception state if the
generator creates its own exception state.
parent 9cf960c9
......@@ -581,6 +581,18 @@ class ExceptionTests(unittest.TestCase):
pass
self.assertEqual(sys.exc_info(), (None, None, None))
def test_generator_doesnt_retain_old_exc(self):
def g():
self.assertIsInstance(sys.exc_info()[1], RuntimeError)
yield
self.assertEqual(sys.exc_info(), (None, None, None))
it = g()
try:
raise RuntimeError
except RuntimeError:
next(it)
self.assertRaises(StopIteration, next, it)
def test_generator_finalizing_and_exc_info(self):
# See #7173
def simple_gen():
......
......@@ -10,6 +10,9 @@ What's New in Python 3.2.2?
Core and Builtins
-----------------
- When a generator yields, do not retain the caller's exception state on the
generator.
- Issue #12475: Prevent generators from leaking their exception state into the
caller's frame as they return for the last time.
......
......@@ -1145,6 +1145,23 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
f->f_exc_traceback = tmp; \
}
#define RESTORE_AND_CLEAR_EXC_STATE() \
{ \
PyObject *type, *value, *tb; \
type = tstate->exc_type; \
value = tstate->exc_value; \
tb = tstate->exc_traceback; \
tstate->exc_type = f->f_exc_type; \
tstate->exc_value = f->f_exc_value; \
tstate->exc_traceback = f->f_exc_traceback; \
f->f_exc_type = NULL; \
f->f_exc_value = NULL; \
f->f_exc_traceback = NULL; \
Py_XDECREF(type); \
Py_XDECREF(value); \
Py_XDECREF(tb); \
}
/* Start of code */
if (f == NULL)
......@@ -3017,10 +3034,25 @@ fast_block_end:
retval = NULL;
fast_yield:
if (co->co_flags & CO_GENERATOR && (why == WHY_YIELD || why == WHY_RETURN))
/* Put aside the current exception state and restore that of the
calling frame. */
SWAP_EXC_STATE();
if (co->co_flags & CO_GENERATOR && (why == WHY_YIELD || why == WHY_RETURN)) {
/* The purpose of this block is to put aside the generator's exception
state and restore that of the calling frame. If the current
exception state is from the caller, we clear the exception values
on the generator frame, so they are not swapped back in latter. The
origin of the current exception state is determined by checking for
except handler blocks, which we must be in iff a new exception
state came into existence in this frame. (An uncaught exception
would have why == WHY_EXCEPTION, and we wouldn't be here). */
int i;
for (i = 0; i < f->f_iblock; i++)
if (f->f_blockstack[i].b_type == EXCEPT_HANDLER)
break;
if (i == f->f_iblock)
/* We did not create this exception. */
RESTORE_AND_CLEAR_EXC_STATE()
else
SWAP_EXC_STATE()
}
if (tstate->use_tracing) {
if (tstate->c_tracefunc) {
......
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