mirror of
https://github.com/pocketpy/pocketpy
synced 2026-03-22 05:00:17 +00:00
Fix assertion failure in py_clearexc when Python function raises exception (fixes #469)
This commit is contained in:
parent
a2f16e5f1f
commit
02bd5c82a1
@ -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 pc = py_peek(0); // Save checkpoint BEFORE push
|
||||||
py_push(ptr());
|
py_push(ptr());
|
||||||
py_pushnil();
|
py_pushnil();
|
||||||
|
|
||||||
@ -108,7 +109,16 @@ object interface<Derived>::operator() (Args&&... args) const {
|
|||||||
|
|
||||||
(foreach(std::forward<Args>(args)), ...);
|
(foreach(std::forward<Args>(args)), ...);
|
||||||
|
|
||||||
raise_call<py_vectorcall>(argc, kwargsc);
|
// Don't use raise_call here - it saves checkpoint AFTER push, which causes
|
||||||
|
// an assertion failure in py_clearexc when py_vectorcall fails (because
|
||||||
|
// py_vectorcall pops 2+argc items on both success and failure).
|
||||||
|
// Instead, we save checkpoint BEFORE push and handle error explicitly.
|
||||||
|
if(!py_vectorcall(argc, kwargsc)) {
|
||||||
|
char* what = py_formatexc();
|
||||||
|
object e = object::from_ret();
|
||||||
|
py_clearexc(pc); // pc is now at or below current sp, assertion safe
|
||||||
|
throw python_error(what, std::move(e));
|
||||||
|
}
|
||||||
|
|
||||||
return object::from_ret();
|
return object::from_ret();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -410,4 +410,57 @@ TEST_F(PYBIND11_TEST, overload_cast) {
|
|||||||
EXPECT_EVAL_EQ("X().add(1, 2, 3)", 6);
|
EXPECT_EVAL_EQ("X().add(1, 2, 3)", 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test for GitHub issue: calling Python function that raises exception
|
||||||
|
// from C++ via py::object::operator() should not crash with assertion failure
|
||||||
|
// in py_clearexc. The bug was that raise_call saved the stack checkpoint
|
||||||
|
// AFTER pushing callable + nil + args, but py_vectorcall pops 2+argc items
|
||||||
|
// on both success and failure, causing the checkpoint to be below the
|
||||||
|
// current stack pointer when py_clearexc is called.
|
||||||
|
TEST_F(PYBIND11_TEST, vectorcall_exception) {
|
||||||
|
auto m = py::module::__main__();
|
||||||
|
|
||||||
|
// Define a Python function that raises an exception
|
||||||
|
py::exec(R"(
|
||||||
|
def raise_error(msg):
|
||||||
|
raise ValueError(msg)
|
||||||
|
)");
|
||||||
|
|
||||||
|
// Get the function object
|
||||||
|
py::object raise_error = m.attr("raise_error");
|
||||||
|
|
||||||
|
// Call the function from C++ - this should catch the exception
|
||||||
|
// without crashing due to assertion failure in py_clearexc
|
||||||
|
try {
|
||||||
|
raise_error("test error");
|
||||||
|
FAIL() << "Expected py::python_error to be thrown";
|
||||||
|
} catch (py::python_error& e) {
|
||||||
|
// Expected: we should catch the exception
|
||||||
|
EXPECT_TRUE(e.match(tp_ValueError));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with arguments
|
||||||
|
try {
|
||||||
|
py::str msg("error with arg");
|
||||||
|
raise_error(msg);
|
||||||
|
FAIL() << "Expected py::python_error to be thrown";
|
||||||
|
} catch (py::python_error& e) {
|
||||||
|
// Expected: we should catch the exception
|
||||||
|
EXPECT_TRUE(e.match(tp_ValueError));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test multiple arguments
|
||||||
|
py::exec(R"(
|
||||||
|
def raise_error2(a, b, c):
|
||||||
|
raise RuntimeError(f'{a}-{b}-{c}')
|
||||||
|
)");
|
||||||
|
py::object raise_error2 = m.attr("raise_error2");
|
||||||
|
|
||||||
|
try {
|
||||||
|
raise_error2(1, 2, 3);
|
||||||
|
FAIL() << "Expected py::python_error to be thrown";
|
||||||
|
} catch (py::python_error& e) {
|
||||||
|
EXPECT_TRUE(e.match(tp_RuntimeError));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user