=== Python/compile.c ================================================================== --- Python/compile.c (revision 53289) +++ Python/compile.c (local) @@ -1880,6 +1880,47 @@ return 1; } +static expr_context_ty +compiler_set_context(expr_ty e, expr_context_ty new_ctx) +{ + expr_context_ty old_ctx; + int n, i; + + switch(e->kind) { + case Attribute_kind: + old_ctx = e->v.Attribute.ctx; + e->v.Attribute.ctx = new_ctx; + break; + case Subscript_kind: + old_ctx = e->v.Subscript.ctx; + e->v.Subscript.ctx = new_ctx; + break; + case Name_kind: + old_ctx = e->v.Name.ctx; + e->v.Name.ctx = new_ctx; + break; + case List_kind: + old_ctx = e->v.List.ctx; + e->v.List.ctx = new_ctx; + + n = asdl_seq_LEN(e->v.List.elts); + for(i = 0; i < n; i++) + compiler_set_context(asdl_seq_GET(e->v.List.elts, i), new_ctx); + break; + case Tuple_kind: + old_ctx = e->v.Tuple.ctx; + e->v.Tuple.ctx = new_ctx; + + n = asdl_seq_LEN(e->v.Tuple.elts); + for(i = 0; i < n; i++) + compiler_set_context(asdl_seq_GET(e->v.Tuple.elts, i), new_ctx); + break; + default: + return 0; + } + return old_ctx; +} + /* Code generated for "try: S except E1, V1: S1 except E2, V2: S2 ...": (The contents of the value stack is shown in [], with the top @@ -1955,13 +1996,74 @@ } ADDOP(c, POP_TOP); if (handler->name) { - VISIT(c, expr, handler->name); + if(handler->name->kind == Tuple_kind || + handler->name->kind == List_kind) { + VISIT(c, expr, handler->name); + ADDOP(c, POP_TOP); + } else { + basicblock *cleanup_end, *cleanup_body; + expr_context_ty orig_ctx; + + cleanup_end = compiler_new_block(c); + cleanup_body = compiler_new_block(c); + if(!(cleanup_end || cleanup_body)) + return 0; + + VISIT(c, expr, handler->name); + ADDOP(c, POP_TOP); + + /* + try: + # body + except type, name: + try: + # body + finally: + name = None + del name + */ + + /* second try: */ + ADDOP_JREL(c, SETUP_FINALLY, cleanup_end); + compiler_use_next_block(c, cleanup_body); + if (!compiler_push_fblock(c, FINALLY_TRY, cleanup_body)) + return 0; + + /* second # body */ + VISIT_SEQ(c, stmt, handler->body); + ADDOP(c, POP_BLOCK); + compiler_pop_fblock(c, FINALLY_TRY, cleanup_body); + + /* finally: */ + ADDOP_O(c, LOAD_CONST, Py_None, consts); + compiler_use_next_block(c, cleanup_end); + if (!compiler_push_fblock(c, FINALLY_END, cleanup_end)) + return 0; + + /* name = None */ + ADDOP_O(c, LOAD_CONST, Py_None, consts); + orig_ctx = compiler_set_context(handler->name, Store); + if(!orig_ctx) + return 0; + VISIT(c, expr, handler->name); + compiler_set_context(handler->name, orig_ctx); + + /* del name */ + orig_ctx = compiler_set_context(handler->name, Del); + if(!orig_ctx) + return 0; + VISIT(c, expr, handler->name); + compiler_set_context(handler->name, orig_ctx); + + ADDOP(c, END_FINALLY); + compiler_pop_fblock(c, FINALLY_END, cleanup_end); + } } else { - ADDOP(c, POP_TOP); + ADDOP(c, POP_TOP); + ADDOP(c, POP_TOP); + VISIT_SEQ(c, stmt, handler->body); } - ADDOP(c, POP_TOP); - VISIT_SEQ(c, stmt, handler->body); ADDOP_JREL(c, JUMP_FORWARD, end); compiler_use_next_block(c, except); if (handler->type) === Lib/test/test_exceptions.py ================================================================== --- Lib/test/test_exceptions.py (revision 53289) +++ Lib/test/test_exceptions.py (local) @@ -344,8 +344,45 @@ self.failUnless(unicode(Exception)) self.failUnless(str(Exception('a'))) self.failUnless(unicode(Exception(u'a'))) + + def testCatchCleanup(self): + # Make sure exceptions caught and named are cleaned up properly + + # Regular + try: + raise Exception() + except Exception, e: + self.failUnless(e) + self.failIf('e' in locals()) + + # Subscription + d = {} + try: + raise Exception() + except Exception, d['e']: + self.failUnless(d['e']) + self.failIf('e' in d) + + # Unpacking should not trigger the cleanup code. Says PJE: + # "In the tuple or list case, there's no need to reset the variables, because + # then the traceback won't be present any more; the exception object will + # have been discarded after unpacking." + try: + raise Exception(5, 6) + except Exception, (a, b): + self.failUnless(a) + self.failUnless(b) + self.failUnless(a) + self.failUnless(b) + + # Proper cleanup + try: + raise Exception() + except Exception, e: + self.failUnless(e) + del e + self.failIf('e' in locals()) - def test_main(): run_unittest(ExceptionTests)