summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Peterson <benjamin@python.org>2011-07-03 21:27:41 (GMT)
committerBenjamin Peterson <benjamin@python.org>2011-07-03 21:27:41 (GMT)
commit536feac7f863c64f32c3bba492b79ddf03323373 (patch)
tree70170db073a0f0de4b91fe91ac26cfa63ef8c682
parentffada78059d914619a997d57804c3fd2696a6e37 (diff)
parentac91341333d27bf39dd8b8c1c3164b5bdc19f03b (diff)
downloadcpython-536feac7f863c64f32c3bba492b79ddf03323373.zip
cpython-536feac7f863c64f32c3bba492b79ddf03323373.tar.gz
cpython-536feac7f863c64f32c3bba492b79ddf03323373.tar.bz2
merge 3.2
-rw-r--r--Lib/test/test_exceptions.py12
-rw-r--r--Misc/NEWS3
-rw-r--r--Python/ceval.c40
3 files changed, 51 insertions, 4 deletions
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index 03685b6..f05d3c0 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -582,6 +582,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():
diff --git a/Misc/NEWS b/Misc/NEWS
index ec79568..3007cdc 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1?
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.
diff --git a/Python/ceval.c b/Python/ceval.c
index 4f7ba80..1dd0d49 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1141,6 +1141,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)
@@ -3001,10 +3018,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) {