mirror of
https://github.com/python/cpython.git
synced 2025-10-07 15:42:02 +00:00
Move exc state to generator. Fixes bpo-25612 (#1773)
Move exception state information from frame objects to coroutine (generator/thread) object where it belongs.
This commit is contained in:
parent
91dc64ba3f
commit
ae3087c638
13 changed files with 188 additions and 164 deletions
|
@ -30,14 +30,6 @@ typedef struct _frame {
|
||||||
char f_trace_lines; /* Emit per-line trace events? */
|
char f_trace_lines; /* Emit per-line trace events? */
|
||||||
char f_trace_opcodes; /* Emit per-opcode 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 */
|
/* Borrowed reference to a generator, or NULL */
|
||||||
PyObject *f_gen;
|
PyObject *f_gen;
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,8 @@ struct _frame; /* Avoid including frameobject.h */
|
||||||
/* Name of the generator. */ \
|
/* Name of the generator. */ \
|
||||||
PyObject *prefix##_name; \
|
PyObject *prefix##_name; \
|
||||||
/* Qualified name of the generator. */ \
|
/* Qualified name of the generator. */ \
|
||||||
PyObject *prefix##_qualname;
|
PyObject *prefix##_qualname; \
|
||||||
|
_PyErr_StackItem prefix##_exc_state;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* The gi_ prefix is intended to remind of generator-iterator. */
|
/* The gi_ prefix is intended to remind of generator-iterator. */
|
||||||
|
|
|
@ -78,6 +78,7 @@ PyAPI_FUNC(void) PyErr_SetNone(PyObject *);
|
||||||
PyAPI_FUNC(void) PyErr_SetObject(PyObject *, PyObject *);
|
PyAPI_FUNC(void) PyErr_SetObject(PyObject *, PyObject *);
|
||||||
#ifndef Py_LIMITED_API
|
#ifndef Py_LIMITED_API
|
||||||
PyAPI_FUNC(void) _PyErr_SetKeyError(PyObject *);
|
PyAPI_FUNC(void) _PyErr_SetKeyError(PyObject *);
|
||||||
|
_PyErr_StackItem *_PyErr_GetTopmostException(PyThreadState *tstate);
|
||||||
#endif
|
#endif
|
||||||
PyAPI_FUNC(void) PyErr_SetString(
|
PyAPI_FUNC(void) PyErr_SetString(
|
||||||
PyObject *exception,
|
PyObject *exception,
|
||||||
|
|
|
@ -123,6 +123,21 @@ typedef int (*Py_tracefunc)(PyObject *, struct _frame *, int, PyObject *);
|
||||||
#ifdef Py_LIMITED_API
|
#ifdef Py_LIMITED_API
|
||||||
typedef struct _ts PyThreadState;
|
typedef struct _ts PyThreadState;
|
||||||
#else
|
#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 {
|
typedef struct _ts {
|
||||||
/* See Python/ceval.c for comments explaining most fields */
|
/* See Python/ceval.c for comments explaining most fields */
|
||||||
|
|
||||||
|
@ -147,13 +162,19 @@ typedef struct _ts {
|
||||||
PyObject *c_profileobj;
|
PyObject *c_profileobj;
|
||||||
PyObject *c_traceobj;
|
PyObject *c_traceobj;
|
||||||
|
|
||||||
|
/* The exception currently being raised */
|
||||||
PyObject *curexc_type;
|
PyObject *curexc_type;
|
||||||
PyObject *curexc_value;
|
PyObject *curexc_value;
|
||||||
PyObject *curexc_traceback;
|
PyObject *curexc_traceback;
|
||||||
|
|
||||||
PyObject *exc_type;
|
/* The exception currently being handled, if no coroutines/generators
|
||||||
PyObject *exc_value;
|
* are present. Always last element on the stack referred to be exc_info.
|
||||||
PyObject *exc_traceback;
|
*/
|
||||||
|
_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 */
|
PyObject *dict; /* Stores per-thread state */
|
||||||
|
|
||||||
|
|
|
@ -1097,6 +1097,62 @@ class ExceptionTests(unittest.TestCase):
|
||||||
self.assertIn("test message", report)
|
self.assertIn("test message", report)
|
||||||
self.assertTrue(report.endswith("\n"))
|
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):
|
class ImportErrorTests(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -971,7 +971,7 @@ class SizeofTest(unittest.TestCase):
|
||||||
nfrees = len(x.f_code.co_freevars)
|
nfrees = len(x.f_code.co_freevars)
|
||||||
extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\
|
extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\
|
||||||
ncells + nfrees - 1
|
ncells + nfrees - 1
|
||||||
check(x, vsize('8P2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
|
check(x, vsize('5P2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
|
||||||
# function
|
# function
|
||||||
def func(): pass
|
def func(): pass
|
||||||
check(func, size('12P'))
|
check(func, size('12P'))
|
||||||
|
@ -988,7 +988,7 @@ class SizeofTest(unittest.TestCase):
|
||||||
check(bar, size('PP'))
|
check(bar, size('PP'))
|
||||||
# generator
|
# generator
|
||||||
def get_gen(): yield 1
|
def get_gen(): yield 1
|
||||||
check(get_gen(), size('Pb2PPP'))
|
check(get_gen(), size('Pb2PPP4P'))
|
||||||
# iterator
|
# iterator
|
||||||
check(iter('abc'), size('lP'))
|
check(iter('abc'), size('lP'))
|
||||||
# callable-iterator
|
# callable-iterator
|
||||||
|
|
|
@ -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.
|
|
@ -379,8 +379,7 @@ static PyGetSetDef frame_getsetlist[] = {
|
||||||
|
|
||||||
* ob_type, ob_size, f_code, f_valuestack;
|
* ob_type, ob_size, f_code, f_valuestack;
|
||||||
|
|
||||||
* f_locals, f_trace,
|
* f_locals, f_trace are NULL;
|
||||||
f_exc_type, f_exc_value, f_exc_traceback are NULL;
|
|
||||||
|
|
||||||
* f_localsplus does not require re-allocation and
|
* f_localsplus does not require re-allocation and
|
||||||
the local variables in f_localsplus are NULL.
|
the local variables in f_localsplus are NULL.
|
||||||
|
@ -438,9 +437,6 @@ frame_dealloc(PyFrameObject *f)
|
||||||
Py_DECREF(f->f_globals);
|
Py_DECREF(f->f_globals);
|
||||||
Py_CLEAR(f->f_locals);
|
Py_CLEAR(f->f_locals);
|
||||||
Py_CLEAR(f->f_trace);
|
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;
|
co = f->f_code;
|
||||||
if (co->co_zombieframe == NULL)
|
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_globals);
|
||||||
Py_VISIT(f->f_locals);
|
Py_VISIT(f->f_locals);
|
||||||
Py_VISIT(f->f_trace);
|
Py_VISIT(f->f_trace);
|
||||||
Py_VISIT(f->f_exc_type);
|
|
||||||
Py_VISIT(f->f_exc_value);
|
|
||||||
Py_VISIT(f->f_exc_traceback);
|
|
||||||
|
|
||||||
/* locals */
|
/* locals */
|
||||||
slots = f->f_code->co_nlocals + PyTuple_GET_SIZE(f->f_code->co_cellvars) + PyTuple_GET_SIZE(f->f_code->co_freevars);
|
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_stacktop = NULL;
|
||||||
f->f_executing = 0;
|
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);
|
Py_CLEAR(f->f_trace);
|
||||||
|
|
||||||
/* locals */
|
/* locals */
|
||||||
|
@ -698,7 +688,6 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
|
||||||
f->f_localsplus[i] = NULL;
|
f->f_localsplus[i] = NULL;
|
||||||
f->f_locals = NULL;
|
f->f_locals = NULL;
|
||||||
f->f_trace = 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_stacktop = f->f_valuestack;
|
||||||
f->f_builtins = builtins;
|
f->f_builtins = builtins;
|
||||||
|
|
|
@ -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 =
|
static char *ASYNC_GEN_IGNORED_EXIT_MSG =
|
||||||
"async generator ignored GeneratorExit";
|
"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
|
static int
|
||||||
gen_traverse(PyGenObject *gen, visitproc visit, void *arg)
|
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_code);
|
||||||
Py_VISIT(gen->gi_name);
|
Py_VISIT(gen->gi_name);
|
||||||
Py_VISIT(gen->gi_qualname);
|
Py_VISIT(gen->gi_qualname);
|
||||||
return 0;
|
return exc_state_traverse(&gen->gi_exc_state, visit, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -87,6 +96,21 @@ _PyGen_Finalize(PyObject *self)
|
||||||
PyErr_Restore(error_type, error_value, error_traceback);
|
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
|
static void
|
||||||
gen_dealloc(PyGenObject *gen)
|
gen_dealloc(PyGenObject *gen)
|
||||||
{
|
{
|
||||||
|
@ -116,6 +140,7 @@ gen_dealloc(PyGenObject *gen)
|
||||||
Py_CLEAR(gen->gi_code);
|
Py_CLEAR(gen->gi_code);
|
||||||
Py_CLEAR(gen->gi_name);
|
Py_CLEAR(gen->gi_name);
|
||||||
Py_CLEAR(gen->gi_qualname);
|
Py_CLEAR(gen->gi_qualname);
|
||||||
|
exc_state_clear(&gen->gi_exc_state);
|
||||||
PyObject_GC_Del(gen);
|
PyObject_GC_Del(gen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +212,11 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
|
||||||
f->f_back = tstate->frame;
|
f->f_back = tstate->frame;
|
||||||
|
|
||||||
gen->gi_running = 1;
|
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);
|
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;
|
gen->gi_running = 0;
|
||||||
|
|
||||||
/* Don't keep the reference to f_back any longer than necessary. It
|
/* 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) {
|
if (!result || f->f_stacktop == NULL) {
|
||||||
/* generator can't be rerun, so release the frame */
|
/* generator can't be rerun, so release the frame */
|
||||||
/* first clean reference cycle through stored exception traceback */
|
/* first clean reference cycle through stored exception traceback */
|
||||||
PyObject *t, *v, *tb;
|
exc_state_clear(&gen->gi_exc_state);
|
||||||
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);
|
|
||||||
gen->gi_frame->f_gen = NULL;
|
gen->gi_frame->f_gen = NULL;
|
||||||
gen->gi_frame = NULL;
|
gen->gi_frame = NULL;
|
||||||
Py_DECREF(f);
|
Py_DECREF(f);
|
||||||
|
@ -810,6 +830,10 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
|
||||||
gen->gi_code = (PyObject *)(f->f_code);
|
gen->gi_code = (PyObject *)(f->f_code);
|
||||||
gen->gi_running = 0;
|
gen->gi_running = 0;
|
||||||
gen->gi_weakreflist = NULL;
|
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)
|
if (name != NULL)
|
||||||
gen->gi_name = name;
|
gen->gi_name = name;
|
||||||
else
|
else
|
||||||
|
|
125
Python/ceval.c
125
Python/ceval.c
|
@ -511,9 +511,6 @@ enum why_code {
|
||||||
WHY_SILENCED = 0x0080 /* Exception silenced by 'with' */
|
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 do_raise(PyObject *, PyObject *);
|
||||||
static int unpack_iterable(PyObject *, int, int, PyObject **);
|
static int unpack_iterable(PyObject *, int, int, PyObject **);
|
||||||
|
|
||||||
|
@ -813,17 +810,19 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
||||||
#define UNWIND_EXCEPT_HANDLER(b) \
|
#define UNWIND_EXCEPT_HANDLER(b) \
|
||||||
do { \
|
do { \
|
||||||
PyObject *type, *value, *traceback; \
|
PyObject *type, *value, *traceback; \
|
||||||
|
_PyErr_StackItem *exc_info; \
|
||||||
assert(STACK_LEVEL() >= (b)->b_level + 3); \
|
assert(STACK_LEVEL() >= (b)->b_level + 3); \
|
||||||
while (STACK_LEVEL() > (b)->b_level + 3) { \
|
while (STACK_LEVEL() > (b)->b_level + 3) { \
|
||||||
value = POP(); \
|
value = POP(); \
|
||||||
Py_XDECREF(value); \
|
Py_XDECREF(value); \
|
||||||
} \
|
} \
|
||||||
type = tstate->exc_type; \
|
exc_info = tstate->exc_info; \
|
||||||
value = tstate->exc_value; \
|
type = exc_info->exc_type; \
|
||||||
traceback = tstate->exc_traceback; \
|
value = exc_info->exc_value; \
|
||||||
tstate->exc_type = POP(); \
|
traceback = exc_info->exc_traceback; \
|
||||||
tstate->exc_value = POP(); \
|
exc_info->exc_type = POP(); \
|
||||||
tstate->exc_traceback = POP(); \
|
exc_info->exc_value = POP(); \
|
||||||
|
exc_info->exc_traceback = POP(); \
|
||||||
Py_XDECREF(type); \
|
Py_XDECREF(type); \
|
||||||
Py_XDECREF(value); \
|
Py_XDECREF(value); \
|
||||||
Py_XDECREF(traceback); \
|
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_stacktop = NULL; /* remains NULL unless yield suspends frame */
|
||||||
f->f_executing = 1;
|
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
|
#ifdef LLTRACE
|
||||||
lltrace = _PyDict_GetItemId(f->f_globals, &PyId___ltrace__) != NULL;
|
lltrace = _PyDict_GetItemId(f->f_globals, &PyId___ltrace__) != NULL;
|
||||||
|
@ -3447,12 +3436,13 @@ fast_block_end:
|
||||||
|| b->b_type == SETUP_FINALLY)) {
|
|| b->b_type == SETUP_FINALLY)) {
|
||||||
PyObject *exc, *val, *tb;
|
PyObject *exc, *val, *tb;
|
||||||
int handler = b->b_handler;
|
int handler = b->b_handler;
|
||||||
|
_PyErr_StackItem *exc_info = tstate->exc_info;
|
||||||
/* Beware, this invalidates all b->b_* fields */
|
/* Beware, this invalidates all b->b_* fields */
|
||||||
PyFrame_BlockSetup(f, EXCEPT_HANDLER, -1, STACK_LEVEL());
|
PyFrame_BlockSetup(f, EXCEPT_HANDLER, -1, STACK_LEVEL());
|
||||||
PUSH(tstate->exc_traceback);
|
PUSH(exc_info->exc_traceback);
|
||||||
PUSH(tstate->exc_value);
|
PUSH(exc_info->exc_value);
|
||||||
if (tstate->exc_type != NULL) {
|
if (exc_info->exc_type != NULL) {
|
||||||
PUSH(tstate->exc_type);
|
PUSH(exc_info->exc_type);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
|
@ -3470,10 +3460,10 @@ fast_block_end:
|
||||||
else
|
else
|
||||||
PyException_SetTraceback(val, Py_None);
|
PyException_SetTraceback(val, Py_None);
|
||||||
Py_INCREF(exc);
|
Py_INCREF(exc);
|
||||||
tstate->exc_type = exc;
|
exc_info->exc_type = exc;
|
||||||
Py_INCREF(val);
|
Py_INCREF(val);
|
||||||
tstate->exc_value = val;
|
exc_info->exc_value = val;
|
||||||
tstate->exc_traceback = tb;
|
exc_info->exc_traceback = tb;
|
||||||
if (tb == NULL)
|
if (tb == NULL)
|
||||||
tb = Py_None;
|
tb = Py_None;
|
||||||
Py_INCREF(tb);
|
Py_INCREF(tb);
|
||||||
|
@ -3516,28 +3506,6 @@ fast_block_end:
|
||||||
assert((retval != NULL) ^ (PyErr_Occurred() != NULL));
|
assert((retval != NULL) ^ (PyErr_Occurred() != NULL));
|
||||||
|
|
||||||
fast_yield:
|
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->use_tracing) {
|
||||||
if (tstate->c_tracefunc) {
|
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).
|
/* Logic for the raise statement (too complicated for inlining).
|
||||||
This *consumes* a reference count to each of its arguments. */
|
This *consumes* a reference count to each of its arguments. */
|
||||||
static int
|
static int
|
||||||
|
@ -4121,10 +4035,11 @@ do_raise(PyObject *exc, PyObject *cause)
|
||||||
if (exc == NULL) {
|
if (exc == NULL) {
|
||||||
/* Reraise */
|
/* Reraise */
|
||||||
PyThreadState *tstate = PyThreadState_GET();
|
PyThreadState *tstate = PyThreadState_GET();
|
||||||
|
_PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate);
|
||||||
PyObject *tb;
|
PyObject *tb;
|
||||||
type = tstate->exc_type;
|
type = exc_info->exc_type;
|
||||||
value = tstate->exc_value;
|
value = exc_info->exc_value;
|
||||||
tb = tstate->exc_traceback;
|
tb = exc_info->exc_traceback;
|
||||||
if (type == Py_None || type == NULL) {
|
if (type == Py_None || type == NULL) {
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_RuntimeError,
|
||||||
"No active exception to reraise");
|
"No active exception to reraise");
|
||||||
|
|
|
@ -53,6 +53,18 @@ PyErr_Restore(PyObject *type, PyObject *value, PyObject *traceback)
|
||||||
Py_XDECREF(oldtraceback);
|
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*
|
static PyObject*
|
||||||
_PyErr_CreateException(PyObject *exception, PyObject *value)
|
_PyErr_CreateException(PyObject *exception, PyObject *value)
|
||||||
{
|
{
|
||||||
|
@ -83,7 +95,7 @@ PyErr_SetObject(PyObject *exception, PyObject *value)
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_XINCREF(value);
|
Py_XINCREF(value);
|
||||||
exc_value = tstate->exc_value;
|
exc_value = _PyErr_GetTopmostException(tstate)->exc_value;
|
||||||
if (exc_value != NULL && exc_value != Py_None) {
|
if (exc_value != NULL && exc_value != Py_None) {
|
||||||
/* Implicit exception chaining */
|
/* Implicit exception chaining */
|
||||||
Py_INCREF(exc_value);
|
Py_INCREF(exc_value);
|
||||||
|
@ -335,9 +347,11 @@ PyErr_GetExcInfo(PyObject **p_type, PyObject **p_value, PyObject **p_traceback)
|
||||||
{
|
{
|
||||||
PyThreadState *tstate = PyThreadState_GET();
|
PyThreadState *tstate = PyThreadState_GET();
|
||||||
|
|
||||||
*p_type = tstate->exc_type;
|
_PyErr_StackItem *exc_info = _PyErr_GetTopmostException(tstate);
|
||||||
*p_value = tstate->exc_value;
|
*p_type = exc_info->exc_type;
|
||||||
*p_traceback = tstate->exc_traceback;
|
*p_value = exc_info->exc_value;
|
||||||
|
*p_traceback = exc_info->exc_traceback;
|
||||||
|
|
||||||
|
|
||||||
Py_XINCREF(*p_type);
|
Py_XINCREF(*p_type);
|
||||||
Py_XINCREF(*p_value);
|
Py_XINCREF(*p_value);
|
||||||
|
@ -350,13 +364,13 @@ PyErr_SetExcInfo(PyObject *p_type, PyObject *p_value, PyObject *p_traceback)
|
||||||
PyObject *oldtype, *oldvalue, *oldtraceback;
|
PyObject *oldtype, *oldvalue, *oldtraceback;
|
||||||
PyThreadState *tstate = PyThreadState_GET();
|
PyThreadState *tstate = PyThreadState_GET();
|
||||||
|
|
||||||
oldtype = tstate->exc_type;
|
oldtype = tstate->exc_info->exc_type;
|
||||||
oldvalue = tstate->exc_value;
|
oldvalue = tstate->exc_info->exc_value;
|
||||||
oldtraceback = tstate->exc_traceback;
|
oldtraceback = tstate->exc_info->exc_traceback;
|
||||||
|
|
||||||
tstate->exc_type = p_type;
|
tstate->exc_info->exc_type = p_type;
|
||||||
tstate->exc_value = p_value;
|
tstate->exc_info->exc_value = p_value;
|
||||||
tstate->exc_traceback = p_traceback;
|
tstate->exc_info->exc_traceback = p_traceback;
|
||||||
|
|
||||||
Py_XDECREF(oldtype);
|
Py_XDECREF(oldtype);
|
||||||
Py_XDECREF(oldvalue);
|
Py_XDECREF(oldvalue);
|
||||||
|
|
|
@ -257,9 +257,11 @@ new_threadstate(PyInterpreterState *interp, int init)
|
||||||
tstate->curexc_value = NULL;
|
tstate->curexc_value = NULL;
|
||||||
tstate->curexc_traceback = NULL;
|
tstate->curexc_traceback = NULL;
|
||||||
|
|
||||||
tstate->exc_type = NULL;
|
tstate->exc_state.exc_type = NULL;
|
||||||
tstate->exc_value = NULL;
|
tstate->exc_state.exc_value = NULL;
|
||||||
tstate->exc_traceback = 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_profilefunc = NULL;
|
||||||
tstate->c_tracefunc = NULL;
|
tstate->c_tracefunc = NULL;
|
||||||
|
@ -444,9 +446,16 @@ PyThreadState_Clear(PyThreadState *tstate)
|
||||||
Py_CLEAR(tstate->curexc_value);
|
Py_CLEAR(tstate->curexc_value);
|
||||||
Py_CLEAR(tstate->curexc_traceback);
|
Py_CLEAR(tstate->curexc_traceback);
|
||||||
|
|
||||||
Py_CLEAR(tstate->exc_type);
|
Py_CLEAR(tstate->exc_state.exc_type);
|
||||||
Py_CLEAR(tstate->exc_value);
|
Py_CLEAR(tstate->exc_state.exc_value);
|
||||||
Py_CLEAR(tstate->exc_traceback);
|
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_profilefunc = NULL;
|
||||||
tstate->c_tracefunc = NULL;
|
tstate->c_tracefunc = NULL;
|
||||||
|
|
|
@ -311,14 +311,13 @@ PyDoc_STRVAR(excepthook_doc,
|
||||||
static PyObject *
|
static PyObject *
|
||||||
sys_exc_info(PyObject *self, PyObject *noargs)
|
sys_exc_info(PyObject *self, PyObject *noargs)
|
||||||
{
|
{
|
||||||
PyThreadState *tstate;
|
_PyErr_StackItem *err_info = _PyErr_GetTopmostException(PyThreadState_GET());
|
||||||
tstate = PyThreadState_GET();
|
|
||||||
return Py_BuildValue(
|
return Py_BuildValue(
|
||||||
"(OOO)",
|
"(OOO)",
|
||||||
tstate->exc_type != NULL ? tstate->exc_type : Py_None,
|
err_info->exc_type != NULL ? err_info->exc_type : Py_None,
|
||||||
tstate->exc_value != NULL ? tstate->exc_value : Py_None,
|
err_info->exc_value != NULL ? err_info->exc_value : Py_None,
|
||||||
tstate->exc_traceback != NULL ?
|
err_info->exc_traceback != NULL ?
|
||||||
tstate->exc_traceback : Py_None);
|
err_info->exc_traceback : Py_None);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(exc_info_doc,
|
PyDoc_STRVAR(exc_info_doc,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue