1use crate::{
2 exceptions::{PyBaseException, PyTypeError},
3 ffi,
4 types::{PyTraceback, PyType},
5 Bound, IntoPy, Py, PyAny, PyObject, PyTypeInfo, Python,
6};
7
8pub(crate) struct PyErrStateNormalized {
9 #[cfg(not(Py_3_12))]
10 ptype: Py<PyType>,
11 pub pvalue: Py<PyBaseException>,
12 #[cfg(not(Py_3_12))]
13 ptraceback: Option<Py<PyTraceback>>,
14}
15
16impl PyErrStateNormalized {
17 #[cfg(not(Py_3_12))]
18 pub(crate) fn ptype<'py>(&self, py: Python<'py>) -> Bound<'py, PyType> {
19 self.ptype.bind(py).clone()
20 }
21
22 #[cfg(Py_3_12)]
23 pub(crate) fn ptype<'py>(&self, py: Python<'py>) -> Bound<'py, PyType> {
24 use crate::types::any::PyAnyMethods;
25 self.pvalue.bind(py).get_type()
26 }
27
28 #[cfg(not(Py_3_12))]
29 pub(crate) fn ptraceback<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTraceback>> {
30 self.ptraceback
31 .as_ref()
32 .map(|traceback| traceback.bind(py).clone())
33 }
34
35 #[cfg(Py_3_12)]
36 pub(crate) fn ptraceback<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTraceback>> {
37 use crate::ffi_ptr_ext::FfiPtrExt;
38 use crate::types::any::PyAnyMethods;
39 unsafe {
40 ffi::PyException_GetTraceback(self.pvalue.as_ptr())
41 .assume_owned_or_opt(py)
42 .map(|b| b.downcast_into_unchecked())
43 }
44 }
45
46 #[cfg(Py_3_12)]
47 pub(crate) fn take(py: Python<'_>) -> Option<PyErrStateNormalized> {
48 unsafe { Py::from_owned_ptr_or_opt(py, ffi::PyErr_GetRaisedException()) }
49 .map(|pvalue| PyErrStateNormalized { pvalue })
50 }
51
52 #[cfg(not(Py_3_12))]
53 unsafe fn from_normalized_ffi_tuple(
54 py: Python<'_>,
55 ptype: *mut ffi::PyObject,
56 pvalue: *mut ffi::PyObject,
57 ptraceback: *mut ffi::PyObject,
58 ) -> Self {
59 PyErrStateNormalized {
60 ptype: Py::from_owned_ptr_or_opt(py, ptype).expect("Exception type missing"),
61 pvalue: Py::from_owned_ptr_or_opt(py, pvalue).expect("Exception value missing"),
62 ptraceback: Py::from_owned_ptr_or_opt(py, ptraceback),
63 }
64 }
65
66 pub fn clone_ref(&self, py: Python<'_>) -> Self {
67 Self {
68 #[cfg(not(Py_3_12))]
69 ptype: self.ptype.clone_ref(py),
70 pvalue: self.pvalue.clone_ref(py),
71 #[cfg(not(Py_3_12))]
72 ptraceback: self
73 .ptraceback
74 .as_ref()
75 .map(|ptraceback| ptraceback.clone_ref(py)),
76 }
77 }
78}
79
80pub(crate) struct PyErrStateLazyFnOutput {
81 pub(crate) ptype: PyObject,
82 pub(crate) pvalue: PyObject,
83}
84
85pub(crate) type PyErrStateLazyFn =
86 dyn for<'py> FnOnce(Python<'py>) -> PyErrStateLazyFnOutput + Send + Sync;
87
88pub(crate) enum PyErrState {
89 Lazy(Box<PyErrStateLazyFn>),
90 #[cfg(not(Py_3_12))]
91 FfiTuple {
92 ptype: PyObject,
93 pvalue: Option<PyObject>,
94 ptraceback: Option<PyObject>,
95 },
96 Normalized(PyErrStateNormalized),
97}
98
99pub trait PyErrArguments: Send + Sync {
101 fn arguments(self, py: Python<'_>) -> PyObject;
103}
104
105impl<T> PyErrArguments for T
106where
107 T: IntoPy<PyObject> + Send + Sync,
108{
109 fn arguments(self, py: Python<'_>) -> PyObject {
110 self.into_py(py)
111 }
112}
113
114impl PyErrState {
115 pub(crate) fn lazy(ptype: Py<PyAny>, args: impl PyErrArguments + 'static) -> Self {
116 PyErrState::Lazy(Box::new(move |py| PyErrStateLazyFnOutput {
117 ptype,
118 pvalue: args.arguments(py),
119 }))
120 }
121
122 pub(crate) fn normalized(pvalue: Bound<'_, PyBaseException>) -> Self {
123 #[cfg(not(Py_3_12))]
124 use crate::types::any::PyAnyMethods;
125
126 Self::Normalized(PyErrStateNormalized {
127 #[cfg(not(Py_3_12))]
128 ptype: pvalue.get_type().into(),
129 #[cfg(not(Py_3_12))]
130 ptraceback: unsafe {
131 Py::from_owned_ptr_or_opt(
132 pvalue.py(),
133 ffi::PyException_GetTraceback(pvalue.as_ptr()),
134 )
135 },
136 pvalue: pvalue.into(),
137 })
138 }
139
140 pub(crate) fn normalize(self, py: Python<'_>) -> PyErrStateNormalized {
141 match self {
142 #[cfg(not(Py_3_12))]
143 PyErrState::Lazy(lazy) => {
144 let (ptype, pvalue, ptraceback) = lazy_into_normalized_ffi_tuple(py, lazy);
145 unsafe {
146 PyErrStateNormalized::from_normalized_ffi_tuple(py, ptype, pvalue, ptraceback)
147 }
148 }
149 #[cfg(Py_3_12)]
150 PyErrState::Lazy(lazy) => {
151 raise_lazy(py, lazy);
154 PyErrStateNormalized::take(py)
155 .expect("exception missing after writing to the interpreter")
156 }
157 #[cfg(not(Py_3_12))]
158 PyErrState::FfiTuple {
159 ptype,
160 pvalue,
161 ptraceback,
162 } => {
163 let mut ptype = ptype.into_ptr();
164 let mut pvalue = pvalue.map_or(std::ptr::null_mut(), Py::into_ptr);
165 let mut ptraceback = ptraceback.map_or(std::ptr::null_mut(), Py::into_ptr);
166 unsafe {
167 ffi::PyErr_NormalizeException(&mut ptype, &mut pvalue, &mut ptraceback);
168 PyErrStateNormalized::from_normalized_ffi_tuple(py, ptype, pvalue, ptraceback)
169 }
170 }
171 PyErrState::Normalized(normalized) => normalized,
172 }
173 }
174
175 #[cfg(not(Py_3_12))]
176 pub(crate) fn restore(self, py: Python<'_>) {
177 let (ptype, pvalue, ptraceback) = match self {
178 PyErrState::Lazy(lazy) => lazy_into_normalized_ffi_tuple(py, lazy),
179 PyErrState::FfiTuple {
180 ptype,
181 pvalue,
182 ptraceback,
183 } => (
184 ptype.into_ptr(),
185 pvalue.map_or(std::ptr::null_mut(), Py::into_ptr),
186 ptraceback.map_or(std::ptr::null_mut(), Py::into_ptr),
187 ),
188 PyErrState::Normalized(PyErrStateNormalized {
189 ptype,
190 pvalue,
191 ptraceback,
192 }) => (
193 ptype.into_ptr(),
194 pvalue.into_ptr(),
195 ptraceback.map_or(std::ptr::null_mut(), Py::into_ptr),
196 ),
197 };
198 unsafe { ffi::PyErr_Restore(ptype, pvalue, ptraceback) }
199 }
200
201 #[cfg(Py_3_12)]
202 pub(crate) fn restore(self, py: Python<'_>) {
203 match self {
204 PyErrState::Lazy(lazy) => raise_lazy(py, lazy),
205 PyErrState::Normalized(PyErrStateNormalized { pvalue }) => unsafe {
206 ffi::PyErr_SetRaisedException(pvalue.into_ptr())
207 },
208 }
209 }
210}
211
212#[cfg(not(Py_3_12))]
213fn lazy_into_normalized_ffi_tuple(
214 py: Python<'_>,
215 lazy: Box<PyErrStateLazyFn>,
216) -> (*mut ffi::PyObject, *mut ffi::PyObject, *mut ffi::PyObject) {
217 raise_lazy(py, lazy);
220 let mut ptype = std::ptr::null_mut();
221 let mut pvalue = std::ptr::null_mut();
222 let mut ptraceback = std::ptr::null_mut();
223 unsafe {
224 ffi::PyErr_Fetch(&mut ptype, &mut pvalue, &mut ptraceback);
225 ffi::PyErr_NormalizeException(&mut ptype, &mut pvalue, &mut ptraceback);
226 }
227 (ptype, pvalue, ptraceback)
228}
229
230fn raise_lazy(py: Python<'_>, lazy: Box<PyErrStateLazyFn>) {
238 let PyErrStateLazyFnOutput { ptype, pvalue } = lazy(py);
239 unsafe {
240 if ffi::PyExceptionClass_Check(ptype.as_ptr()) == 0 {
241 ffi::PyErr_SetString(
242 PyTypeError::type_object_raw(py).cast(),
243 ffi::c_str!("exceptions must derive from BaseException").as_ptr(),
244 )
245 } else {
246 ffi::PyErr_SetObject(ptype.as_ptr(), pvalue.as_ptr())
247 }
248 }
249}