From d33d3769b28328649c4f6e447e083092f8cec16f Mon Sep 17 00:00:00 2001 From: Junhyung Lee <70549809+espressolee@users.noreply.github.com> Date: Fri, 4 Sep 2026 00:38:14 +0800 Subject: [PATCH 1/2] test: assert line numbers of evaluated source #6089 dropped the coding cookie prepended to py::eval/py::exec source, which had shifted reported SyntaxError and traceback line numbers by one, and noted that no test asserted on them. Add one: errors on line 2 of the evaluated source must report line 2. With pybind11's own test target the new test reports (3, 3) against the parent of #6089 and (2, 2) with the fix. Co-Authored-By: Claude Fable 5.1 --- tests/test_eval.cpp | 24 ++++++++++++++++++++++++ tests/test_eval.py | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/tests/test_eval.cpp b/tests/test_eval.cpp index cd2903f0ab..e0ed86ef47 100644 --- a/tests/test_eval.cpp +++ b/tests/test_eval.cpp @@ -87,6 +87,30 @@ TEST_SUBMODULE(eval_, m) { return false; }); + // Line numbers reported from evaluated source must match the source as given; + // before #6089 a prepended coding cookie shifted them by one. + m.def("test_eval_line_numbers", []() { + int syntax_lineno = -1; + try { + py::exec("x = 1\nx = = 2\n"); + throw std::runtime_error("py::exec did not raise SyntaxError"); + } catch (py::error_already_set &e) { + syntax_lineno = e.value().attr("lineno").cast(); + } + int traceback_lineno = -1; + try { + py::exec("x = 1\nraise RuntimeError('line two')\n"); + throw std::runtime_error("py::exec did not raise RuntimeError"); + } catch (py::error_already_set &e) { + py::object tb = e.trace(); + for (py::object next = tb.attr("tb_next"); !next.is_none(); next = tb.attr("tb_next")) { + tb = std::move(next); + } + traceback_lineno = tb.attr("tb_lineno").cast(); + } + return py::make_tuple(syntax_lineno, traceback_lineno); + }); + // test_eval_empty_globals m.def("eval_empty_globals", [](py::object global) { if (global.is_none()) { diff --git a/tests/test_eval.py b/tests/test_eval.py index 8ac1907c7a..db6cec8e95 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -27,6 +27,10 @@ def test_eval_file(): assert m.test_eval_file_failure() +def test_eval_line_numbers(): + assert m.test_eval_line_numbers() == (2, 2) + + def test_eval_empty_globals(): assert "__builtins__" in m.eval_empty_globals(None) From 6ca057410c7dc308cb90f1bbf55ecb66fd7b7c9e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:38:45 +0000 Subject: [PATCH 2/2] style: pre-commit fixes --- tests/test_eval.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_eval.cpp b/tests/test_eval.cpp index e0ed86ef47..6fdf7a81a5 100644 --- a/tests/test_eval.cpp +++ b/tests/test_eval.cpp @@ -103,7 +103,8 @@ TEST_SUBMODULE(eval_, m) { throw std::runtime_error("py::exec did not raise RuntimeError"); } catch (py::error_already_set &e) { py::object tb = e.trace(); - for (py::object next = tb.attr("tb_next"); !next.is_none(); next = tb.attr("tb_next")) { + for (py::object next = tb.attr("tb_next"); !next.is_none(); + next = tb.attr("tb_next")) { tb = std::move(next); } traceback_lineno = tb.attr("tb_lineno").cast();