@@ -18,46 +18,102 @@ Buffer buf(128);
1818
1919NAMESPACE_END (detail)
2020
21+ #if PY_VERSION_HEX >= 0x030C0000
2122python_error::python_error () {
22- PyErr_Fetch (&m_type, &m_value, &m_trace);
23+ m_value = PyErr_GetRaisedException ();
24+ check (m_value,
25+ " nanobind::python_error::python_error(): error indicator unset!" );
26+ }
27+
28+ python_error::~python_error () {
29+ if (m_value) {
30+ gil_scoped_acquire acq;
31+ /* With GIL held */ {
32+ // Clear error status in case the following executes Python code
33+ error_scope scope;
34+ Py_DECREF (m_value);
35+ }
36+ }
37+
38+ free (m_what);
39+ }
40+
41+ python_error::python_error (const python_error &e)
42+ : std::exception(e), m_value(e.m_value) {
43+ if (m_value) {
44+ gil_scoped_acquire acq;
45+ Py_INCREF (m_value);
46+ }
47+ if (e.m_what )
48+ m_what = NB_STRDUP (e.m_what );
49+ }
50+
51+ python_error::python_error (python_error &&e) noexcept
52+ : std::exception(e), m_value(e.m_value), m_what(e.m_what) {
53+ e.m_value = nullptr ;
54+ e.m_what = nullptr ;
55+ }
56+
57+ void python_error::restore () noexcept {
58+ check (m_value,
59+ " nanobind::python_error::restore(): error was already restored!" );
60+
61+ PyErr_SetRaisedException (m_value);
62+ m_value = nullptr ;
63+ }
64+
65+ #else /* Exception handling for Python 3.11 and older versions */
66+
67+ python_error::python_error () {
68+ PyErr_Fetch (&m_type, &m_value, &m_traceback);
2369 check (m_type,
2470 " nanobind::python_error::python_error(): error indicator unset!" );
2571}
2672
2773python_error::~python_error () {
28- if (m_type || m_value || m_trace ) {
74+ if (m_type) {
2975 gil_scoped_acquire acq;
3076 /* With GIL held */ {
3177 // Clear error status in case the following executes Python code
3278 error_scope scope;
33- Py_XDECREF (m_type);
3479 Py_XDECREF (m_value);
35- Py_XDECREF (m_trace);
80+ Py_XDECREF (m_type);
81+ Py_XDECREF (m_traceback);
3682 }
3783 }
3884 free (m_what);
3985}
4086
4187python_error::python_error (const python_error &e)
4288 : std::exception(e), m_type(e.m_type), m_value(e.m_value),
43- m_trace (e.m_trace ) {
44- if (m_type || m_value || m_trace ) {
89+ m_traceback (e.m_traceback ) {
90+ if (m_type) {
4591 gil_scoped_acquire acq;
46- Py_XINCREF (m_type);
92+ Py_INCREF (m_type);
4793 Py_XINCREF (m_value);
48- Py_XINCREF (m_trace );
94+ Py_XINCREF (m_traceback );
4995 }
5096 if (e.m_what )
5197 m_what = NB_STRDUP (e.m_what );
5298}
5399
54100python_error::python_error (python_error &&e) noexcept
55101 : std::exception(e), m_type(e.m_type), m_value(e.m_value),
56- m_trace (e.m_trace ), m_what(e.m_what) {
57- e.m_type = e.m_value = e.m_trace = nullptr ;
102+ m_traceback (e.m_traceback ), m_what(e.m_what) {
103+ e.m_type = e.m_value = e.m_traceback = nullptr ;
58104 e.m_what = nullptr ;
59105}
60106
107+ void python_error::restore () noexcept {
108+ check (m_type,
109+ " nanobind::python_error::restore(): error was already restored!" );
110+
111+ PyErr_Restore (m_type, m_value, m_traceback);
112+ m_type = m_value = m_traceback = nullptr ;
113+ }
114+
115+ #endif
116+
61117const char *python_error::what () const noexcept {
62118 using detail::buf;
63119
@@ -71,23 +127,30 @@ const char *python_error::what() const noexcept {
71127 if (m_what)
72128 return m_what;
73129
74- PyErr_NormalizeException (&m_type, &m_value, &m_trace);
130+ #if PY_VERSION_HEX < 0x030C0000
131+ PyErr_NormalizeException (&m_type, &m_value, &m_traceback);
75132 check (m_type,
76133 " nanobind::python_error::what(): PyNormalize_Exception() failed!" );
77134
78- if (m_trace ) {
79- if (PyException_SetTraceback (m_value, m_trace ) < 0 )
135+ if (m_traceback ) {
136+ if (PyException_SetTraceback (m_value, m_traceback ) < 0 )
80137 PyErr_Clear ();
81138 }
82139
140+ handle exc_type = m_type, exc_value = m_value;
141+ #else
142+ handle exc_value = m_value, exc_type = exc_value.type ();
143+ #endif
144+ object exc_traceback = traceback ();
145+
83146#if defined(Py_LIMITED_API) || defined(PYPY_VERSION)
84147 object mod = module_::import_ (" traceback" ),
85- result = mod.attr (" format_exception" )(handle (m_type), handle (m_value), handle (m_trace) );
148+ result = mod.attr (" format_exception" )(exc_type, exc_value, exc_traceback );
86149 m_what = NB_STRDUP (borrow<str>(str (" \n " ).attr (" join" )(result)).c_str ());
87150#else
88151 buf.clear ();
89- if (m_trace ) {
90- PyTracebackObject *to = (PyTracebackObject *) m_trace ;
152+ if (exc_traceback. is_valid () ) {
153+ PyTracebackObject *to = (PyTracebackObject *) exc_traceback. ptr () ;
91154
92155 // Get the deepest trace possible
93156 while (to->tb_next )
@@ -130,28 +193,20 @@ const char *python_error::what() const noexcept {
130193 }
131194 }
132195
133- if (m_type ) {
134- object name = handle (m_type) .attr (" __name__" );
196+ if (exc_type. is_valid () ) {
197+ object name = exc_type .attr (" __name__" );
135198 buf.put_dstr (borrow<str>(name).c_str ());
136199 buf.put (" : " );
137200 }
138201
139- if (m_value )
202+ if (exc_value. is_valid () )
140203 buf.put_dstr (str (m_value).c_str ());
141204 m_what = buf.copy ();
142205#endif
143206
144207 return m_what;
145208}
146209
147- void python_error::restore () noexcept {
148- check (m_type,
149- " nanobind::python_error::restore(): error was already restored!" );
150-
151- PyErr_Restore (m_type, m_value, m_trace);
152- m_type = m_value = m_trace = nullptr ;
153- }
154-
155210builtin_exception::builtin_exception (exception_type type, const char *what)
156211 : std::runtime_error(what ? what : " " ), m_type(type) { }
157212builtin_exception::~builtin_exception () { }
0 commit comments