diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index d63c1778c9d..5f712111ca1 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -205,9 +205,9 @@ class TestLineCounts(unittest.TestCase): (self.my_py_filename, firstlineno + 4): 1, (self.my_py_filename, firstlineno + 5): 1, (self.my_py_filename, firstlineno + 6): 1, - (self.my_py_filename, firstlineno + 7): 1, - (self.my_py_filename, firstlineno + 8): 1, - (self.my_py_filename, firstlineno + 9): 1, + (self.my_py_filename, firstlineno + 7): 2, + (self.my_py_filename, firstlineno + 8): 2, + (self.my_py_filename, firstlineno + 9): 2, (self.my_py_filename, firstlineno + 10): 1, (self.my_py_filename, firstlineno + 11): 1, } diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index a458b21b094..97bd9bae1d5 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -616,6 +616,51 @@ class TracebackErrorLocationCaretTests(unittest.TestCase): self.assertSpecialized(lambda: 1// 0, "~^^~~") + def test_decorator_application_lineno_correct(self): + def dec_error(func): + raise TypeError + def dec_fine(func): + return func + def applydecs(): + @dec_error + @dec_fine + def g(): pass + result_lines = self.get_exception(applydecs) + lineno_applydescs = applydecs.__code__.co_firstlineno + lineno_dec_error = dec_error.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ^^^^^^^^^^\n' + f' File "{__file__}", line {lineno_applydescs + 1}, in applydecs\n' + ' @dec_error\n' + ' ^^^^^^^^^\n' + f' File "{__file__}", line {lineno_dec_error + 1}, in dec_error\n' + ' raise TypeError\n' + ' ^^^^^^^^^^^^^^^\n' + ) + self.assertEqual(result_lines, expected_error.splitlines()) + + def applydecs_class(): + @dec_error + @dec_fine + class A: pass + result_lines = self.get_exception(applydecs_class) + lineno_applydescs_class = applydecs_class.__code__.co_firstlineno + expected_error = ( + 'Traceback (most recent call last):\n' + f' File "{__file__}", line {self.callable_line}, in get_exception\n' + ' callable()\n' + ' ^^^^^^^^^^\n' + f' File "{__file__}", line {lineno_applydescs_class + 1}, in applydecs_class\n' + ' @dec_error\n' + ' ^^^^^^^^^\n' + f' File "{__file__}", line {lineno_dec_error + 1}, in dec_error\n' + ' raise TypeError\n' + ' ^^^^^^^^^^^^^^^\n' + ) + self.assertEqual(result_lines, expected_error.splitlines()) @cpython_only @requires_debug_ranges() diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-12-10-13-42-17.bpo-37971.6BC1Tx.rst b/Misc/NEWS.d/next/Core and Builtins/2021-12-10-13-42-17.bpo-37971.6BC1Tx.rst new file mode 100644 index 00000000000..17f44f0e5b0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-12-10-13-42-17.bpo-37971.6BC1Tx.rst @@ -0,0 +1,3 @@ +Fix a bug where the line numbers given in a traceback when a decorator +application raised an exception were wrong. + diff --git a/Python/compile.c b/Python/compile.c index 6138031833a..bbdb0115be3 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2132,6 +2132,27 @@ compiler_decorators(struct compiler *c, asdl_expr_seq* decos) return 1; } +static int +compiler_apply_decorators(struct compiler *c, asdl_expr_seq* decos) +{ + if (!decos) + return 1; + + int old_lineno = c->u->u_lineno; + int old_end_lineno = c->u->u_end_lineno; + int old_col_offset = c->u->u_col_offset; + int old_end_col_offset = c->u->u_end_col_offset; + for (Py_ssize_t i = asdl_seq_LEN(decos) - 1; i > -1; i--) { + SET_LOC(c, (expr_ty)asdl_seq_GET(decos, i)); + ADDOP_I(c, CALL_FUNCTION, 1); + } + c->u->u_lineno = old_lineno; + c->u->u_end_lineno = old_end_lineno; + c->u->u_col_offset = old_col_offset; + c->u->u_end_col_offset = old_end_col_offset; + return 1; +} + static int compiler_visit_kwonlydefaults(struct compiler *c, asdl_arg_seq *kwonlyargs, asdl_expr_seq *kw_defaults) @@ -2462,11 +2483,8 @@ compiler_function(struct compiler *c, stmt_ty s, int is_async) Py_DECREF(qualname); Py_DECREF(co); - /* decorators */ - for (i = 0; i < asdl_seq_LEN(decos); i++) { - ADDOP_I(c, CALL_FUNCTION, 1); - } - + if (!compiler_apply_decorators(c, decos)) + return 0; return compiler_nameop(c, name, Store); } @@ -2597,9 +2615,8 @@ compiler_class(struct compiler *c, stmt_ty s) return 0; /* 6. apply decorators */ - for (i = 0; i < asdl_seq_LEN(decos); i++) { - ADDOP_I(c, CALL_FUNCTION, 1); - } + if (!compiler_apply_decorators(c, decos)) + return 0; /* 7. store into */ if (!compiler_nameop(c, s->v.ClassDef.name, Store))