diff -r 51e5afca0f70 Doc/library/dis.rst --- a/Doc/library/dis.rst Fri May 30 20:10:19 2008 +0200 +++ b/Doc/library/dis.rst Sun Jun 01 19:06:11 2008 +0200 @@ -397,6 +397,14 @@ Miscellaneous opcodes. denoting nested loops, try statements, and such. +.. opcode:: POP_EXCEPT () + + Removes one block from the block stack. The popped block must be an exception + handler block, as implicitly created when entering an except handler. + In addition to popping extraneous values from the frame stack, the + last three popped values are used to restore the exception state. + + .. opcode:: END_FINALLY () Terminates a :keyword:`finally` clause. The interpreter recalls whether the diff -r 51e5afca0f70 Doc/library/inspect.rst --- a/Doc/library/inspect.rst Fri May 30 20:10:19 2008 +0200 +++ b/Doc/library/inspect.rst Sun Jun 01 19:06:11 2008 +0200 @@ -94,17 +94,6 @@ attributes: | | f_code | code object being | | | | executed in this frame | +-----------+-----------------+---------------------------+ -| | f_exc_traceback | traceback if raised in | -| | | this frame, or ``None`` | -+-----------+-----------------+---------------------------+ -| | f_exc_type | exception type if raised | -| | | in this frame, or | -| | | ``None`` | -+-----------+-----------------+---------------------------+ -| | f_exc_value | exception value if raised | -| | | in this frame, or | -| | | ``None`` | -+-----------+-----------------+---------------------------+ | | f_globals | global namespace seen by | | | | this frame | +-----------+-----------------+---------------------------+ diff -r 51e5afca0f70 Doc/library/sys.rst --- a/Doc/library/sys.rst Fri May 30 20:10:19 2008 +0200 +++ b/Doc/library/sys.rst Sun Jun 01 19:06:11 2008 +0200 @@ -136,8 +136,8 @@ always available. frame is not handling an exception, the information is taken from the calling stack frame, or its caller, and so on until a stack frame is found that is handling an exception. Here, "handling an exception" is defined as "executing - or having executed an except clause." For any stack frame, only information - about the most recently handled exception is accessible. + an except clause." For any stack frame, only information about the exception + being currently handled is accessible. .. index:: object: traceback diff -r 51e5afca0f70 Doc/reference/datamodel.rst --- a/Doc/reference/datamodel.rst Fri May 30 20:10:19 2008 +0200 +++ b/Doc/reference/datamodel.rst Sun Jun 01 19:06:11 2008 +0200 @@ -875,19 +875,14 @@ Internal types .. index:: single: f_trace (frame attribute) - single: f_exc_type (frame attribute) - single: f_exc_value (frame attribute) - single: f_exc_traceback (frame attribute) single: f_lineno (frame attribute) Special writable attributes: :attr:`f_trace`, if not ``None``, is a function called at the start of each source code line (this is used by the debugger); - :attr:`f_exc_type`, :attr:`f_exc_value`, :attr:`f_exc_traceback` represent the - last exception raised in the parent frame provided another exception was ever - raised in the current frame (in all other cases they are None); :attr:`f_lineno` - is the current line number of the frame --- writing to this from within a trace - function jumps to the given line (only for the bottom-most frame). A debugger - can implement a Jump command (aka Set Next Statement) by writing to f_lineno. + :attr:`f_lineno` is the current line number of the frame --- writing to this + from within a trace function jumps to the given line (only for the bottom-most + frame). A debugger can implement a Jump command (aka Set Next Statement) + by writing to f_lineno. Traceback objects .. index:: diff -r 51e5afca0f70 Include/frameobject.h --- a/Include/frameobject.h Fri May 30 20:10:19 2008 +0200 +++ b/Include/frameobject.h Sun Jun 01 19:06:11 2008 +0200 @@ -27,13 +27,13 @@ typedef struct _frame { PyObject **f_stacktop; PyObject *f_trace; /* Trace function */ - /* If an exception is raised in this frame, the next three are used to - * record the exception info (if any) originally in the thread state. See - * comments before set_exc_info() -- it's not obvious. - * Invariant: if _type is NULL, then so are _value and _traceback. - * Desired invariant: all three are NULL, or all three are non-NULL. That - * one isn't currently true, but "should be". - */ + /* 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; diff -r 51e5afca0f70 Include/opcode.h --- a/Include/opcode.h Fri May 30 20:10:19 2008 +0200 +++ b/Include/opcode.h Sun Jun 01 19:06:11 2008 +0200 @@ -70,6 +70,7 @@ extern "C" { #define YIELD_VALUE 86 #define POP_BLOCK 87 #define END_FINALLY 88 +#define POP_EXCEPT 89 #define HAVE_ARGUMENT 90 /* Opcodes from here have an argument: */ @@ -133,6 +134,13 @@ extern "C" { #define EXTENDED_ARG 143 +/* EXCEPT_HANDLER is a special, implicit block type which is created when + entering an except handler. It is not an opcode but we define it here + as we want it to be available to both frameobject.c and ceval.c, while + remaining private.*/ +#define EXCEPT_HANDLER 257 + + enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE, PyCmp_GT=Py_GT, PyCmp_GE=Py_GE, PyCmp_IN, PyCmp_NOT_IN, PyCmp_IS, PyCmp_IS_NOT, PyCmp_EXC_MATCH, PyCmp_BAD}; diff -r 51e5afca0f70 Lib/doctest.py --- a/Lib/doctest.py Fri May 30 20:10:19 2008 +0200 +++ b/Lib/doctest.py Sun Jun 01 19:06:11 2008 +0200 @@ -1242,10 +1242,9 @@ class DocTestRunner: # The example raised an exception: check if it was expected. else: - exc_info = sys.exc_info() - exc_msg = traceback.format_exception_only(*exc_info[:2])[-1] + exc_msg = traceback.format_exception_only(*exception[:2])[-1] if not quiet: - got += _exception_traceback(exc_info) + got += _exception_traceback(exception) # If `example.exc_msg` is None, then we weren't expecting # an exception. @@ -1275,7 +1274,7 @@ class DocTestRunner: elif outcome is BOOM: if not quiet: self.report_unexpected_exception(out, test, example, - exc_info) + exception) failures += 1 else: assert False, ("unknown outcome", outcome) diff -r 51e5afca0f70 Lib/inspect.py --- a/Lib/inspect.py Fri May 30 20:10:19 2008 +0200 +++ b/Lib/inspect.py Sun Jun 01 19:06:11 2008 +0200 @@ -197,9 +197,6 @@ def isframe(object): f_back next outer frame object (this frame's caller) f_builtins built-in namespace seen by this frame f_code code object being executed in this frame - f_exc_traceback traceback if raised in this frame, or None - f_exc_type exception type if raised in this frame, or None - f_exc_value exception value if raised in this frame, or None f_globals global namespace seen by this frame f_lasti index of last attempted instruction in bytecode f_lineno current line number in Python source code diff -r 51e5afca0f70 Lib/opcode.py --- a/Lib/opcode.py Fri May 30 20:10:19 2008 +0200 +++ b/Lib/opcode.py Sun Jun 01 19:06:11 2008 +0200 @@ -105,6 +105,7 @@ def_op('YIELD_VALUE', 86) def_op('YIELD_VALUE', 86) def_op('POP_BLOCK', 87) def_op('END_FINALLY', 88) +def_op('POP_EXCEPT', 89) HAVE_ARGUMENT = 90 # Opcodes from here have an argument: diff -r 51e5afca0f70 Lib/test/test_exceptions.py --- a/Lib/test/test_exceptions.py Fri May 30 20:10:19 2008 +0200 +++ b/Lib/test/test_exceptions.py Sun Jun 01 19:06:11 2008 +0200 @@ -427,6 +427,7 @@ class ExceptionTests(unittest.TestCase): local_ref = obj raise MyException(obj) + # Qualified "except" with "as" obj = MyObj() wr = weakref.ref(obj) try: @@ -437,6 +438,113 @@ class ExceptionTests(unittest.TestCase): obj = wr() self.failUnless(obj is None, "%s" % obj) + # Qualified "except" without "as" + obj = MyObj() + wr = weakref.ref(obj) + try: + inner_raising_func() + except MyException: + pass + obj = None + obj = wr() + self.failUnless(obj is None, "%s" % obj) + + # Bare "except" + obj = MyObj() + wr = weakref.ref(obj) + try: + inner_raising_func() + except: + pass + obj = None + obj = wr() + self.failUnless(obj is None, "%s" % obj) + + # "except" with premature block leave + obj = MyObj() + wr = weakref.ref(obj) + for i in [0]: + try: + inner_raising_func() + except: + break + obj = None + obj = wr() + self.failUnless(obj is None, "%s" % obj) + + # "except" block raising another exception + obj = MyObj() + wr = weakref.ref(obj) + try: + try: + inner_raising_func() + except: + raise KeyError + except KeyError: + obj = None + obj = wr() + self.failUnless(obj is None, "%s" % obj) + + # Some complicated construct + obj = MyObj() + wr = weakref.ref(obj) + try: + inner_raising_func() + except MyException: + try: + try: + raise + finally: + raise + except MyException: + pass + obj = None + obj = wr() + self.failUnless(obj is None, "%s" % obj) + + # Inside an exception-silencing "with" block + class Context: + def __enter__(self): + return self + def __exit__ (self, exc_type, exc_value, exc_tb): + return True + obj = MyObj() + wr = weakref.ref(obj) + with Context(): + inner_raising_func() + obj = None + obj = wr() + self.failUnless(obj is None, "%s" % obj) + + def test_generator_leaking(self): + # Test that generator exception state doesn't leak into the calling + # frame + def yield_raise(): + try: + raise KeyError("caught") + except KeyError: + yield sys.exc_info()[0] + yield sys.exc_info()[0] + yield sys.exc_info()[0] + g = yield_raise() + self.assertEquals(next(g), KeyError) + self.assertEquals(sys.exc_info()[0], None) + self.assertEquals(next(g), KeyError) + self.assertEquals(sys.exc_info()[0], None) + self.assertEquals(next(g), None) + + # Same test, but inside an exception handler + try: + raise TypeError("foo") + except TypeError: + g = yield_raise() + self.assertEquals(next(g), KeyError) + self.assertEquals(sys.exc_info()[0], TypeError) + self.assertEquals(next(g), KeyError) + self.assertEquals(sys.exc_info()[0], TypeError) + self.assertEquals(next(g), TypeError) + del g + self.assertEquals(sys.exc_info()[0], TypeError) def test_main(): run_unittest(ExceptionTests) diff -r 51e5afca0f70 Lib/test/test_raise.py --- a/Lib/test/test_raise.py Fri May 30 20:10:19 2008 +0200 +++ b/Lib/test/test_raise.py Sun Jun 01 19:06:11 2008 +0200 @@ -14,6 +14,13 @@ def get_tb(): raise OSError() except: return sys.exc_info()[2] + + +class Context: + def __enter__(self): + return self + def __exit__(self, exc_type, exc_value, exc_tb): + return True class TestRaise(unittest.TestCase): @@ -36,6 +43,60 @@ class TestRaise(unittest.TestCase): self.failUnless(exc1 is exc2) else: self.fail("No exception raised") + + def test_except_reraise(self): + def reraise(): + try: + raise TypeError("foo") + except: + try: + raise KeyError("caught") + except KeyError: + pass + raise + self.assertRaises(TypeError, reraise) + + def test_nested_reraise(self): + def nested_reraise(): + raise + def reraise(): + try: + raise TypeError("foo") + except: + nested_reraise() + self.assertRaises(TypeError, reraise) + + def test_with_reraise1(self): + def reraise(): + try: + raise TypeError("foo") + except: + with Context(): + pass + raise + self.assertRaises(TypeError, reraise) + + def test_with_reraise2(self): + def reraise(): + try: + raise TypeError("foo") + except: + with Context(): + raise KeyError("caught") + raise + self.assertRaises(TypeError, reraise) + + def test_yield_reraise(self): + def reraise(): + try: + raise TypeError("foo") + except: + yield 1 + raise + g = reraise() + next(g) + self.assertRaises(TypeError, lambda: next(g)) + self.assertRaises(StopIteration, lambda: next(g)) def test_erroneous_exception(self): class MyException(Exception): @@ -158,6 +219,5 @@ def test_main(): def test_main(): support.run_unittest(__name__) - if __name__ == "__main__": unittest.main() diff -r 51e5afca0f70 Misc/cheatsheet --- a/Misc/cheatsheet Fri May 30 20:10:19 2008 +0200 +++ b/Misc/cheatsheet Sun Jun 01 19:06:11 2008 +0200 @@ -1263,9 +1263,6 @@ Special informative state attributes for f_lineno (int, R/O): current line number f_lasti (int, R/O): precise instruction (index into bytecode) f_trace (function/None, R/W): debug hook called at start of each source line - f_exc_type (Type/None, R/W): Most recent exception type - f_exc_value (any, R/W): Most recent exception value - f_exc_traceback (traceback/None, R/W): Most recent exception traceback Tracebacks: tb_next (frame/None, R/O): next level in stack trace (toward the frame where the exception occurred) diff -r 51e5afca0f70 Objects/frameobject.c --- a/Objects/frameobject.c Fri May 30 20:10:19 2008 +0200 +++ b/Objects/frameobject.c Sun Jun 01 19:06:11 2008 +0200 @@ -20,9 +20,6 @@ static PyMemberDef frame_memberlist[] = {"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_exc_type", T_OBJECT, OFF(f_exc_type)}, - {"f_exc_value", T_OBJECT, OFF(f_exc_value)}, - {"f_exc_traceback", T_OBJECT, OFF(f_exc_traceback)}, {NULL} /* Sentinel */ }; diff -r 51e5afca0f70 Python/ceval.c --- a/Python/ceval.c Fri May 30 20:10:19 2008 +0200 +++ b/Python/ceval.c Sun Jun 01 19:06:11 2008 +0200 @@ -116,8 +116,6 @@ static PyObject * cmp_outcome(int, PyObj static PyObject * cmp_outcome(int, PyObject *, PyObject *); static PyObject * import_from(PyObject *, PyObject *); static int import_all_from(PyObject *, PyObject *); -static void set_exc_info(PyThreadState *, PyObject *, PyObject *, PyObject *); -static void reset_exc_info(PyThreadState *); static void format_exc_check_arg(PyObject *, const char *, PyObject *); static PyObject * unicode_concatenate(PyObject *, PyObject *, PyFrameObject *, unsigned char *); @@ -692,6 +690,52 @@ PyEval_EvalFrameEx(PyFrameObject *f, int GETLOCAL(i) = value; \ Py_XDECREF(tmp); } while (0) + +#define UNWIND_BLOCK(b) \ + while (STACK_LEVEL() > (b)->b_level) { \ + v = POP(); \ + Py_XDECREF(v); \ + } + +#define UNWIND_EXCEPT_HANDLER(b) \ + while (STACK_LEVEL() > (b)->b_level + 3) { \ + v = POP(); \ + Py_XDECREF(v); \ + } \ + Py_XDECREF(tstate->exc_type); \ + tstate->exc_type = POP(); \ + Py_XDECREF(tstate->exc_value); \ + tstate->exc_value = POP(); \ + Py_XDECREF(tstate->exc_traceback); \ + tstate->exc_traceback = POP(); + +#define SAVE_EXC_STATE() \ + { \ + Py_XINCREF(tstate->exc_type); \ + Py_XINCREF(tstate->exc_value); \ + Py_XINCREF(tstate->exc_traceback); \ + Py_XDECREF(f->f_exc_type); \ + Py_XDECREF(f->f_exc_value); \ + Py_XDECREF(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; \ + } + +#define SWAP_EXC_STATE() \ + { \ + 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; \ + } + /* Start of code */ if (f == NULL) @@ -764,6 +808,18 @@ PyEval_EvalFrameEx(PyFrameObject *f, int stack_pointer = f->f_stacktop; assert(stack_pointer != NULL); f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ + + if (f->f_code->co_flags & CO_GENERATOR) { + 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(); + } + else { + SAVE_EXC_STATE(); + } + } #ifdef LLTRACE lltrace = PyDict_GetItemString(f->f_globals, "__lltrace__") != NULL; @@ -1443,15 +1499,29 @@ PyEval_EvalFrameEx(PyFrameObject *f, int retval = POP(); f->f_stacktop = stack_pointer; why = WHY_YIELD; + /* Put aside the current exception state and restore + that of the calling frame. This only serves when + "yield" is used inside an except handler. */ + SWAP_EXC_STATE(); goto fast_yield; + + case POP_EXCEPT: + { + PyTryBlock *b = PyFrame_BlockPop(f); + if (b->b_type != EXCEPT_HANDLER) { + PyErr_SetString(PyExc_SystemError, + "popped block is not an except handler"); + why = WHY_EXCEPTION; + break; + } + UNWIND_EXCEPT_HANDLER(b); + } + continue; case POP_BLOCK: { PyTryBlock *b = PyFrame_BlockPop(f); - while (STACK_LEVEL() > b->b_level) { - v = POP(); - Py_DECREF(v); - } + UNWIND_BLOCK(b); } continue; @@ -1476,19 +1546,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int PyErr_SetString(PyExc_SystemError, "'finally' pops bad exception"); why = WHY_EXCEPTION; - } - /* - Make sure the exception state is cleaned up before - the end of an except block. This ensures objects - referenced by the exception state are not kept - alive too long. - See #2507. - */ - if (tstate->frame->f_exc_type != NULL) - reset_exc_info(tstate); - else { - assert(tstate->frame->f_exc_value == NULL); - assert(tstate->frame->f_exc_traceback == NULL); } Py_DECREF(v); break; @@ -2370,18 +2427,60 @@ fast_block_end: break; } - while (STACK_LEVEL() > b->b_level) { - v = POP(); - Py_XDECREF(v); + if (b->b_type == EXCEPT_HANDLER) { + UNWIND_EXCEPT_HANDLER(b); + if (why == WHY_EXCEPTION) { + Py_CLEAR(tstate->exc_type); + Py_CLEAR(tstate->exc_value); + Py_CLEAR(tstate->exc_traceback); + } + continue; } + UNWIND_BLOCK(b); if (b->b_type == SETUP_LOOP && why == WHY_BREAK) { why = WHY_NOT; JUMPTO(b->b_handler); break; } - if (b->b_type == SETUP_FINALLY || - (b->b_type == SETUP_EXCEPT && - why == WHY_EXCEPTION)) { + if (b->b_type == SETUP_EXCEPT && + why == WHY_EXCEPTION) { + PyObject *exc, *val, *tb; + 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); + } + else { + Py_INCREF(Py_None); + PUSH(Py_None); + } + PyErr_Fetch(&exc, &val, &tb); + /* Make the raw exception data + available to the handler, + so a program can emulate the + Python main loop. */ + PyErr_NormalizeException( + &exc, &val, &tb); + PyException_SetTraceback(val, tb); + Py_INCREF(exc); + tstate->exc_type = exc; + Py_INCREF(val); + tstate->exc_value = val; + tstate->exc_traceback = tb; + if (tb == NULL) + tb = Py_None; + Py_INCREF(tb); + PUSH(tb); + PUSH(val); + PUSH(exc); + why = WHY_NOT; + JUMPTO(handler); + break; + } + if (b->b_type == SETUP_FINALLY) { if (why == WHY_EXCEPTION) { PyObject *exc, *val, *tb; PyErr_Fetch(&exc, &val, &tb); @@ -2389,22 +2488,11 @@ fast_block_end: val = Py_None; Py_INCREF(val); } - /* Make the raw exception data - available to the handler, - so a program can emulate the - Python main loop. Don't do - this for 'finally'. */ - if (b->b_type == SETUP_EXCEPT) { - PyErr_NormalizeException( - &exc, &val, &tb); - set_exc_info(tstate, - exc, val, tb); + if (tb == NULL) { + tb = Py_None; + Py_INCREF(tb); } - if (tb == NULL) { - Py_INCREF(Py_None); - PUSH(Py_None); - } else - PUSH(tb); + PUSH(tb); PUSH(val); PUSH(exc); } @@ -2469,13 +2557,6 @@ fast_yield: why = WHY_EXCEPTION; } } - } - - if (tstate->frame->f_exc_type != NULL) - reset_exc_info(tstate); - else { - assert(tstate->frame->f_exc_value == NULL); - assert(tstate->frame->f_exc_traceback == NULL); } /* pop frame */ @@ -2757,150 +2838,6 @@ fail: /* Jump here from prelude on failu } -/* Implementation notes for set_exc_info() and reset_exc_info(): - -- Below, 'exc_ZZZ' stands for 'exc_type', 'exc_value' and - 'exc_traceback'. These always travel together. - -- tstate->curexc_ZZZ is the "hot" exception that is set by - PyErr_SetString(), cleared by PyErr_Clear(), and so on. - -- Once an exception is caught by an except clause, it is transferred - from tstate->curexc_ZZZ to tstate->exc_ZZZ, from which sys.exc_info() - can pick it up. This is the primary task of set_exc_info(). - XXX That can't be right: set_exc_info() doesn't look at tstate->curexc_ZZZ. - -- Now let me explain the complicated dance with frame->f_exc_ZZZ. - - Long ago, when none of this existed, there were just a few globals: - one set corresponding to the "hot" exception, and one set - corresponding to sys.exc_ZZZ. (Actually, the latter weren't C - globals; they were simply stored as sys.exc_ZZZ. For backwards - compatibility, they still are!) The problem was that in code like - this: - - try: - "something that may fail" - except "some exception": - "do something else first" - "print the exception from sys.exc_ZZZ." - - if "do something else first" invoked something that raised and caught - an exception, sys.exc_ZZZ were overwritten. That was a frequent - cause of subtle bugs. I fixed this by changing the semantics as - follows: - - - Within one frame, sys.exc_ZZZ will hold the last exception caught - *in that frame*. - - - But initially, and as long as no exception is caught in a given - frame, sys.exc_ZZZ will hold the last exception caught in the - previous frame (or the frame before that, etc.). - - The first bullet fixed the bug in the above example. The second - bullet was for backwards compatibility: it was (and is) common to - have a function that is called when an exception is caught, and to - have that function access the caught exception via sys.exc_ZZZ. - (Example: traceback.print_exc()). - - At the same time I fixed the problem that sys.exc_ZZZ weren't - thread-safe, by introducing sys.exc_info() which gets it from tstate; - but that's really a separate improvement. - - The reset_exc_info() function in ceval.c restores the tstate->exc_ZZZ - variables to what they were before the current frame was called. The - set_exc_info() function saves them on the frame so that - reset_exc_info() can restore them. The invariant is that - frame->f_exc_ZZZ is NULL iff the current frame never caught an - exception (where "catching" an exception applies only to successful - except clauses); and if the current frame ever caught an exception, - frame->f_exc_ZZZ is the exception that was stored in tstate->exc_ZZZ - at the start of the current frame. - -*/ - -static void -set_exc_info(PyThreadState *tstate, - PyObject *type, PyObject *value, PyObject *tb) -{ - PyFrameObject *frame = tstate->frame; - PyObject *tmp_type, *tmp_value, *tmp_tb; - - assert(type != NULL); - assert(frame != NULL); - if (frame->f_exc_type == NULL) { - assert(frame->f_exc_value == NULL); - assert(frame->f_exc_traceback == NULL); - /* This frame didn't catch an exception before. */ - /* Save previous exception of this thread in this frame. */ - if (tstate->exc_type == NULL) { - /* XXX Why is this set to Py_None? */ - Py_INCREF(Py_None); - tstate->exc_type = Py_None; - } - Py_INCREF(tstate->exc_type); - Py_XINCREF(tstate->exc_value); - Py_XINCREF(tstate->exc_traceback); - frame->f_exc_type = tstate->exc_type; - frame->f_exc_value = tstate->exc_value; - frame->f_exc_traceback = tstate->exc_traceback; - } - /* Set new exception for this thread. */ - tmp_type = tstate->exc_type; - tmp_value = tstate->exc_value; - tmp_tb = tstate->exc_traceback; - Py_INCREF(type); - Py_XINCREF(value); - Py_XINCREF(tb); - tstate->exc_type = type; - tstate->exc_value = value; - tstate->exc_traceback = tb; - PyException_SetTraceback(value, tb); - Py_XDECREF(tmp_type); - Py_XDECREF(tmp_value); - Py_XDECREF(tmp_tb); -} - -static void -reset_exc_info(PyThreadState *tstate) -{ - PyFrameObject *frame; - PyObject *tmp_type, *tmp_value, *tmp_tb; - - /* It's a precondition that the thread state's frame caught an - * exception -- verify in a debug build. - */ - assert(tstate != NULL); - frame = tstate->frame; - assert(frame != NULL); - assert(frame->f_exc_type != NULL); - - /* Copy the frame's exception info back to the thread state. */ - tmp_type = tstate->exc_type; - tmp_value = tstate->exc_value; - tmp_tb = tstate->exc_traceback; - Py_INCREF(frame->f_exc_type); - Py_XINCREF(frame->f_exc_value); - Py_XINCREF(frame->f_exc_traceback); - tstate->exc_type = frame->f_exc_type; - tstate->exc_value = frame->f_exc_value; - tstate->exc_traceback = frame->f_exc_traceback; - Py_XDECREF(tmp_type); - Py_XDECREF(tmp_value); - Py_XDECREF(tmp_tb); - - /* Clear the frame's exception info. */ - tmp_type = frame->f_exc_type; - tmp_value = frame->f_exc_value; - tmp_tb = frame->f_exc_traceback; - frame->f_exc_type = NULL; - frame->f_exc_value = NULL; - frame->f_exc_traceback = NULL; - Py_DECREF(tmp_type); - Py_XDECREF(tmp_value); - Py_XDECREF(tmp_tb); -} - /* Logic for the raise statement (too complicated for inlining). This *consumes* a reference count to each of its arguments. */ static enum why_code diff -r 51e5afca0f70 Python/compile.c --- a/Python/compile.c Fri May 30 20:10:19 2008 +0200 +++ b/Python/compile.c Sun Jun 01 19:06:11 2008 +0200 @@ -760,6 +760,8 @@ opcode_stack_effect(int opcode, int opar case POP_BLOCK: return 0; + case POP_EXCEPT: + return 0; /* -3 except if bad bytecode */ case END_FINALLY: return -1; /* or -2 or -3 if exception occurred */ @@ -817,6 +819,8 @@ opcode_stack_effect(int opcode, int opar case SETUP_LOOP: return 0; case SETUP_EXCEPT: + return 6; /* can push 3 values for the new exception + + 3 others for the previous exception state */ case SETUP_FINALLY: return 3; /* actually pushed by an exception */ @@ -2031,6 +2035,7 @@ compiler_try_except(struct compiler *c, /* second # body */ VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body); ADDOP(c, POP_BLOCK); + ADDOP(c, POP_EXCEPT); compiler_pop_fblock(c, FINALLY_TRY, cleanup_body); /* finally: */ @@ -2050,9 +2055,20 @@ compiler_try_except(struct compiler *c, compiler_pop_fblock(c, FINALLY_END, cleanup_end); } else { + basicblock *cleanup_body; + + cleanup_body = compiler_new_block(c); + if(!cleanup_body) + return 0; + + ADDOP(c, POP_TOP); ADDOP(c, POP_TOP); - ADDOP(c, POP_TOP); + compiler_use_next_block(c, cleanup_body); + if (!compiler_push_fblock(c, FINALLY_TRY, cleanup_body)) + return 0; VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body); + ADDOP(c, POP_EXCEPT); + compiler_pop_fblock(c, FINALLY_TRY, cleanup_body); } ADDOP_JREL(c, JUMP_FORWARD, end); compiler_use_next_block(c, except); diff -r 51e5afca0f70 Python/import.c --- a/Python/import.c Fri May 30 20:10:19 2008 +0200 +++ b/Python/import.c Sun Jun 01 19:06:11 2008 +0200 @@ -86,8 +86,9 @@ extern time_t PyOS_GetLastModificationTi 3100 (merge from 2.6a0, see 62151) 3102 (__file__ points to source file) Python 3.0a4: 3110 (WITH_CLEANUP optimization). + Python 3.0a4: 3120 (lexical exception stacking, including POP_EXCEPT) */ -#define MAGIC (3110 | ((long)'\r'<<16) | ((long)'\n'<<24)) +#define MAGIC (3120 | ((long)'\r'<<16) | ((long)'\n'<<24)) /* Magic word as global; note that _PyImport_Init() can change the value of this global to accommodate for alterations of how the