Compare commits

...

2 Commits

Author SHA1 Message Date
Deepanjali Pathak
abb7e3ed80
Merge 4578d64a9b5db9132c6368449bbab1024ebfd54b into 0676b21da2827b827ee7cac89e9b27c4aa3c005e 2026-03-20 16:59:32 +05:30
Jason Matthew Suhari
0676b21da2
fix: save stack checkpoint before pushing args in operator() (#479)
* fix: capture stack checkpoint before pushing args in operator() (#469)

* test: add regression test for operator() python error propagation (#469)
2026-03-20 13:22:25 +08:00
2 changed files with 23 additions and 1 deletions

View File

@ -70,6 +70,7 @@ args_proxy interface<Derived>::operator* () const {
template <typename Derived> template <typename Derived>
template <return_value_policy policy, typename... Args> template <return_value_policy policy, typename... Args>
object interface<Derived>::operator() (Args&&... args) const { object interface<Derived>::operator() (Args&&... args) const {
py_StackRef p0 = py_peek(0); // checkpoint before pushing so py_clearexc can safely rewind
py_push(ptr()); py_push(ptr());
py_pushnil(); py_pushnil();
@ -108,7 +109,13 @@ object interface<Derived>::operator() (Args&&... args) const {
(foreach(std::forward<Args>(args)), ...); (foreach(std::forward<Args>(args)), ...);
raise_call<py_vectorcall>(argc, kwargsc); if(!py_vectorcall(argc, kwargsc)) {
py_matchexc(tp_Exception);
object e = object::from_ret();
auto what = py_formatexc();
py_clearexc(p0);
throw python_error(what, std::move(e));
}
return object::from_ret(); return object::from_ret();
} }

View File

@ -79,3 +79,18 @@ TEST_F(PYBIND11_TEST, exception_cpp_to_python) {
TEST_EXCEPTION(attribute_error, AttributeError); TEST_EXCEPTION(attribute_error, AttributeError);
TEST_EXCEPTION(runtime_error, RuntimeError); TEST_EXCEPTION(runtime_error, RuntimeError);
} }
// Regression test: operator() must throw python_error instead of crashing when Python raises (#469)
TEST_F(PYBIND11_TEST, operator_call_propagates_python_error) {
py::exec("def f(x):\n raise ValueError('intentional error')");
py::object fn = py::eval("f");
bool caught = false;
try {
fn(py::int_(1));
} catch(py::python_error& e) {
caught = true;
EXPECT_TRUE(e.match(tp_ValueError));
}
EXPECT_TRUE(caught);
}