@@ -66,7 +66,6 @@ struct PythonCallInDestructor {
66
66
};
67
67
68
68
69
-
70
69
struct PythonAlreadySetInDestructor {
71
70
PythonAlreadySetInDestructor (const py::str &s) : s(s) {}
72
71
~PythonAlreadySetInDestructor () {
@@ -84,7 +83,30 @@ struct PythonAlreadySetInDestructor {
84
83
};
85
84
86
85
86
+ // An Exception that is going to be bound as a py:class_
87
+ class BoundException : public std ::exception {
88
+ public:
89
+ BoundException (const std::string& m, int e) : message{m}, m_errorCode(e) {}
90
+ virtual const char * what () const noexcept override {return message.c_str ();}
91
+ int errorCode () const noexcept { return m_errorCode;}
92
+ private:
93
+ std::string message;
94
+ int m_errorCode;
95
+ };
96
+
97
+
98
+
87
99
TEST_SUBMODULE (exceptions, m) {
100
+ // Provide a class binding for BoundException. This is providing the
101
+ // dynamic_attr because the parent type (PyExc_Exception) permits dynamic
102
+ // attributes. One improvement might be to make is_except force
103
+ // dynamic_attr
104
+ auto PyBoundException = py::class_< BoundException>(m, " BoundException" , py::is_except (), py::dynamic_attr ())
105
+ .def (py::init< std::string, int >())
106
+ .def (" getErrorCode" , &BoundException::errorCode)
107
+ .def (" getMessage" , &BoundException::what)
108
+ .def_property_readonly (" message" , &BoundException::what);
109
+
88
110
m.def (" throw_std_exception" , []() {
89
111
throw std::runtime_error (" This exception was intentionally thrown." );
90
112
});
@@ -128,14 +150,29 @@ TEST_SUBMODULE(exceptions, m) {
128
150
// A slightly more complicated one that declares MyException5_1 as a subclass of MyException5
129
151
py::register_exception<MyException5_1>(m, " MyException5_1" , ex5.ptr ());
130
152
153
+ // An exception translator for the exception that is bound as a class
154
+ // This is using PyErr_SetObject instead of PyErr_SetString.
155
+ py::register_exception_translator ([](std::exception_ptr p) {
156
+ try {
157
+ if (p) {
158
+ std::rethrow_exception (p);
159
+ }
160
+ } catch (const BoundException &e) {
161
+ auto err = py::cast (e);
162
+ auto errType = err.get_type ().ptr ();
163
+ PyErr_SetObject (errType, err.ptr ());
164
+ }
165
+ });
166
+
131
167
m.def (" throws1" , []() { throw MyException (" this error should go to a custom type" ); });
132
168
m.def (" throws2" , []() { throw MyException2 (" this error should go to a standard Python exception" ); });
133
169
m.def (" throws3" , []() { throw MyException3 (" this error cannot be translated" ); });
134
170
m.def (" throws4" , []() { throw MyException4 (" this error is rethrown" ); });
135
171
m.def (" throws5" , []() { throw MyException5 (" this is a helper-defined translated exception" ); });
136
172
m.def (" throws5_1" , []() { throw MyException5_1 (" MyException5 subclass" ); });
137
173
m.def (" throws_logic_error" , []() { throw std::logic_error (" this error should fall through to the standard handler" ); });
138
- m.def (" throws_overflow_error" , []() {throw std::overflow_error (" " ); });
174
+ m.def (" throws_overflow_error" , []() { throw std::overflow_error (" " ); });
175
+ m.def (" throws_bound_exception" , []() { throw BoundException (" this error is a class" , 42 ); });
139
176
m.def (" exception_matches" , []() {
140
177
py::dict foo;
141
178
try {
0 commit comments