mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 13:15:12 +00:00
135ba8deaf
This clears the Python error at the error_already_set throw site, thus allowing Python calls to be made in destructors which are triggered by the exception. This is preferable to the alternative, which would be guarding every Python API call with an error_scope. This effectively flips the behavior of error_already_set. Previously, it was assumed that the error stays in Python, so handling the exception in C++ would require explicitly calling PyErr_Clear(), but nothing was needed to propagate the error to Python. With this change, handling the error in C++ does not require a PyErr_Clear() call, but propagating the error to Python requires an explicit error_already_set::restore(). The change does not break old code which explicitly calls PyErr_Clear() for cleanup, which should be the majority of user code. The need for an explicit restore() call does break old code, but this should be mostly confined to the library and not user code.
52 lines
1.6 KiB
Python
52 lines
1.6 KiB
Python
import pytest
|
|
|
|
|
|
def test_error_already_set(msg):
|
|
from pybind11_tests import throw_already_set
|
|
|
|
with pytest.raises(RuntimeError) as excinfo:
|
|
throw_already_set(False)
|
|
assert msg(excinfo.value) == "Unknown internal error occurred"
|
|
|
|
with pytest.raises(ValueError) as excinfo:
|
|
throw_already_set(True)
|
|
assert msg(excinfo.value) == "foo"
|
|
|
|
|
|
def test_python_call_in_catch():
|
|
from pybind11_tests import python_call_in_destructor
|
|
|
|
d = {}
|
|
assert python_call_in_destructor(d) is True
|
|
assert d["good"] is True
|
|
|
|
|
|
def test_custom(msg):
|
|
from pybind11_tests import (MyException, throws1, throws2, throws3, throws4,
|
|
throws_logic_error)
|
|
|
|
# Can we catch a MyException?"
|
|
with pytest.raises(MyException) as excinfo:
|
|
throws1()
|
|
assert msg(excinfo.value) == "this error should go to a custom type"
|
|
|
|
# Can we translate to standard Python exceptions?
|
|
with pytest.raises(RuntimeError) as excinfo:
|
|
throws2()
|
|
assert msg(excinfo.value) == "this error should go to a standard Python exception"
|
|
|
|
# Can we handle unknown exceptions?
|
|
with pytest.raises(RuntimeError) as excinfo:
|
|
throws3()
|
|
assert msg(excinfo.value) == "Caught an unknown exception!"
|
|
|
|
# Can we delegate to another handler by rethrowing?
|
|
with pytest.raises(MyException) as excinfo:
|
|
throws4()
|
|
assert msg(excinfo.value) == "this error is rethrown"
|
|
|
|
# "Can we fall-through to the default handler?"
|
|
with pytest.raises(RuntimeError) as excinfo:
|
|
throws_logic_error()
|
|
assert msg(excinfo.value) == "this error should fall through to the standard handler"
|