diff -r 279b6d778663 Include/frameobject.h --- a/Include/frameobject.h Sat Jan 28 11:37:42 2012 +0100 +++ b/Include/frameobject.h Sat Jan 28 16:15:48 2012 +0000 @@ -27,16 +27,7 @@ to the current stack top. */ PyObject **f_stacktop; 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 */ diff -r 279b6d778663 Include/genobject.h --- a/Include/genobject.h Sat Jan 28 11:37:42 2012 +0100 +++ b/Include/genobject.h Sat Jan 28 16:15:48 2012 +0000 @@ -14,17 +14,17 @@ PyObject_HEAD /* The gi_ prefix is intended to remind of generator-iterator. */ - /* Note: gi_frame can be NULL if the generator is "finished" */ - struct _frame *gi_frame; - /* True if generator is being executed. */ int gi_running; - + /* The code object backing the generator */ PyObject *gi_code; /* List of weak reference. */ PyObject *gi_weakreflist; + + PyCoState gi_costate; + } PyGenObject; PyAPI_DATA(PyTypeObject) PyGen_Type; diff -r 279b6d778663 Include/pystate.h --- a/Include/pystate.h Sat Jan 28 11:37:42 2012 +0100 +++ b/Include/pystate.h Sat Jan 28 16:15:48 2012 +0000 @@ -65,12 +65,30 @@ #ifdef Py_LIMITED_API typedef struct _ts PyThreadState; #else + +typedef struct _cos { + /* 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 *exc_type, *exc_value, *exc_traceback; + + PyObject *yieldfrom; /* Iterator being delegated to by yield from */ + /* Top-most frame from which values are yielded + * Can be NULL (For generator if finished, for thread always). + */ + struct _frame *top_frame; + struct _cos *outer; +} PyCoState; + typedef struct _ts { /* See Python/ceval.c for comments explaining most fields */ struct _ts *next; PyInterpreterState *interp; - struct _frame *frame; int recursion_depth; char overflowed; /* The stack has overflowed. Allow 50 more calls @@ -92,10 +110,6 @@ PyObject *curexc_value; PyObject *curexc_traceback; - PyObject *exc_type; - PyObject *exc_value; - PyObject *exc_traceback; - PyObject *dict; /* Stores per-thread state */ /* XXX doesn't mean anything anymore (the comment below is obsolete) @@ -114,6 +128,9 @@ long thread_id; /* Thread id where this tstate was created */ /* XXX signal handlers should also be here */ + + PyCoState entry_state; + PyCoState *current_costate; } PyThreadState; #endif diff -r 279b6d778663 Lib/test/test_sys.py --- a/Lib/test/test_sys.py Sat Jan 28 11:37:42 2012 +0100 +++ b/Lib/test/test_sys.py Sat Jan 28 16:15:48 2012 +0000 @@ -727,7 +727,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 + '9P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) # function def func(): pass check(func, size(h + '12P')) @@ -744,7 +744,7 @@ check(bar, size(h + 'P')) # generator def get_gen(): yield 1 - check(get_gen(), size(h + 'Pi2P')) + check(get_gen(), size(h + 'i8P')) # iterator check(iter('abc'), size(h + 'lP')) # callable-iterator diff -r 279b6d778663 Objects/frameobject.c --- a/Objects/frameobject.c Sat Jan 28 11:37:42 2012 +0100 +++ b/Objects/frameobject.c Sat Jan 28 16:15:48 2012 +0000 @@ -20,7 +20,6 @@ {"f_builtins", T_OBJECT, OFF(f_builtins), READONLY}, {"f_globals", T_OBJECT, OFF(f_globals), READONLY}, {"f_lasti", T_INT, OFF(f_lasti), READONLY}, - {"f_yieldfrom", T_OBJECT, OFF(f_yieldfrom), READONLY}, {NULL} /* Sentinel */ }; @@ -385,8 +384,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,10 +440,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; if (co->co_zombieframe == NULL) @@ -474,10 +468,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 */ slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars); @@ -507,11 +497,7 @@ 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); /* locals */ slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars); @@ -683,7 +669,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; @@ -715,7 +700,6 @@ f->f_lasti = -1; f->f_lineno = code->co_firstlineno; f->f_iblock = 0; - f->f_yieldfrom = NULL; _PyObject_GC_TRACK(f); return f; diff -r 279b6d778663 Objects/genobject.c --- a/Objects/genobject.c Sat Jan 28 11:37:42 2012 +0100 +++ b/Objects/genobject.c Sat Jan 28 16:15:48 2012 +0000 @@ -11,8 +11,12 @@ static int gen_traverse(PyGenObject *gen, visitproc visit, void *arg) { - Py_VISIT((PyObject *)gen->gi_frame); + Py_VISIT((PyObject *)gen->gi_costate.top_frame); Py_VISIT(gen->gi_code); + Py_VISIT(gen->gi_costate.exc_type); + Py_VISIT(gen->gi_costate.exc_value); + Py_VISIT(gen->gi_costate.exc_traceback); + Py_VISIT(gen->gi_costate.yieldfrom); return 0; } @@ -28,7 +32,8 @@ _PyObject_GC_TRACK(self); - if (gen->gi_frame != NULL && gen->gi_frame->f_stacktop != NULL) { + if (gen->gi_costate.top_frame != NULL && + gen->gi_costate.top_frame->f_stacktop != NULL) { /* Generator is paused, so we need to close */ Py_TYPE(gen)->tp_del(self); if (self->ob_refcnt > 0) @@ -36,7 +41,11 @@ } _PyObject_GC_UNTRACK(self); - Py_CLEAR(gen->gi_frame); + Py_CLEAR(gen->gi_costate.exc_type); + Py_CLEAR(gen->gi_costate.exc_value); + Py_CLEAR(gen->gi_costate.exc_traceback); + Py_CLEAR(gen->gi_costate.yieldfrom); + Py_CLEAR(gen->gi_costate.top_frame); Py_CLEAR(gen->gi_code); PyObject_GC_Del(gen); } @@ -46,7 +55,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc) { PyThreadState *tstate = PyThreadState_GET(); - PyFrameObject *f = gen->gi_frame; + PyFrameObject *f = gen->gi_costate.top_frame; PyObject *result; if (gen->gi_running) { @@ -82,6 +91,8 @@ f->f_back = tstate->frame; gen->gi_running = 1; + gen->gi_costate.outer = tstate->current_costate; + tstate->current_costate = &gen->gi_costate; result = PyEval_EvalFrameEx(f, exc); gen->gi_running = 0; @@ -90,6 +101,8 @@ * cycle. */ assert(f->f_back == tstate->frame); Py_CLEAR(f->f_back); + assert(gen->gi_costate.outer == tstate->current_costate); + gen->gi_costate.outer = NULL; /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ @@ -110,17 +123,20 @@ if (!result || f->f_stacktop == NULL) { /* 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; + PyObject *t, *v, *tb, *yf; + t = gen->gi_costate.exc_type; + v = gen->gi_costate.exc_value; + tb = gen->gi_costate.exc_traceback; + yf = gen->gi_costate.yieldfrom; + gen->gi_costate.exc_type = NULL; + gen->gi_costate.exc_value = NULL; + gen->gi_costate.exc_traceback = NULL; + gen->gi_costate.yieldfrom = NULL; Py_XDECREF(t); Py_XDECREF(v); Py_XDECREF(tb); - gen->gi_frame = NULL; + Py_XDECREF(yf); + gen->gi_costate.top_frame = NULL; Py_DECREF(f); } @@ -136,7 +152,7 @@ { int exc = 0; PyObject *ret; - PyObject *yf = gen->gi_frame ? gen->gi_frame->f_yieldfrom : NULL; + PyObject *yf = gen->gi_costate.yieldfrom; /* XXX (ncoghlan): Are the incref/decref on arg and yf strictly needed? * Or would it be valid to rely on borrowed references? */ @@ -180,7 +196,7 @@ gen_close_iter(PyObject *yf) { PyObject *retval = NULL; - + if (PyGen_CheckExact(yf)) { retval = gen_close((PyGenObject *)yf, NULL); if (retval == NULL) { @@ -202,13 +218,13 @@ } Py_XDECREF(retval); return 0; -} +} static PyObject * gen_close(PyGenObject *gen, PyObject *args) { PyObject *retval; - PyObject *yf = gen->gi_frame ? gen->gi_frame->f_yieldfrom : NULL; + PyObject *yf = gen->gi_costate.yieldfrom; int err = 0; if (yf) { @@ -243,7 +259,8 @@ PyObject *error_type, *error_value, *error_traceback; PyGenObject *gen = (PyGenObject *)self; - if (gen->gi_frame == NULL || gen->gi_frame->f_stacktop == NULL) + if (gen->gi_costate.top_frame == NULL || + gen->gi_costate.top_frame->f_stacktop == NULL) /* Generator isn't paused, so no need to close */ return; @@ -309,7 +326,7 @@ PyObject *typ; PyObject *tb = NULL; PyObject *val = NULL; - PyObject *yf = gen->gi_frame ? gen->gi_frame->f_yieldfrom : NULL; + PyObject *yf = gen->gi_costate.yieldfrom; if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb)) return NULL; @@ -422,7 +439,7 @@ PyObject *val = NULL; PyObject *ret; int exc = 0; - PyObject *yf = gen->gi_frame ? gen->gi_frame->f_yieldfrom : NULL; + PyObject *yf = gen->gi_costate.yieldfrom; if (yf) { Py_INCREF(yf); /* ceval.c ensures that yf is an iterator */ @@ -441,18 +458,10 @@ return ret; } -/* - * In certain recursive situations, a generator may lose its frame - * before we get a chance to clear f_yieldfrom, so we use this - * helper function. - */ - static void gen_undelegate(PyGenObject *gen) { - if (gen->gi_frame) { - Py_XDECREF(gen->gi_frame->f_yieldfrom); - gen->gi_frame->f_yieldfrom = NULL; - } + Py_XDECREF(gen->gi_costate.yieldfrom); + gen->gi_costate.yieldfrom = NULL; } /* @@ -468,7 +477,7 @@ PyGen_FetchStopIterationValue(PyObject **pvalue) { PyObject *et, *ev, *tb; PyObject *value = NULL; - + if (PyErr_ExceptionMatches(PyExc_StopIteration)) { PyErr_Fetch(&et, &ev, &tb); Py_XDECREF(et); @@ -517,7 +526,7 @@ static PyMemberDef gen_memberlist[] = { - {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY}, + {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_costate.top_frame), READONLY}, {"gi_running", T_INT, offsetof(PyGenObject, gi_running), READONLY}, {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY}, {NULL} /* Sentinel */ @@ -589,11 +598,16 @@ Py_DECREF(f); return NULL; } - gen->gi_frame = f; + gen->gi_costate.top_frame = f; Py_INCREF(f->f_code); gen->gi_code = (PyObject *)(f->f_code); gen->gi_running = 0; gen->gi_weakreflist = NULL; + gen->gi_costate.exc_type = NULL; + gen->gi_costate.exc_value = NULL; + gen->gi_costate.exc_traceback = NULL; + gen->gi_costate.yieldfrom = NULL; + gen->gi_costate.outer = NULL; _PyObject_GC_TRACK(gen); return (PyObject *)gen; } @@ -602,7 +616,7 @@ PyGen_NeedsFinalizing(PyGenObject *gen) { int i; - PyFrameObject *f = gen->gi_frame; + PyFrameObject *f = gen->gi_costate.top_frame; if (f == NULL || f->f_stacktop == NULL) return 0; /* no frame or empty blockstack == no finalization */ diff -r 279b6d778663 Python/ceval.c --- a/Python/ceval.c Sat Jan 28 11:37:42 2012 +0100 +++ b/Python/ceval.c Sat Jan 28 16:15:48 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; \ + PyCoState *co; \ 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(); \ + co = tstate->current_costate; \ + type = co->exc_type; \ + value = co->exc_value; \ + traceback = co->exc_traceback; \ + co->exc_type = POP(); \ + co->exc_value = POP(); \ + co->exc_traceback = POP(); \ Py_XDECREF(type); \ Py_XDECREF(value); \ Py_XDECREF(traceback); \ @@ -1184,16 +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; @@ -1870,7 +1859,7 @@ continue; } /* x is iterator, retval is value to be yielded */ - f->f_yieldfrom = x; + tstate->current_costate->yieldfrom = x; f->f_stacktop = stack_pointer; why = WHY_YIELD; goto fast_yield; @@ -2957,10 +2946,10 @@ int handler = b->b_handler; /* 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(tstate->current_costate->exc_traceback); + PUSH(tstate->current_costate->exc_value); + if (tstate->current_costate->exc_type != NULL) { + PUSH(tstate->current_costate->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; + tstate->current_costate->exc_type = exc; Py_INCREF(val); - tstate->exc_value = val; - tstate->exc_traceback = tb; + tstate->current_costate->exc_value = val; + tstate->current_costate->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) { @@ -3074,7 +3043,8 @@ exit_eval_frame: Py_LeaveRecursiveCall(); tstate->frame = f->f_back; - + if (tstate->current_costate->top_frame == f) + tstate->current_costate = tstate->current_costate->outer; return retval; } @@ -3452,61 +3422,6 @@ return res; } - -/* 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 @@ -3516,11 +3431,11 @@ if (exc == NULL) { /* Reraise */ - PyThreadState *tstate = PyThreadState_GET(); + PyCoState *costate = PyThreadState_GET()->current_costate; PyObject *tb; - type = tstate->exc_type; - value = tstate->exc_value; - tb = tstate->exc_traceback; + type = costate->exc_type; + value = costate->exc_value; + tb = costate->exc_traceback; if (type == Py_None) { PyErr_SetString(PyExc_RuntimeError, "No active exception to reraise"); diff -r 279b6d778663 Python/errors.c --- a/Python/errors.c Sat Jan 28 11:37:42 2012 +0100 +++ b/Python/errors.c Sat Jan 28 16:15:48 2012 +0000 @@ -52,7 +52,7 @@ void PyErr_SetObject(PyObject *exception, PyObject *value) { - PyThreadState *tstate = PyThreadState_GET(); + PyCoState *costate = PyThreadState_GET()->current_costate; PyObject *exc_value; PyObject *tb = NULL; @@ -64,7 +64,7 @@ return; } Py_XINCREF(value); - exc_value = tstate->exc_value; + exc_value = costate->exc_value; if (exc_value != NULL && exc_value != Py_None) { /* Implicit exception chaining */ Py_INCREF(exc_value); diff -r 279b6d778663 Python/pystate.c --- a/Python/pystate.c Sat Jan 28 11:37:42 2012 +0100 +++ b/Python/pystate.c Sat Jan 28 16:15:49 2012 +0000 @@ -198,14 +198,18 @@ tstate->curexc_value = NULL; tstate->curexc_traceback = NULL; - tstate->exc_type = NULL; - tstate->exc_value = NULL; - tstate->exc_traceback = NULL; - tstate->c_profilefunc = NULL; tstate->c_tracefunc = NULL; tstate->c_profileobj = NULL; tstate->c_traceobj = NULL; + + tstate->entry_state.exc_type = NULL; + tstate->entry_state.exc_value = NULL; + tstate->entry_state.exc_traceback = NULL; + tstate->entry_state.yieldfrom = NULL; + tstate->entry_state.top_frame = NULL; + tstate->entry_state.outer = NULL; + tstate->current_costate = &tstate->entry_state; if (init) _PyThreadState_Init(tstate); @@ -290,14 +294,22 @@ 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); - tstate->c_profilefunc = NULL; tstate->c_tracefunc = NULL; Py_CLEAR(tstate->c_profileobj); Py_CLEAR(tstate->c_traceobj); + + + Py_CLEAR(tstate->entry_state.exc_type); + Py_CLEAR(tstate->entry_state.exc_value); + Py_CLEAR(tstate->entry_state.exc_traceback); + assert(tstate->entry_state.top_frame == NULL); + assert(tstate->entry_state.yieldfrom == NULL); + if (Py_VerboseFlag && tstate->entry_state.outer != &tstate->entry_state) + fprintf(stderr, + "PyThreadState_Clear: warning: thread still has a generator\n"); + tstate->current_costate = &tstate->entry_state; + } diff -r 279b6d778663 Python/sysmodule.c --- a/Python/sysmodule.c Sat Jan 28 11:37:42 2012 +0100 +++ b/Python/sysmodule.c Sat Jan 28 16:15:49 2012 +0000 @@ -207,14 +207,15 @@ static PyObject * sys_exc_info(PyObject *self, PyObject *noargs) { - PyThreadState *tstate; - tstate = PyThreadState_GET(); + PyCoState *costate = PyThreadState_GET()->current_costate; + while ((costate->exc_type == NULL || costate->exc_type == Py_None) && + costate->outer != NULL) + costate = costate->outer; 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); + costate->exc_type != NULL ? costate->exc_type : Py_None, + costate->exc_value != NULL ? costate->exc_value : Py_None, + costate->exc_traceback != NULL ? costate->exc_traceback : Py_None); } PyDoc_STRVAR(exc_info_doc,