diff -r 58bd6a58365d Include/frameobject.h --- a/Include/frameobject.h Wed Feb 08 04:09:37 2012 +0100 +++ b/Include/frameobject.h Mon Feb 13 11:41:33 2012 +0000 @@ -29,15 +29,6 @@ PyObject *f_trace; /* Trace function */ PyObject *f_yieldfrom; /* Iterator being delegated to by yield from */ - /* In a generator, we need to be able to swap between the exception - state inside the generator and the exception state of the calling - frame (which shouldn't be impacted when the generator "yields" - from an except handler). - These three fields exist exactly for that, and are unused for - non-generator frames. See the SAVE_EXC_STATE and SWAP_EXC_STATE - macros in ceval.c for details of their use. */ - PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; - PyThreadState *f_tstate; int f_lasti; /* Last instruction if called */ /* Call PyFrame_GetLineNumber() instead of reading this field diff -r 58bd6a58365d Include/genobject.h --- a/Include/genobject.h Wed Feb 08 04:09:37 2012 +0100 +++ b/Include/genobject.h Mon Feb 13 11:41:33 2012 +0000 @@ -25,6 +25,9 @@ /* List of weak reference. */ PyObject *gi_weakreflist; + + PyExcState gi_exc_state; + } PyGenObject; PyAPI_DATA(PyTypeObject) PyGen_Type; diff -r 58bd6a58365d Include/pystate.h --- a/Include/pystate.h Wed Feb 08 04:09:37 2012 +0100 +++ b/Include/pystate.h Mon Feb 13 11:41:33 2012 +0000 @@ -65,6 +65,20 @@ #ifdef Py_LIMITED_API typedef struct _ts PyThreadState; #else + +typedef struct _excstate { + /* In a generator, we need to be able to swap between the exception + state inside the generator and the exception state of the calling + frame (which shouldn't be impacted when the generator "yields" + from an except handler). + These three fields exist exactly for that. */ + PyObject *exc_type, *exc_value, *exc_traceback; + + struct _excstate *exc_previous; + +} PyExcState; + + typedef struct _ts { /* See Python/ceval.c for comments explaining most fields */ @@ -92,9 +106,7 @@ PyObject *curexc_value; PyObject *curexc_traceback; - PyObject *exc_type; - PyObject *exc_value; - PyObject *exc_traceback; + PyExcState exc_state; PyObject *dict; /* Stores per-thread state */ @@ -114,6 +126,8 @@ long thread_id; /* Thread id where this tstate was created */ /* XXX signal handlers should also be here */ + + PyExcState *exc_info; } PyThreadState; #endif diff -r 58bd6a58365d Lib/test/test_sys.py --- a/Lib/test/test_sys.py Wed Feb 08 04:09:37 2012 +0100 +++ b/Lib/test/test_sys.py Mon Feb 13 11:41:33 2012 +0000 @@ -730,7 +730,7 @@ nfrees = len(x.f_code.co_freevars) extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\ ncells + nfrees - 1 - check(x, size(vh + '13P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) + check(x, size(vh + '10P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) # function def func(): pass check(func, size(h + '12P')) @@ -747,7 +747,7 @@ check(bar, size(h + 'P')) # generator def get_gen(): yield 1 - check(get_gen(), size(h + 'Pi2P')) + check(get_gen(), size(h + 'Pi6P')) # iterator check(iter('abc'), size(h + 'lP')) # callable-iterator diff -r 58bd6a58365d Objects/frameobject.c --- a/Objects/frameobject.c Wed Feb 08 04:09:37 2012 +0100 +++ b/Objects/frameobject.c Mon Feb 13 11:41:33 2012 +0000 @@ -385,8 +385,7 @@ * ob_type, ob_size, f_code, f_valuestack; - * f_locals, f_trace, - f_exc_type, f_exc_value, f_exc_traceback are NULL; + * f_locals, f_trace are NULL; * f_localsplus does not require re-allocation and the local variables in f_localsplus are NULL. @@ -442,9 +441,6 @@ Py_DECREF(f->f_globals); Py_CLEAR(f->f_locals); Py_CLEAR(f->f_trace); - Py_CLEAR(f->f_exc_type); - Py_CLEAR(f->f_exc_value); - Py_CLEAR(f->f_exc_traceback); Py_CLEAR(f->f_yieldfrom); co = f->f_code; @@ -474,9 +470,6 @@ Py_VISIT(f->f_globals); Py_VISIT(f->f_locals); Py_VISIT(f->f_trace); - Py_VISIT(f->f_exc_type); - Py_VISIT(f->f_exc_value); - Py_VISIT(f->f_exc_traceback); Py_VISIT(f->f_yieldfrom); /* locals */ @@ -507,9 +500,6 @@ oldtop = f->f_stacktop; f->f_stacktop = NULL; - Py_CLEAR(f->f_exc_type); - Py_CLEAR(f->f_exc_value); - Py_CLEAR(f->f_exc_traceback); Py_CLEAR(f->f_trace); Py_CLEAR(f->f_yieldfrom); @@ -683,7 +673,6 @@ f->f_localsplus[i] = NULL; f->f_locals = NULL; f->f_trace = NULL; - f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; } f->f_stacktop = f->f_valuestack; f->f_builtins = builtins; diff -r 58bd6a58365d Objects/genobject.c --- a/Objects/genobject.c Wed Feb 08 04:09:37 2012 +0100 +++ b/Objects/genobject.c Mon Feb 13 11:41:33 2012 +0000 @@ -13,6 +13,9 @@ { Py_VISIT((PyObject *)gen->gi_frame); Py_VISIT(gen->gi_code); + Py_VISIT(gen->gi_exc_state.exc_type); + Py_VISIT(gen->gi_exc_state.exc_value); + Py_VISIT(gen->gi_exc_state.exc_traceback); return 0; } @@ -38,6 +41,9 @@ _PyObject_GC_UNTRACK(self); Py_CLEAR(gen->gi_frame); Py_CLEAR(gen->gi_code); + Py_CLEAR(gen->gi_exc_state.exc_type); + Py_CLEAR(gen->gi_exc_state.exc_value); + Py_CLEAR(gen->gi_exc_state.exc_traceback); PyObject_GC_Del(gen); } @@ -82,7 +88,11 @@ f->f_back = tstate->frame; gen->gi_running = 1; + gen->gi_exc_state.exc_previous = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; result = PyEval_EvalFrameEx(f, exc); + tstate->exc_info = gen->gi_exc_state.exc_previous; + gen->gi_exc_state.exc_previous = NULL; gen->gi_running = 0; /* Don't keep the reference to f_back any longer than necessary. It @@ -111,12 +121,12 @@ /* generator can't be rerun, so release the frame */ /* first clean reference cycle through stored exception traceback */ PyObject *t, *v, *tb; - t = f->f_exc_type; - v = f->f_exc_value; - tb = f->f_exc_traceback; - f->f_exc_type = NULL; - f->f_exc_value = NULL; - f->f_exc_traceback = NULL; + t = gen->gi_exc_state.exc_type; + v = gen->gi_exc_state.exc_value; + tb = gen->gi_exc_state.exc_traceback; + gen->gi_exc_state.exc_type = NULL; + gen->gi_exc_state.exc_value = NULL; + gen->gi_exc_state.exc_traceback = NULL; Py_XDECREF(t); Py_XDECREF(v); Py_XDECREF(tb); @@ -594,6 +604,10 @@ gen->gi_code = (PyObject *)(f->f_code); gen->gi_running = 0; gen->gi_weakreflist = NULL; + gen->gi_exc_state.exc_type = NULL; + gen->gi_exc_state.exc_value = NULL; + gen->gi_exc_state.exc_traceback = NULL; + gen->gi_exc_state.exc_previous = NULL; _PyObject_GC_TRACK(gen); return (PyObject *)gen; } diff -r 58bd6a58365d Python/ceval.c --- a/Python/ceval.c Wed Feb 08 04:09:37 2012 +0100 +++ b/Python/ceval.c Mon Feb 13 11:41:33 2012 +0000 @@ -750,9 +750,6 @@ WHY_SILENCED = 0x0080 /* Exception silenced by 'with' */ }; -static void save_exc_state(PyThreadState *, PyFrameObject *); -static void swap_exc_state(PyThreadState *, PyFrameObject *); -static void restore_and_clear_exc_state(PyThreadState *, PyFrameObject *); static enum why_code do_raise(PyObject *, PyObject *); static int unpack_iterable(PyObject *, int, int, PyObject **); @@ -1098,17 +1095,19 @@ #define UNWIND_EXCEPT_HANDLER(b) \ { \ PyObject *type, *value, *traceback; \ + PyExcState *exc_info; \ assert(STACK_LEVEL() >= (b)->b_level + 3); \ while (STACK_LEVEL() > (b)->b_level + 3) { \ value = POP(); \ Py_XDECREF(value); \ } \ - type = tstate->exc_type; \ - value = tstate->exc_value; \ - traceback = tstate->exc_traceback; \ - tstate->exc_type = POP(); \ - tstate->exc_value = POP(); \ - tstate->exc_traceback = POP(); \ + exc_info = tstate->exc_info; \ + type = exc_info->exc_type; \ + value = exc_info->exc_value; \ + traceback = exc_info->exc_traceback; \ + exc_info->exc_type = POP(); \ + exc_info->exc_value = POP(); \ + exc_info->exc_traceback = POP(); \ Py_XDECREF(type); \ Py_XDECREF(value); \ Py_XDECREF(traceback); \ @@ -1184,17 +1183,6 @@ assert(stack_pointer != NULL); f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ - if (co->co_flags & CO_GENERATOR && !throwflag) { - if (f->f_exc_type != NULL && f->f_exc_type != Py_None) { - /* We were in an except handler when we left, - restore the exception state which was put aside - (see YIELD_VALUE). */ - swap_exc_state(tstate, f); - } - else - save_exc_state(tstate, f); - } - #ifdef LLTRACE lltrace = PyDict_GetItemString(f->f_globals, "__lltrace__") != NULL; #endif @@ -2955,12 +2943,13 @@ || b->b_type == SETUP_FINALLY)) { PyObject *exc, *val, *tb; int handler = b->b_handler; + PyExcState *exc_info = tstate->exc_info; /* Beware, this invalidates all b->b_* fields */ PyFrame_BlockSetup(f, EXCEPT_HANDLER, -1, STACK_LEVEL()); - PUSH(tstate->exc_traceback); - PUSH(tstate->exc_value); - if (tstate->exc_type != NULL) { - PUSH(tstate->exc_type); + PUSH(exc_info->exc_traceback); + PUSH(exc_info->exc_value); + if (exc_info->exc_type != NULL) { + PUSH(exc_info->exc_type); } else { Py_INCREF(Py_None); @@ -2975,10 +2964,10 @@ &exc, &val, &tb); PyException_SetTraceback(val, tb); Py_INCREF(exc); - tstate->exc_type = exc; + exc_info->exc_type = exc; Py_INCREF(val); - tstate->exc_value = val; - tstate->exc_traceback = tb; + exc_info->exc_value = val; + exc_info->exc_traceback = tb; if (tb == NULL) tb = Py_None; Py_INCREF(tb); @@ -3018,26 +3007,6 @@ retval = NULL; fast_yield: - 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(tstate, f); - else - swap_exc_state(tstate, f); - } - if (tstate->use_tracing) { if (tstate->c_tracefunc) { if (why == WHY_RETURN || why == WHY_YIELD) { @@ -3453,60 +3422,6 @@ } -/* These 3 functions deal with the exception state of generators. */ - -static void -save_exc_state(PyThreadState *tstate, PyFrameObject *f) -{ - PyObject *type, *value, *traceback; - Py_XINCREF(tstate->exc_type); - Py_XINCREF(tstate->exc_value); - Py_XINCREF(tstate->exc_traceback); - type = f->f_exc_type; - value = f->f_exc_value; - traceback = f->f_exc_traceback; - f->f_exc_type = tstate->exc_type; - f->f_exc_value = tstate->exc_value; - f->f_exc_traceback = tstate->exc_traceback; - Py_XDECREF(type); - Py_XDECREF(value); - Py_XDECREF(traceback); -} - -static void -swap_exc_state(PyThreadState *tstate, PyFrameObject *f) -{ - PyObject *tmp; - tmp = tstate->exc_type; - tstate->exc_type = f->f_exc_type; - f->f_exc_type = tmp; - tmp = tstate->exc_value; - tstate->exc_value = f->f_exc_value; - f->f_exc_value = tmp; - tmp = tstate->exc_traceback; - tstate->exc_traceback = f->f_exc_traceback; - f->f_exc_traceback = tmp; -} - -static void -restore_and_clear_exc_state(PyThreadState *tstate, PyFrameObject *f) -{ - 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); -} - - /* Logic for the raise statement (too complicated for inlining). This *consumes* a reference count to each of its arguments. */ static enum why_code @@ -3517,10 +3432,11 @@ if (exc == NULL) { /* Reraise */ PyThreadState *tstate = PyThreadState_GET(); + PyExcState *exc_info = tstate->exc_info; PyObject *tb; - type = tstate->exc_type; - value = tstate->exc_value; - tb = tstate->exc_traceback; + type = exc_info->exc_type; + value = exc_info->exc_value; + tb = exc_info->exc_traceback; if (type == Py_None) { PyErr_SetString(PyExc_RuntimeError, "No active exception to reraise"); diff -r 58bd6a58365d Python/errors.c --- a/Python/errors.c Wed Feb 08 04:09:37 2012 +0100 +++ b/Python/errors.c Mon Feb 13 11:41:33 2012 +0000 @@ -64,7 +64,7 @@ return; } Py_XINCREF(value); - exc_value = tstate->exc_value; + exc_value = tstate->exc_info->exc_value; if (exc_value != NULL && exc_value != Py_None) { /* Implicit exception chaining */ Py_INCREF(exc_value); diff -r 58bd6a58365d Python/pystate.c --- a/Python/pystate.c Wed Feb 08 04:09:37 2012 +0100 +++ b/Python/pystate.c Mon Feb 13 11:41:33 2012 +0000 @@ -198,9 +198,11 @@ tstate->curexc_value = NULL; tstate->curexc_traceback = NULL; - tstate->exc_type = NULL; - tstate->exc_value = NULL; - tstate->exc_traceback = NULL; + tstate->exc_state.exc_type = NULL; + tstate->exc_state.exc_value = NULL; + tstate->exc_state.exc_traceback = NULL; + tstate->exc_state.exc_previous = NULL; + tstate->exc_info = &tstate->exc_state; tstate->c_profilefunc = NULL; tstate->c_tracefunc = NULL; @@ -290,9 +292,13 @@ Py_CLEAR(tstate->curexc_value); Py_CLEAR(tstate->curexc_traceback); - Py_CLEAR(tstate->exc_type); - Py_CLEAR(tstate->exc_value); - Py_CLEAR(tstate->exc_traceback); + Py_CLEAR(tstate->exc_state.exc_type); + Py_CLEAR(tstate->exc_state.exc_value); + Py_CLEAR(tstate->exc_state.exc_traceback); + assert(tstate->exc_info.exc_previous == NULL); + if (Py_VerboseFlag && tstate->exc_info != &tstate->exc_state) + fprintf(stderr, + "PyThreadState_Clear: warning: thread still has a generator\n"); tstate->c_profilefunc = NULL; tstate->c_tracefunc = NULL; diff -r 58bd6a58365d Python/sysmodule.c --- a/Python/sysmodule.c Wed Feb 08 04:09:37 2012 +0100 +++ b/Python/sysmodule.c Mon Feb 13 11:41:33 2012 +0000 @@ -207,14 +207,16 @@ static PyObject * sys_exc_info(PyObject *self, PyObject *noargs) { - PyThreadState *tstate; - tstate = PyThreadState_GET(); + PyExcState *exc_info = PyThreadState_GET()->exc_info; + while ((exc_info->exc_type == NULL || exc_info->exc_type == Py_None) && + exc_info->exc_previous != NULL) + exc_info = exc_info->exc_previous; return Py_BuildValue( "(OOO)", - tstate->exc_type != NULL ? tstate->exc_type : Py_None, - tstate->exc_value != NULL ? tstate->exc_value : Py_None, - tstate->exc_traceback != NULL ? - tstate->exc_traceback : Py_None); + exc_info->exc_type != NULL ? exc_info->exc_type : Py_None, + exc_info->exc_value != NULL ? exc_info->exc_value : Py_None, + exc_info->exc_traceback != NULL ? + exc_info->exc_traceback : Py_None); } PyDoc_STRVAR(exc_info_doc,