test_exceptions.py revision 12391:ceeca8b41e4b
1import pytest
2
3from pybind11_tests import exceptions as m
4import pybind11_cross_module_tests as cm
5
6
7def test_std_exception(msg):
8    with pytest.raises(RuntimeError) as excinfo:
9        m.throw_std_exception()
10    assert msg(excinfo.value) == "This exception was intentionally thrown."
11
12
13def test_error_already_set(msg):
14    with pytest.raises(RuntimeError) as excinfo:
15        m.throw_already_set(False)
16    assert msg(excinfo.value) == "Unknown internal error occurred"
17
18    with pytest.raises(ValueError) as excinfo:
19        m.throw_already_set(True)
20    assert msg(excinfo.value) == "foo"
21
22
23def test_cross_module_exceptions():
24    with pytest.raises(RuntimeError) as excinfo:
25        cm.raise_runtime_error()
26    assert str(excinfo.value) == "My runtime error"
27
28    with pytest.raises(ValueError) as excinfo:
29        cm.raise_value_error()
30    assert str(excinfo.value) == "My value error"
31
32    with pytest.raises(ValueError) as excinfo:
33        cm.throw_pybind_value_error()
34    assert str(excinfo.value) == "pybind11 value error"
35
36    with pytest.raises(TypeError) as excinfo:
37        cm.throw_pybind_type_error()
38    assert str(excinfo.value) == "pybind11 type error"
39
40    with pytest.raises(StopIteration) as excinfo:
41        cm.throw_stop_iteration()
42
43
44def test_python_call_in_catch():
45    d = {}
46    assert m.python_call_in_destructor(d) is True
47    assert d["good"] is True
48
49
50def test_exception_matches():
51    m.exception_matches()
52
53
54def test_custom(msg):
55    # Can we catch a MyException?
56    with pytest.raises(m.MyException) as excinfo:
57        m.throws1()
58    assert msg(excinfo.value) == "this error should go to a custom type"
59
60    # Can we translate to standard Python exceptions?
61    with pytest.raises(RuntimeError) as excinfo:
62        m.throws2()
63    assert msg(excinfo.value) == "this error should go to a standard Python exception"
64
65    # Can we handle unknown exceptions?
66    with pytest.raises(RuntimeError) as excinfo:
67        m.throws3()
68    assert msg(excinfo.value) == "Caught an unknown exception!"
69
70    # Can we delegate to another handler by rethrowing?
71    with pytest.raises(m.MyException) as excinfo:
72        m.throws4()
73    assert msg(excinfo.value) == "this error is rethrown"
74
75    # Can we fall-through to the default handler?
76    with pytest.raises(RuntimeError) as excinfo:
77        m.throws_logic_error()
78    assert msg(excinfo.value) == "this error should fall through to the standard handler"
79
80    # Can we handle a helper-declared exception?
81    with pytest.raises(m.MyException5) as excinfo:
82        m.throws5()
83    assert msg(excinfo.value) == "this is a helper-defined translated exception"
84
85    # Exception subclassing:
86    with pytest.raises(m.MyException5) as excinfo:
87        m.throws5_1()
88    assert msg(excinfo.value) == "MyException5 subclass"
89    assert isinstance(excinfo.value, m.MyException5_1)
90
91    with pytest.raises(m.MyException5_1) as excinfo:
92        m.throws5_1()
93    assert msg(excinfo.value) == "MyException5 subclass"
94
95    with pytest.raises(m.MyException5) as excinfo:
96        try:
97            m.throws5()
98        except m.MyException5_1:
99            raise RuntimeError("Exception error: caught child from parent")
100    assert msg(excinfo.value) == "this is a helper-defined translated exception"
101
102
103def test_nested_throws(capture):
104    """Tests nested (e.g. C++ -> Python -> C++) exception handling"""
105
106    def throw_myex():
107        raise m.MyException("nested error")
108
109    def throw_myex5():
110        raise m.MyException5("nested error 5")
111
112    # In the comments below, the exception is caught in the first step, thrown in the last step
113
114    # C++ -> Python
115    with capture:
116        m.try_catch(m.MyException5, throw_myex5)
117    assert str(capture).startswith("MyException5: nested error 5")
118
119    # Python -> C++ -> Python
120    with pytest.raises(m.MyException) as excinfo:
121        m.try_catch(m.MyException5, throw_myex)
122    assert str(excinfo.value) == "nested error"
123
124    def pycatch(exctype, f, *args):
125        try:
126            f(*args)
127        except m.MyException as e:
128            print(e)
129
130    # C++ -> Python -> C++ -> Python
131    with capture:
132        m.try_catch(
133            m.MyException5, pycatch, m.MyException, m.try_catch, m.MyException, throw_myex5)
134    assert str(capture).startswith("MyException5: nested error 5")
135
136    # C++ -> Python -> C++
137    with capture:
138        m.try_catch(m.MyException, pycatch, m.MyException5, m.throws4)
139    assert capture == "this error is rethrown"
140
141    # Python -> C++ -> Python -> C++
142    with pytest.raises(m.MyException5) as excinfo:
143        m.try_catch(m.MyException, pycatch, m.MyException, m.throws5)
144    assert str(excinfo.value) == "this is a helper-defined translated exception"
145