summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2017-10-22 21:41:51 (GMT)
committerAntoine Pitrou <pitrou@free.fr>2017-10-22 21:41:51 (GMT)
commitae3087c6382011c47db82fea4d05f8bbf514265d (patch)
treec5d832a760d9898700f1ca397a5a305734b3d77a
parent91dc64ba3f51100540b2ab6c6cd72c3bb18a6d49 (diff)
downloadcpython-ae3087c6382011c47db82fea4d05f8bbf514265d.zip
cpython-ae3087c6382011c47db82fea4d05f8bbf514265d.tar.gz
cpython-ae3087c6382011c47db82fea4d05f8bbf514265d.tar.bz2
Move exc state to generator. Fixes bpo-25612 (#1773)
Move exception state information from frame objects to coroutine (generator/thread) object where it belongs.
-rw-r--r--Include/frameobject.h8
-rw-r--r--Include/genobject.h3
-rw-r--r--Include/pyerrors.h1
-rw-r--r--Include/pystate.h27
-rw-r--r--Lib/test/test_exceptions.py56
-rw-r--r--Lib/test/test_sys.py4
-rw-r--r--Misc/NEWS.d/next/C API/2017-10-22-13-12-28.bpo-25612.1jnWKT.rst3
-rw-r--r--Objects/frameobject.c13
-rw-r--r--Objects/genobject.c46
-rw-r--r--Python/ceval.c125
-rw-r--r--Python/errors.c34
-rw-r--r--Python/pystate.c21
-rw-r--r--Python/sysmodule.c11
13 files changed, 188 insertions, 164 deletions
diff --git a/Include/frameobject.h b/Include/frameobject.h
index dbe0a84..a95baf8 100644
--- a/Include/frameobject.h
+++ b/Include/frameobject.h
@@ -30,14 +30,6 @@ typedef struct _frame {
char f_trace_lines; /* Emit per-line trace events? */
char f_trace_opcodes; /* Emit per-opcode trace events? */
- /* 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
- functions in ceval.c for details of their use. */
- PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
/* Borrowed reference to a generator, or NULL */
PyObject *f_gen;
diff --git a/Include/genobject.h b/Include/genobject.h
index b9db9f9..87fbe17 100644
--- a/Include/genobject.h
+++ b/Include/genobject.h
@@ -25,7 +25,8 @@ struct _frame; /* Avoid including frameobject.h */
/* Name of the generator. */ \
PyObject *prefix##_name; \
/* Qualified name of the generator. */ \
- PyObject *prefix##_qualname;
+ PyObject *prefix##_qualname; \
+ _PyErr_StackItem prefix##_exc_state;
typedef struct {
/* The gi_ prefix is intended to remind of generator-iterator. */
diff --git a/Include/pyerrors.h b/Include/pyerrors.h
index fcaba85..9418201 100644
--- a/Include/pyerrors.h
+++ b/Include/pyerrors.h
@@ -78,6 +78,7 @@ PyAPI_FUNC(void) PyErr_SetNone(PyObject *);
PyAPI_FUNC(void) PyErr_SetObject(PyObject *, PyObject *);
#ifndef Py_LIMITED_API
PyAPI_FUNC(void) _PyErr_SetKeyError(PyObject *);
+_PyErr_StackItem *_PyErr_GetTopmostException(PyThreadState *tstate);
#endif
PyAPI_FUNC(void) PyErr_SetString(
PyObject *exception,
diff --git a/Include/pystate.h b/Include/pystate.h
index 507a598..238008f 100644
--- a/Include/pystate.h
+++ b/Include/pystate.h
@@ -123,6 +123,21 @@ typedef int (*Py_tracefunc)(PyObject *, struct _frame *, int, PyObject *);
#ifdef Py_LIMITED_API
typedef struct _ts PyThreadState;
#else
+
+typedef struct _err_stackitem {
+ /* This struct represents an entry on the exception stack, which is a
+ * per-coroutine state. (Coroutine in the computer science sense,
+ * including the thread and generators).
+ * This ensures that the exception state is not impacted by "yields"
+ * from an except handler.
+ */
+ PyObject *exc_type, *exc_value, *exc_traceback;
+
+ struct _err_stackitem *previous_item;
+
+} _PyErr_StackItem;
+
+
typedef struct _ts {
/* See Python/ceval.c for comments explaining most fields */
@@ -147,13 +162,19 @@ typedef struct _ts {
PyObject *c_profileobj;
PyObject *c_traceobj;
+ /* The exception currently being raised */
PyObject *curexc_type;
PyObject *curexc_value;
PyObject *curexc_traceback;
- PyObject *exc_type;
- PyObject *exc_value;
- PyObject *exc_traceback;
+ /* The exception currently being handled, if no coroutines/generators
+ * are present. Always last element on the stack referred to be exc_info.
+ */
+ _PyErr_StackItem exc_state;
+
+ /* Pointer to the top of the stack of the exceptions currently
+ * being handled */
+ _PyErr_StackItem *exc_info;
PyObject *dict; /* Stores per-thread state */
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index a25f3bf..ad4bc09 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -1097,6 +1097,62 @@ class ExceptionTests(unittest.TestCase):
self.assertIn("test message", report)
self.assertTrue(report.endswith("\n"))
+ def test_yield_in_nested_try_excepts(self):
+ #Issue #25612
+ class MainError(Exception):
+ pass
+
+ class SubError(Exception):
+ pass
+
+ def main():
+ try:
+ raise MainError()
+ except MainError:
+ try:
+ yield
+ except SubError:
+ pass
+ raise
+
+ coro = main()
+ coro.send(None)
+ with self.assertRaises(MainError):
+ coro.throw(SubError())
+
+ def test_generator_doesnt_retain_old_exc2(self):
+ #Issue 28884#msg282532
+ def g():
+ try:
+ raise ValueError
+ except ValueError:
+ yield 1
+ self.assertEqual(sys.exc_info(), (None, None, None))
+ yield 2
+
+ gen = g()
+
+ try:
+ raise IndexError
+ except IndexError:
+ self.assertEqual(next(gen), 1)
+ self.assertEqual(next(gen), 2)
+
+ def test_raise_in_generator(self):
+ #Issue 25612#msg304117
+ def g():
+ yield 1
+ raise
+ yield 2
+
+ with self.assertRaises(ZeroDivisionError):
+ i = g()
+ try:
+ 1/0
+ except:
+ next(i)
+ next(i)
+
class ImportErrorTests(unittest.TestCase):
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index fd45abe..6872629 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -971,7 +971,7 @@ class SizeofTest(unittest.TestCase):
nfrees = len(x.f_code.co_freevars)
extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\
ncells + nfrees - 1
- check(x, vsize('8P2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
+ check(x, vsize('5P2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
# function
def func(): pass
check(func, size('12P'))
@@ -988,7 +988,7 @@ class SizeofTest(unittest.TestCase):
check(bar, size('PP'))
# generator
def get_gen(): yield 1
- check(get_gen(), size('Pb2PPP'))
+ check(get_gen(), size('Pb2PPP4P'))
# iterator
check(iter('abc'), size('lP'))
# callable-iterator
diff --git a/Misc/NEWS.d/next/C API/2017-10-22-13-12-28.bpo-25612.1jnWKT.rst b/Misc/NEWS.d/next/C API/2017-10-22-13-12-28.bpo-25612.1jnWKT.rst
new file mode 100644
index 0000000..102c2e3
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2017-10-22-13-12-28.bpo-25612.1jnWKT.rst
@@ -0,0 +1,3 @@
+Move the current exception state from the frame object to the co-routine.
+This simplifies the interpreter and fixes a couple of obscure bugs caused by
+having swap exception state when entering or exiting a generator.
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 8739596..6ab3a22 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -379,8 +379,7 @@ static PyGetSetDef frame_getsetlist[] = {
* 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.
@@ -438,9 +437,6 @@ frame_dealloc(PyFrameObject *f)
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);
co = f->f_code;
if (co->co_zombieframe == NULL)
@@ -469,9 +465,6 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
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);
/* locals */
slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars);
@@ -502,9 +495,6 @@ frame_tp_clear(PyFrameObject *f)
f->f_stacktop = NULL;
f->f_executing = 0;
- Py_CLEAR(f->f_exc_type);
- Py_CLEAR(f->f_exc_value);
- Py_CLEAR(f->f_exc_traceback);
Py_CLEAR(f->f_trace);
/* locals */
@@ -698,7 +688,6 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
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 --git a/Objects/genobject.c b/Objects/genobject.c
index 5d5798c..7793a54 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -16,6 +16,15 @@ static char *NON_INIT_CORO_MSG = "can't send non-None value to a "
static char *ASYNC_GEN_IGNORED_EXIT_MSG =
"async generator ignored GeneratorExit";
+static inline int
+exc_state_traverse(_PyErr_StackItem *exc_state, visitproc visit, void *arg)
+{
+ Py_VISIT(exc_state->exc_type);
+ Py_VISIT(exc_state->exc_value);
+ Py_VISIT(exc_state->exc_traceback);
+ return 0;
+}
+
static int
gen_traverse(PyGenObject *gen, visitproc visit, void *arg)
{
@@ -23,7 +32,7 @@ gen_traverse(PyGenObject *gen, visitproc visit, void *arg)
Py_VISIT(gen->gi_code);
Py_VISIT(gen->gi_name);
Py_VISIT(gen->gi_qualname);
- return 0;
+ return exc_state_traverse(&gen->gi_exc_state, visit, arg);
}
void
@@ -87,6 +96,21 @@ _PyGen_Finalize(PyObject *self)
PyErr_Restore(error_type, error_value, error_traceback);
}
+static inline void
+exc_state_clear(_PyErr_StackItem *exc_state)
+{
+ PyObject *t, *v, *tb;
+ t = exc_state->exc_type;
+ v = exc_state->exc_value;
+ tb = exc_state->exc_traceback;
+ exc_state->exc_type = NULL;
+ exc_state->exc_value = NULL;
+ exc_state->exc_traceback = NULL;
+ Py_XDECREF(t);
+ Py_XDECREF(v);
+ Py_XDECREF(tb);
+}
+
static void
gen_dealloc(PyGenObject *gen)
{
@@ -116,6 +140,7 @@ gen_dealloc(PyGenObject *gen)
Py_CLEAR(gen->gi_code);
Py_CLEAR(gen->gi_name);
Py_CLEAR(gen->gi_qualname);
+ exc_state_clear(&gen->gi_exc_state);
PyObject_GC_Del(gen);
}
@@ -187,7 +212,11 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
f->f_back = tstate->frame;
gen->gi_running = 1;
+ gen->gi_exc_state.previous_item = tstate->exc_info;
+ tstate->exc_info = &gen->gi_exc_state;
result = PyEval_EvalFrameEx(f, exc);
+ tstate->exc_info = gen->gi_exc_state.previous_item;
+ gen->gi_exc_state.previous_item = NULL;
gen->gi_running = 0;
/* Don't keep the reference to f_back any longer than necessary. It
@@ -281,16 +310,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
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;
- Py_XDECREF(t);
- Py_XDECREF(v);
- Py_XDECREF(tb);
+ exc_state_clear(&gen->gi_exc_state);
gen->gi_frame->f_gen = NULL;
gen->gi_frame = NULL;
Py_DECREF(f);
@@ -810,6 +830,10 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
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.previous_item = NULL;
if (name != NULL)
gen->gi_name = name;
else
diff --git a/Python/ceval.c b/Python/ceval.c
index 0f7a40c..f9a798c 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -511,9 +511,6 @@ enum why_code {
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 int do_raise(PyObject *, PyObject *);
static int unpack_iterable(PyObject *, int, int, PyObject **);
@@ -813,17 +810,19 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
#define UNWIND_EXCEPT_HANDLER(b) \
do { \
PyObject *type, *value, *traceback; \
+ _PyErr_StackItem *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); \
@@ -910,16 +909,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */
f->f_executing = 1;
- if (co->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
- if (!throwflag && 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_GetItemId(f->f_globals, &PyId___ltrace__) != NULL;
@@ -3447,12 +3436,13 @@ fast_block_end:
|| b->b_type == SETUP_FINALLY)) {
PyObject *exc, *val, *tb;
int handler = b->b_handler;
+ _PyErr_StackItem *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);
@@ -3470,10 +3460,10 @@ fast_block_end:
else
PyException_SetTraceback(val, Py_None);
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);
@@ -3516,28 +3506,6 @@ fast_block_end:
assert((retval != NULL) ^ (PyErr_Occurred() != NULL));
fast_yield:
- if (co->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
-
- /* 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) {
@@ -4057,60 +4025,6 @@ special_lookup(PyObject *o, _Py_Identifier *id)
}
-/* 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 int
@@ -4121,10 +4035,11 @@ do_raise(PyObject *exc, PyObject *cause)
if (exc == NULL) {
/* Reraise */
PyThreadState *tstate = PyThreadState_GET();
+ _PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate);
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 || type == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"No active exception to reraise");
diff --git a/Python/errors.c b/Python/errors.c
index 5709fdd..51fc791 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -53,6 +53,18 @@ PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback)
Py_XDECREF(oldtraceback);
}
+_PyErr_StackItem *
+_PyErr_GetTopmostException(PyThreadState *tstate)
+{
+ _PyErr_StackItem *exc_info = tstate->exc_info;
+ while ((exc_info->exc_type == NULL || exc_info->exc_type == Py_None) &&
+ exc_info->previous_item != NULL)
+ {
+ exc_info = exc_info->previous_item;
+ }
+ return exc_info;
+}
+
static PyObject*
_PyErr_CreateException(PyObject *exception, PyObject *value)
{
@@ -83,7 +95,7 @@ PyErr_SetObject(PyObject *exception, PyObject *value)
}
Py_XINCREF(value);
- exc_value = tstate->exc_value;
+ exc_value = _PyErr_GetTopmostException(tstate)->exc_value;
if (exc_value != NULL && exc_value != Py_None) {
/* Implicit exception chaining */
Py_INCREF(exc_value);
@@ -335,9 +347,11 @@ PyErr_GetExcInfo(PyObject **p_type, PyObject **p_value, PyObject **p_traceback)
{
PyThreadState *tstate = PyThreadState_GET();
- *p_type = tstate->exc_type;
- *p_value = tstate->exc_value;
- *p_traceback = tstate->exc_traceback;
+ _PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate);
+ *p_type = exc_info->exc_type;
+ *p_value = exc_info->exc_value;
+ *p_traceback = exc_info->exc_traceback;
+
Py_XINCREF(*p_type);
Py_XINCREF(*p_value);
@@ -350,13 +364,13 @@ PyErr_SetExcInfo(PyObject *p_type, PyObject *p_value, PyObject *p_traceback)
PyObject *oldtype, *oldvalue, *oldtraceback;
PyThreadState *tstate = PyThreadState_GET();
- oldtype = tstate->exc_type;
- oldvalue = tstate->exc_value;
- oldtraceback = tstate->exc_traceback;
+ oldtype = tstate->exc_info->exc_type;
+ oldvalue = tstate->exc_info->exc_value;
+ oldtraceback = tstate->exc_info->exc_traceback;
- tstate->exc_type = p_type;
- tstate->exc_value = p_value;
- tstate->exc_traceback = p_traceback;
+ tstate->exc_info->exc_type = p_type;
+ tstate->exc_info->exc_value = p_value;
+ tstate->exc_info->exc_traceback = p_traceback;
Py_XDECREF(oldtype);
Py_XDECREF(oldvalue);
diff --git a/Python/pystate.c b/Python/pystate.c
index 3feae34..d85d604 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -257,9 +257,11 @@ new_threadstate(PyInterpreterState *interp, int init)
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.previous_item = NULL;
+ tstate->exc_info = &tstate->exc_state;
tstate->c_profilefunc = NULL;
tstate->c_tracefunc = NULL;
@@ -444,9 +446,16 @@ PyThreadState_Clear(PyThreadState *tstate)
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);
+
+ /* The stack of exception states should contain just this thread. */
+ assert(tstate->exc_info->previous_item == 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 --git a/Python/sysmodule.c b/Python/sysmodule.c
index e38a200..6dc8e08 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -311,14 +311,13 @@ PyDoc_STRVAR(excepthook_doc,
static PyObject *
sys_exc_info(PyObject *self, PyObject *noargs)
{
- PyThreadState *tstate;
- tstate = PyThreadState_GET();
+ _PyErr_StackItem *err_info = _PyErr_GetTopmostException(PyThreadState_GET());
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);
+ err_info->exc_type != NULL ? err_info->exc_type : Py_None,
+ err_info->exc_value != NULL ? err_info->exc_value : Py_None,
+ err_info->exc_traceback != NULL ?
+ err_info->exc_traceback : Py_None);
}
PyDoc_STRVAR(exc_info_doc,