Index: Python/graminit.c =================================================================== --- Python/graminit.c (revision 41455) +++ Python/graminit.c (working copy) @@ -841,15 +841,26 @@ static arc arcs_39_7[1] = { {22, 9}, }; -static arc arcs_39_8[3] = { +static arc arcs_39_8[4] = { {95, 4}, - {91, 5}, + {91, 10}, + {96, 5}, {0, 8}, }; static arc arcs_39_9[1] = { {0, 9}, }; -static state states_39[10] = { +static arc arcs_39_10[1] = { + {21, 11}, +}; +static arc arcs_39_11[1] = { + {22, 12}, +}; +static arc arcs_39_12[2] = { + {96, 5}, + {0, 12}, +}; +static state states_39[13] = { {1, arcs_39_0}, {1, arcs_39_1}, {1, arcs_39_2}, @@ -858,8 +869,11 @@ {1, arcs_39_5}, {1, arcs_39_6}, {1, arcs_39_7}, - {3, arcs_39_8}, + {4, arcs_39_8}, {1, arcs_39_9}, + {1, arcs_39_10}, + {1, arcs_39_11}, + {2, arcs_39_12}, }; static arc arcs_40_0[1] = { {97, 1}, @@ -1754,7 +1768,7 @@ "\000\000\000\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\000\000"}, {294, "for_stmt", 0, 10, states_38, "\000\000\000\000\000\000\000\000\000\000\000\040\000\000\000\000\000\000\000\000\000"}, - {295, "try_stmt", 0, 10, states_39, + {295, "try_stmt", 0, 13, states_39, "\000\000\000\000\000\000\000\000\000\000\000\100\000\000\000\000\000\000\000\000\000"}, {296, "except_clause", 0, 5, states_40, "\000\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\000"}, Index: Python/ast.c =================================================================== --- Python/ast.c (revision 41455) +++ Python/ast.c (working copy) @@ -2862,76 +2862,109 @@ static stmt_ty ast_for_try_stmt(struct compiling *c, const node *n) { + int i; + const int nch = NCH(n); + int n_except = (nch - 3)/3; + stmt_ty result_st = NULL, except_st = NULL; + asdl_seq *body = NULL, *orelse = NULL, *finally = NULL; + asdl_seq *inner = NULL, *handlers = NULL; + REQ(n, try_stmt); + + body = ast_for_suite(c, CHILD(n, 2)); + if (body == NULL) + goto error; - if (TYPE(CHILD(n, 3)) == NAME) {/* must be 'finally' */ - /* try_stmt: 'try' ':' suite 'finally' ':' suite) */ - asdl_seq *s1, *s2; - s1 = ast_for_suite(c, CHILD(n, 2)); - if (!s1) - return NULL; - s2 = ast_for_suite(c, CHILD(n, 5)); - if (!s2) { - asdl_stmt_seq_free(s1); - return NULL; - } - - return TryFinally(s1, s2, LINENO(n)); + if (TYPE(CHILD(n, nch - 3)) == NAME) { + if (strcmp(STR(CHILD(n, nch - 3)), "finally") == 0) { + if (nch >= 9 && TYPE(CHILD(n, nch - 6)) == NAME) { + /* we can assume it's an "else", + because nch >= 9 for try-else-finally and + it would otherwise have a type of except_clause */ + orelse = ast_for_suite(c, CHILD(n, nch - 4)); + if (orelse == NULL) + goto error; + n_except--; + } + + finally = ast_for_suite(c, CHILD(n, nch - 1)); + if (finally == NULL) + goto error; + n_except--; + } + else { + /* we can assume it's an "else", + otherwise it would have a type of except_clause */ + orelse = ast_for_suite(c, CHILD(n, nch - 1)); + if (orelse == NULL) + goto error; + n_except--; + } } - else if (TYPE(CHILD(n, 3)) == except_clause) { - /* try_stmt: ('try' ':' suite (except_clause ':' suite)+ - ['else' ':' suite] - */ - asdl_seq *suite_seq1, *suite_seq2; - asdl_seq *handlers; - int i, has_else = 0, n_except = NCH(n) - 3; - if (TYPE(CHILD(n, NCH(n) - 3)) == NAME) { - has_else = 1; - n_except -= 3; - } - n_except /= 3; - handlers = asdl_seq_new(n_except); - if (!handlers) - return NULL; - for (i = 0; i < n_except; i++) { - excepthandler_ty e = ast_for_except_clause(c, - CHILD(n, 3 + i * 3), - CHILD(n, 5 + i * 3)); - if (!e) { - for ( ; i >= 0; i--) - free_excepthandler(asdl_seq_GET(handlers, i)); - asdl_seq_free(handlers); /* ok */ - return NULL; - } - asdl_seq_SET(handlers, i, e); + else if (TYPE(CHILD(n, nch - 3)) != except_clause) { + ast_error(n, "malformed 'try' statement"); + goto error; + } + + if (n_except > 0) { + /* process except statements to create a try ... except */ + handlers = asdl_seq_new(n_except); + if (handlers == NULL) + goto error; + + for (i = 0; i < n_except; i++) { + excepthandler_ty e = ast_for_except_clause(c, CHILD(n, 3 + i * 3), + CHILD(n, 5 + i * 3)); + if (!e) + goto error; + asdl_seq_SET(handlers, i, e); } + + except_st = TryExcept(body, handlers, orelse, LINENO(n)); + if (except_st == NULL) { + /* free_stmt(except_st) will free these after the jump */ + body = handlers = orelse = NULL; + goto error; + } - suite_seq1 = ast_for_suite(c, CHILD(n, 2)); - if (!suite_seq1) { - for (i = 0; i < asdl_seq_LEN(handlers); i++) - free_excepthandler(asdl_seq_GET(handlers, i)); - asdl_seq_free(handlers); /* ok */ - return NULL; - } - if (has_else) { - suite_seq2 = ast_for_suite(c, CHILD(n, NCH(n) - 1)); - if (!suite_seq2) { - for (i = 0; i < asdl_seq_LEN(handlers); i++) - free_excepthandler(asdl_seq_GET(handlers, i)); - asdl_seq_free(handlers); /* ok */ - asdl_stmt_seq_free(suite_seq1); - return NULL; - } + /* if a 'finally' is present too, we nest the TryExcept within a + TryFinally to emulate try ... except ... finally */ + if (finally != NULL) { + inner = asdl_seq_new(1); + if (inner == NULL) + goto error; + asdl_seq_SET(inner, 0, except_st); + result_st = TryFinally(inner, finally, LINENO(n)); + if (result_st == NULL) { + /* asdl_stmt_seq_free will free except_st after the jump */ + except_st = NULL; + goto error; + } } else - suite_seq2 = NULL; - - return TryExcept(suite_seq1, handlers, suite_seq2, LINENO(n)); + result_st = except_st; } else { - ast_error(n, "malformed 'try' statement"); - return NULL; + /* no exceptions: must be a try ... finally */ + assert(orelse == NULL); + assert(finally != NULL); + result_st = TryFinally(body, finally, LINENO(n)); + if (result_st == NULL) + goto error; } + + return result_st; + +error: + asdl_stmt_seq_free(inner); + free_stmt(except_st); + for (i = 0; i < asdl_seq_LEN(handlers); i++) + free_excepthandler(asdl_seq_GET(handlers, i)); + asdl_seq_free(handlers); + asdl_stmt_seq_free(finally); + asdl_stmt_seq_free(orelse); + asdl_stmt_seq_free(body); + return NULL; } static stmt_ty Index: Grammar/Grammar =================================================================== --- Grammar/Grammar (revision 41455) +++ Grammar/Grammar (working copy) @@ -67,8 +67,7 @@ if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] while_stmt: 'while' test ':' suite ['else' ':' suite] for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite] -try_stmt: ('try' ':' suite (except_clause ':' suite)+ #diagram:break - ['else' ':' suite] | 'try' ':' suite 'finally' ':' suite) +try_stmt: 'try' ':' suite ((except_clause ':' suite)+ ['else' ':' suite] ['finally' ':' suite] | 'finally' ':' suite) # NB compile.c makes sure that the default except clause is last except_clause: 'except' [test [',' test]] suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT Index: Lib/test/test_exception_variations.py =================================================================== --- Lib/test/test_exception_variations.py (revision 0) +++ Lib/test/test_exception_variations.py (revision 0) @@ -0,0 +1,180 @@ +from test.test_support import run_unittest +import unittest + +class ExceptionTestCase(unittest.TestCase): + def test_try_except_else_finally(self): + hit_except = False + hit_else = False + hit_finally = False + + try: + raise Exception, 'nyaa!' + except: + hit_except = True + else: + hit_else = True + finally: + hit_finally = True + + self.assertTrue(hit_except) + self.assertTrue(hit_finally) + self.assertFalse(hit_else) + + def test_try_except_else_finally_no_exception(self): + hit_except = False + hit_else = False + hit_finally = False + + try: + pass + except: + hit_except = True + else: + hit_else = True + finally: + hit_finally = True + + self.assertFalse(hit_except) + self.assertTrue(hit_finally) + self.assertTrue(hit_else) + + def test_try_except_finally(self): + hit_except = False + hit_finally = False + + try: + raise Exception, 'yarr!' + except: + hit_except = True + finally: + hit_finally = True + + self.assertTrue(hit_except) + self.assertTrue(hit_finally) + + def test_try_except_finally_no_exception(self): + hit_except = False + hit_finally = False + + try: + pass + except: + hit_except = True + finally: + hit_finally = True + + self.assertFalse(hit_except) + self.assertTrue(hit_finally) + + def test_try_except(self): + hit_except = False + + try: + raise Exception, 'ahoy!' + except: + hit_except = True + + self.assertTrue(hit_except) + + def test_try_except_no_exception(self): + hit_except = False + + try: + pass + except: + hit_except = True + + self.assertFalse(hit_except) + + def test_try_except_else(self): + hit_except = False + hit_else = False + + try: + raise Exception, 'foo!' + except: + hit_except = True + else: + hit_else = True + + self.assertFalse(hit_else) + self.assertTrue(hit_except) + + def test_try_except_else_no_exception(self): + hit_except = False + hit_else = False + + try: + pass + except: + hit_except = True + else: + hit_else = True + + self.assertFalse(hit_except) + self.assertTrue(hit_else) + + def test_try_finally_no_exception(self): + hit_finally = False + + try: + pass + finally: + hit_finally = True + + self.assertTrue(hit_finally) + + def test_nested(self): + hit_finally = False + hit_inner_except = False + hit_inner_finally = False + + try: + try: + raise Exception, 'inner exception' + except: + hit_inner_except = True + finally: + hit_inner_finally = True + finally: + hit_finally = True + + self.assertTrue(hit_inner_except) + self.assertTrue(hit_inner_finally) + self.assertTrue(hit_finally) + + def test_nested_else(self): + hit_else = False + hit_finally = False + hit_except = False + hit_inner_except = False + hit_inner_else = False + + try: + try: + pass + except: + hit_inner_except = True + else: + hit_inner_else = True + + raise Exception, 'outer exception' + except: + hit_except = True + else: + hit_else = True + finally: + hit_finally = True + + self.assertFalse(hit_inner_except) + self.assertTrue(hit_inner_else) + self.assertFalse(hit_else) + self.assertTrue(hit_finally) + self.assertTrue(hit_except) + +def test_main(): + run_unittest(ExceptionTestCase) + +if __name__ == '__main__': + test_main() +