[Python-checkins] cpython (3.2): never retain a generator's caller's exception state on the generator after a

benjamin.peterson python-checkins at python.org
Sun Jul 3 23:23:34 CEST 2011


http://hg.python.org/cpython/rev/419871c62bb3
changeset: 71157:419871c62bb3
branch: 3.2
parent: 71155:733cb6f42217
user: Benjamin Peterson <benjamin at python.org>
date: Sun Jul 03 16:25:11 2011 -0500
summary:
 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.
files:
 Lib/test/test_exceptions.py | 12 +++++++
 Misc/NEWS | 3 +
 Python/ceval.c | 40 ++++++++++++++++++++++--
 3 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -581,6 +581,18 @@
 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
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@
 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
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1145,6 +1145,23 @@
 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 @@
 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) {
-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /