pyo3/err/
mod.rs

1use crate::instance::Bound;
2use crate::panic::PanicException;
3use crate::type_object::PyTypeInfo;
4use crate::types::any::PyAnyMethods;
5use crate::types::{
6    string::PyStringMethods, traceback::PyTracebackMethods, typeobject::PyTypeMethods, PyTraceback,
7    PyType,
8};
9use crate::{
10    exceptions::{self, PyBaseException},
11    ffi,
12};
13use crate::{Borrowed, BoundObject, Py, PyAny, PyObject, Python};
14use std::borrow::Cow;
15use std::ffi::CStr;
16
17mod err_state;
18mod impls;
19
20use crate::conversion::IntoPyObject;
21use err_state::{PyErrState, PyErrStateLazyFnOutput, PyErrStateNormalized};
22use std::convert::Infallible;
23use std::ptr;
24
25/// Represents a Python exception.
26///
27/// To avoid needing access to [`Python`] in `Into` conversions to create `PyErr` (thus improving
28/// compatibility with `?` and other Rust errors) this type supports creating exceptions instances
29/// in a lazy fashion, where the full Python object for the exception is created only when needed.
30///
31/// Accessing the contained exception in any way, such as with [`value`](PyErr::value),
32/// [`get_type`](PyErr::get_type), or [`is_instance`](PyErr::is_instance)
33/// will create the full exception object if it was not already created.
34pub struct PyErr {
35    state: PyErrState,
36}
37
38// The inner value is only accessed through ways that require proving the gil is held
39#[cfg(feature = "nightly")]
40unsafe impl crate::marker::Ungil for PyErr {}
41
42/// Represents the result of a Python call.
43pub type PyResult<T> = Result<T, PyErr>;
44
45/// Error that indicates a failure to convert a PyAny to a more specific Python type.
46#[derive(Debug)]
47pub struct DowncastError<'a, 'py> {
48    from: Borrowed<'a, 'py, PyAny>,
49    to: Cow<'static, str>,
50}
51
52impl<'a, 'py> DowncastError<'a, 'py> {
53    /// Create a new `PyDowncastError` representing a failure to convert the object
54    /// `from` into the type named in `to`.
55    pub fn new(from: &'a Bound<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self {
56        DowncastError {
57            from: from.as_borrowed(),
58            to: to.into(),
59        }
60    }
61    pub(crate) fn new_from_borrowed(
62        from: Borrowed<'a, 'py, PyAny>,
63        to: impl Into<Cow<'static, str>>,
64    ) -> Self {
65        DowncastError {
66            from,
67            to: to.into(),
68        }
69    }
70}
71
72/// Error that indicates a failure to convert a PyAny to a more specific Python type.
73#[derive(Debug)]
74pub struct DowncastIntoError<'py> {
75    from: Bound<'py, PyAny>,
76    to: Cow<'static, str>,
77}
78
79impl<'py> DowncastIntoError<'py> {
80    /// Create a new `DowncastIntoError` representing a failure to convert the object
81    /// `from` into the type named in `to`.
82    pub fn new(from: Bound<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self {
83        DowncastIntoError {
84            from,
85            to: to.into(),
86        }
87    }
88
89    /// Consumes this `DowncastIntoError` and returns the original object, allowing continued
90    /// use of it after a failed conversion.
91    ///
92    /// See [`downcast_into`][PyAnyMethods::downcast_into] for an example.
93    pub fn into_inner(self) -> Bound<'py, PyAny> {
94        self.from
95    }
96}
97
98/// Helper conversion trait that allows to use custom arguments for lazy exception construction.
99pub trait PyErrArguments: Send + Sync {
100    /// Arguments for exception
101    fn arguments(self, py: Python<'_>) -> PyObject;
102}
103
104impl<T> PyErrArguments for T
105where
106    T: for<'py> IntoPyObject<'py> + Send + Sync,
107{
108    fn arguments(self, py: Python<'_>) -> PyObject {
109        // FIXME: `arguments` should become fallible
110        match self.into_pyobject(py) {
111            Ok(obj) => obj.into_any().unbind(),
112            Err(e) => panic!("Converting PyErr arguments failed: {}", e.into()),
113        }
114    }
115}
116
117impl PyErr {
118    /// Creates a new PyErr of type `T`.
119    ///
120    /// `args` can be:
121    /// * a tuple: the exception instance will be created using the equivalent to the Python
122    ///   expression `T(*tuple)`
123    /// * any other value: the exception instance will be created using the equivalent to the Python
124    ///   expression `T(value)`
125    ///
126    /// This exception instance will be initialized lazily. This avoids the need for the Python GIL
127    /// to be held, but requires `args` to be `Send` and `Sync`. If `args` is not `Send` or `Sync`,
128    /// consider using [`PyErr::from_value`] instead.
129    ///
130    /// If `T` does not inherit from `BaseException`, then a `TypeError` will be returned.
131    ///
132    /// If calling T's constructor with `args` raises an exception, that exception will be returned.
133    ///
134    /// # Examples
135    ///
136    /// ```
137    /// use pyo3::prelude::*;
138    /// use pyo3::exceptions::PyTypeError;
139    ///
140    /// #[pyfunction]
141    /// fn always_throws() -> PyResult<()> {
142    ///     Err(PyErr::new::<PyTypeError, _>("Error message"))
143    /// }
144    /// #
145    /// # Python::with_gil(|py| {
146    /// #     let fun = pyo3::wrap_pyfunction!(always_throws, py).unwrap();
147    /// #     let err = fun.call0().expect_err("called a function that should always return an error but the return value was Ok");
148    /// #     assert!(err.is_instance_of::<PyTypeError>(py))
149    /// # });
150    /// ```
151    ///
152    /// In most cases, you can use a concrete exception's constructor instead:
153    ///
154    /// ```
155    /// use pyo3::prelude::*;
156    /// use pyo3::exceptions::PyTypeError;
157    ///
158    /// #[pyfunction]
159    /// fn always_throws() -> PyResult<()> {
160    ///     Err(PyTypeError::new_err("Error message"))
161    /// }
162    /// #
163    /// # Python::with_gil(|py| {
164    /// #     let fun = pyo3::wrap_pyfunction!(always_throws, py).unwrap();
165    /// #     let err = fun.call0().expect_err("called a function that should always return an error but the return value was Ok");
166    /// #     assert!(err.is_instance_of::<PyTypeError>(py))
167    /// # });
168    /// ```
169    #[inline]
170    pub fn new<T, A>(args: A) -> PyErr
171    where
172        T: PyTypeInfo,
173        A: PyErrArguments + Send + Sync + 'static,
174    {
175        PyErr::from_state(PyErrState::lazy(Box::new(move |py| {
176            PyErrStateLazyFnOutput {
177                ptype: T::type_object(py).into(),
178                pvalue: args.arguments(py),
179            }
180        })))
181    }
182
183    /// Constructs a new PyErr from the given Python type and arguments.
184    ///
185    /// `ty` is the exception type; usually one of the standard exceptions
186    /// like `exceptions::PyRuntimeError`.
187    ///
188    /// `args` is either a tuple or a single value, with the same meaning as in [`PyErr::new`].
189    ///
190    /// If `ty` does not inherit from `BaseException`, then a `TypeError` will be returned.
191    ///
192    /// If calling `ty` with `args` raises an exception, that exception will be returned.
193    pub fn from_type<A>(ty: Bound<'_, PyType>, args: A) -> PyErr
194    where
195        A: PyErrArguments + Send + Sync + 'static,
196    {
197        PyErr::from_state(PyErrState::lazy_arguments(ty.unbind().into_any(), args))
198    }
199
200    /// Creates a new PyErr.
201    ///
202    /// If `obj` is a Python exception object, the PyErr will contain that object.
203    ///
204    /// If `obj` is a Python exception type object, this is equivalent to `PyErr::from_type(obj, ())`.
205    ///
206    /// Otherwise, a `TypeError` is created.
207    ///
208    /// # Examples
209    /// ```rust
210    /// use pyo3::prelude::*;
211    /// use pyo3::PyTypeInfo;
212    /// use pyo3::exceptions::PyTypeError;
213    /// use pyo3::types::PyString;
214    ///
215    /// Python::with_gil(|py| {
216    ///     // Case #1: Exception object
217    ///     let err = PyErr::from_value(PyTypeError::new_err("some type error")
218    ///         .value(py).clone().into_any());
219    ///     assert_eq!(err.to_string(), "TypeError: some type error");
220    ///
221    ///     // Case #2: Exception type
222    ///     let err = PyErr::from_value(PyTypeError::type_object(py).into_any());
223    ///     assert_eq!(err.to_string(), "TypeError: ");
224    ///
225    ///     // Case #3: Invalid exception value
226    ///     let err = PyErr::from_value(PyString::new(py, "foo").into_any());
227    ///     assert_eq!(
228    ///         err.to_string(),
229    ///         "TypeError: exceptions must derive from BaseException"
230    ///     );
231    /// });
232    /// ```
233    pub fn from_value(obj: Bound<'_, PyAny>) -> PyErr {
234        let state = match obj.downcast_into::<PyBaseException>() {
235            Ok(obj) => PyErrState::normalized(PyErrStateNormalized::new(obj)),
236            Err(err) => {
237                // Assume obj is Type[Exception]; let later normalization handle if this
238                // is not the case
239                let obj = err.into_inner();
240                let py = obj.py();
241                PyErrState::lazy_arguments(obj.unbind(), py.None())
242            }
243        };
244
245        PyErr::from_state(state)
246    }
247
248    /// Returns the type of this exception.
249    ///
250    /// # Examples
251    /// ```rust
252    /// use pyo3::{prelude::*, exceptions::PyTypeError, types::PyType};
253    ///
254    /// Python::with_gil(|py| {
255    ///     let err: PyErr = PyTypeError::new_err(("some type error",));
256    ///     assert!(err.get_type(py).is(&PyType::new::<PyTypeError>(py)));
257    /// });
258    /// ```
259    pub fn get_type<'py>(&self, py: Python<'py>) -> Bound<'py, PyType> {
260        self.normalized(py).ptype(py)
261    }
262
263    /// Returns the value of this exception.
264    ///
265    /// # Examples
266    ///
267    /// ```rust
268    /// use pyo3::{exceptions::PyTypeError, PyErr, Python};
269    ///
270    /// Python::with_gil(|py| {
271    ///     let err: PyErr = PyTypeError::new_err(("some type error",));
272    ///     assert!(err.is_instance_of::<PyTypeError>(py));
273    ///     assert_eq!(err.value(py).to_string(), "some type error");
274    /// });
275    /// ```
276    pub fn value<'py>(&self, py: Python<'py>) -> &Bound<'py, PyBaseException> {
277        self.normalized(py).pvalue.bind(py)
278    }
279
280    /// Consumes self to take ownership of the exception value contained in this error.
281    pub fn into_value(self, py: Python<'_>) -> Py<PyBaseException> {
282        // NB technically this causes one reference count increase and decrease in quick succession
283        // on pvalue, but it's probably not worth optimizing this right now for the additional code
284        // complexity.
285        let normalized = self.normalized(py);
286        let exc = normalized.pvalue.clone_ref(py);
287        if let Some(tb) = normalized.ptraceback(py) {
288            unsafe {
289                ffi::PyException_SetTraceback(exc.as_ptr(), tb.as_ptr());
290            }
291        }
292        exc
293    }
294
295    /// Returns the traceback of this exception object.
296    ///
297    /// # Examples
298    /// ```rust
299    /// use pyo3::{exceptions::PyTypeError, Python};
300    ///
301    /// Python::with_gil(|py| {
302    ///     let err = PyTypeError::new_err(("some type error",));
303    ///     assert!(err.traceback(py).is_none());
304    /// });
305    /// ```
306    pub fn traceback<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTraceback>> {
307        self.normalized(py).ptraceback(py)
308    }
309
310    /// Gets whether an error is present in the Python interpreter's global state.
311    #[inline]
312    pub fn occurred(_: Python<'_>) -> bool {
313        unsafe { !ffi::PyErr_Occurred().is_null() }
314    }
315
316    /// Takes the current error from the Python interpreter's global state and clears the global
317    /// state. If no error is set, returns `None`.
318    ///
319    /// If the error is a `PanicException` (which would have originated from a panic in a pyo3
320    /// callback) then this function will resume the panic.
321    ///
322    /// Use this function when it is not known if an error should be present. If the error is
323    /// expected to have been set, for example from [`PyErr::occurred`] or by an error return value
324    /// from a C FFI function, use [`PyErr::fetch`].
325    pub fn take(py: Python<'_>) -> Option<PyErr> {
326        let state = PyErrStateNormalized::take(py)?;
327        let pvalue = state.pvalue.bind(py);
328        if ptr::eq(
329            pvalue.get_type().as_ptr(),
330            PanicException::type_object_raw(py).cast(),
331        ) {
332            let msg: String = pvalue
333                .str()
334                .map(|py_str| py_str.to_string_lossy().into())
335                .unwrap_or_else(|_| String::from("Unwrapped panic from Python code"));
336            Self::print_panic_and_unwind(py, PyErrState::normalized(state), msg)
337        }
338
339        Some(PyErr::from_state(PyErrState::normalized(state)))
340    }
341
342    fn print_panic_and_unwind(py: Python<'_>, state: PyErrState, msg: String) -> ! {
343        eprintln!("--- PyO3 is resuming a panic after fetching a PanicException from Python. ---");
344        eprintln!("Python stack trace below:");
345
346        state.restore(py);
347
348        unsafe {
349            ffi::PyErr_PrintEx(0);
350        }
351
352        std::panic::resume_unwind(Box::new(msg))
353    }
354
355    /// Equivalent to [PyErr::take], but when no error is set:
356    ///  - Panics in debug mode.
357    ///  - Returns a `SystemError` in release mode.
358    ///
359    /// This behavior is consistent with Python's internal handling of what happens when a C return
360    /// value indicates an error occurred but the global error state is empty. (A lack of exception
361    /// should be treated as a bug in the code which returned an error code but did not set an
362    /// exception.)
363    ///
364    /// Use this function when the error is expected to have been set, for example from
365    /// [PyErr::occurred] or by an error return value from a C FFI function.
366    #[cfg_attr(debug_assertions, track_caller)]
367    #[inline]
368    pub fn fetch(py: Python<'_>) -> PyErr {
369        const FAILED_TO_FETCH: &str = "attempted to fetch exception but none was set";
370        match PyErr::take(py) {
371            Some(err) => err,
372            #[cfg(debug_assertions)]
373            None => panic!("{}", FAILED_TO_FETCH),
374            #[cfg(not(debug_assertions))]
375            None => exceptions::PySystemError::new_err(FAILED_TO_FETCH),
376        }
377    }
378
379    /// Creates a new exception type with the given name and docstring.
380    ///
381    /// - `base` can be an existing exception type to subclass, or a tuple of classes.
382    /// - `dict` specifies an optional dictionary of class variables and methods.
383    /// - `doc` will be the docstring seen by python users.
384    ///
385    ///
386    /// # Errors
387    ///
388    /// This function returns an error if `name` is not of the form `<module>.<ExceptionName>`.
389    pub fn new_type<'py>(
390        py: Python<'py>,
391        name: &CStr,
392        doc: Option<&CStr>,
393        base: Option<&Bound<'py, PyType>>,
394        dict: Option<PyObject>,
395    ) -> PyResult<Py<PyType>> {
396        let base: *mut ffi::PyObject = match base {
397            None => std::ptr::null_mut(),
398            Some(obj) => obj.as_ptr(),
399        };
400
401        let dict: *mut ffi::PyObject = match dict {
402            None => std::ptr::null_mut(),
403            Some(obj) => obj.as_ptr(),
404        };
405
406        let doc_ptr = match doc.as_ref() {
407            Some(c) => c.as_ptr(),
408            None => std::ptr::null(),
409        };
410
411        let ptr = unsafe { ffi::PyErr_NewExceptionWithDoc(name.as_ptr(), doc_ptr, base, dict) };
412
413        unsafe { Py::from_owned_ptr_or_err(py, ptr) }
414    }
415
416    /// Prints a standard traceback to `sys.stderr`.
417    pub fn display(&self, py: Python<'_>) {
418        #[cfg(Py_3_12)]
419        unsafe {
420            ffi::PyErr_DisplayException(self.value(py).as_ptr())
421        }
422
423        #[cfg(not(Py_3_12))]
424        unsafe {
425            // keep the bound `traceback` alive for entire duration of
426            // PyErr_Display. if we inline this, the `Bound` will be dropped
427            // after the argument got evaluated, leading to call with a dangling
428            // pointer.
429            let traceback = self.traceback(py);
430            let type_bound = self.get_type(py);
431            ffi::PyErr_Display(
432                type_bound.as_ptr(),
433                self.value(py).as_ptr(),
434                traceback
435                    .as_ref()
436                    .map_or(std::ptr::null_mut(), |traceback| traceback.as_ptr()),
437            )
438        }
439    }
440
441    /// Calls `sys.excepthook` and then prints a standard traceback to `sys.stderr`.
442    pub fn print(&self, py: Python<'_>) {
443        self.clone_ref(py).restore(py);
444        unsafe { ffi::PyErr_PrintEx(0) }
445    }
446
447    /// Calls `sys.excepthook` and then prints a standard traceback to `sys.stderr`.
448    ///
449    /// Additionally sets `sys.last_{type,value,traceback,exc}` attributes to this exception.
450    pub fn print_and_set_sys_last_vars(&self, py: Python<'_>) {
451        self.clone_ref(py).restore(py);
452        unsafe { ffi::PyErr_PrintEx(1) }
453    }
454
455    /// Returns true if the current exception matches the exception in `exc`.
456    ///
457    /// If `exc` is a class object, this also returns `true` when `self` is an instance of a subclass.
458    /// If `exc` is a tuple, all exceptions in the tuple (and recursively in subtuples) are searched for a match.
459    pub fn matches<'py, T>(&self, py: Python<'py>, exc: T) -> Result<bool, T::Error>
460    where
461        T: IntoPyObject<'py>,
462    {
463        Ok(self.is_instance(py, &exc.into_pyobject(py)?.into_any().as_borrowed()))
464    }
465
466    /// Returns true if the current exception is instance of `T`.
467    #[inline]
468    pub fn is_instance(&self, py: Python<'_>, ty: &Bound<'_, PyAny>) -> bool {
469        let type_bound = self.get_type(py);
470        (unsafe { ffi::PyErr_GivenExceptionMatches(type_bound.as_ptr(), ty.as_ptr()) }) != 0
471    }
472
473    /// Returns true if the current exception is instance of `T`.
474    #[inline]
475    pub fn is_instance_of<T>(&self, py: Python<'_>) -> bool
476    where
477        T: PyTypeInfo,
478    {
479        self.is_instance(py, &T::type_object(py))
480    }
481
482    /// Writes the error back to the Python interpreter's global state.
483    /// This is the opposite of `PyErr::fetch()`.
484    #[inline]
485    pub fn restore(self, py: Python<'_>) {
486        self.state.restore(py)
487    }
488
489    /// Reports the error as unraisable.
490    ///
491    /// This calls `sys.unraisablehook()` using the current exception and obj argument.
492    ///
493    /// This method is useful to report errors in situations where there is no good mechanism
494    /// to report back to the Python land.  In Python this is used to indicate errors in
495    /// background threads or destructors which are protected.  In Rust code this is commonly
496    /// useful when you are calling into a Python callback which might fail, but there is no
497    /// obvious way to handle this error other than logging it.
498    ///
499    /// Calling this method has the benefit that the error goes back into a standardized callback
500    /// in Python which for instance allows unittests to ensure that no unraisable error
501    /// actually happend by hooking `sys.unraisablehook`.
502    ///
503    /// Example:
504    /// ```rust
505    /// # use pyo3::prelude::*;
506    /// # use pyo3::exceptions::PyRuntimeError;
507    /// # fn failing_function() -> PyResult<()> { Err(PyRuntimeError::new_err("foo")) }
508    /// # fn main() -> PyResult<()> {
509    /// Python::with_gil(|py| {
510    ///     match failing_function() {
511    ///         Err(pyerr) => pyerr.write_unraisable(py, None),
512    ///         Ok(..) => { /* do something here */ }
513    ///     }
514    ///     Ok(())
515    /// })
516    /// # }
517    #[inline]
518    pub fn write_unraisable(self, py: Python<'_>, obj: Option<&Bound<'_, PyAny>>) {
519        self.restore(py);
520        unsafe { ffi::PyErr_WriteUnraisable(obj.map_or(std::ptr::null_mut(), Bound::as_ptr)) }
521    }
522
523    /// Issues a warning message.
524    ///
525    /// May return an `Err(PyErr)` if warnings-as-errors is enabled.
526    ///
527    /// Equivalent to `warnings.warn()` in Python.
528    ///
529    /// The `category` should be one of the `Warning` classes available in
530    /// [`pyo3::exceptions`](crate::exceptions), or a subclass.  The Python
531    /// object can be retrieved using [`Python::get_type()`].
532    ///
533    /// Example:
534    /// ```rust
535    /// # use pyo3::prelude::*;
536    /// # use pyo3::ffi::c_str;
537    /// # fn main() -> PyResult<()> {
538    /// Python::with_gil(|py| {
539    ///     let user_warning = py.get_type::<pyo3::exceptions::PyUserWarning>();
540    ///     PyErr::warn(py, &user_warning, c_str!("I am warning you"), 0)?;
541    ///     Ok(())
542    /// })
543    /// # }
544    /// ```
545    pub fn warn<'py>(
546        py: Python<'py>,
547        category: &Bound<'py, PyAny>,
548        message: &CStr,
549        stacklevel: i32,
550    ) -> PyResult<()> {
551        error_on_minusone(py, unsafe {
552            ffi::PyErr_WarnEx(
553                category.as_ptr(),
554                message.as_ptr(),
555                stacklevel as ffi::Py_ssize_t,
556            )
557        })
558    }
559
560    /// Issues a warning message, with more control over the warning attributes.
561    ///
562    /// May return a `PyErr` if warnings-as-errors is enabled.
563    ///
564    /// Equivalent to `warnings.warn_explicit()` in Python.
565    ///
566    /// The `category` should be one of the `Warning` classes available in
567    /// [`pyo3::exceptions`](crate::exceptions), or a subclass.
568    pub fn warn_explicit<'py>(
569        py: Python<'py>,
570        category: &Bound<'py, PyAny>,
571        message: &CStr,
572        filename: &CStr,
573        lineno: i32,
574        module: Option<&CStr>,
575        registry: Option<&Bound<'py, PyAny>>,
576    ) -> PyResult<()> {
577        let module_ptr = match module {
578            None => std::ptr::null_mut(),
579            Some(s) => s.as_ptr(),
580        };
581        let registry: *mut ffi::PyObject = match registry {
582            None => std::ptr::null_mut(),
583            Some(obj) => obj.as_ptr(),
584        };
585        error_on_minusone(py, unsafe {
586            ffi::PyErr_WarnExplicit(
587                category.as_ptr(),
588                message.as_ptr(),
589                filename.as_ptr(),
590                lineno,
591                module_ptr,
592                registry,
593            )
594        })
595    }
596
597    /// Clone the PyErr. This requires the GIL, which is why PyErr does not implement Clone.
598    ///
599    /// # Examples
600    /// ```rust
601    /// use pyo3::{exceptions::PyTypeError, PyErr, Python, prelude::PyAnyMethods};
602    /// Python::with_gil(|py| {
603    ///     let err: PyErr = PyTypeError::new_err(("some type error",));
604    ///     let err_clone = err.clone_ref(py);
605    ///     assert!(err.get_type(py).is(&err_clone.get_type(py)));
606    ///     assert!(err.value(py).is(err_clone.value(py)));
607    ///     match err.traceback(py) {
608    ///         None => assert!(err_clone.traceback(py).is_none()),
609    ///         Some(tb) => assert!(err_clone.traceback(py).unwrap().is(&tb)),
610    ///     }
611    /// });
612    /// ```
613    #[inline]
614    pub fn clone_ref(&self, py: Python<'_>) -> PyErr {
615        PyErr::from_state(PyErrState::normalized(self.normalized(py).clone_ref(py)))
616    }
617
618    /// Return the cause (either an exception instance, or None, set by `raise ... from ...`)
619    /// associated with the exception, as accessible from Python through `__cause__`.
620    pub fn cause(&self, py: Python<'_>) -> Option<PyErr> {
621        use crate::ffi_ptr_ext::FfiPtrExt;
622        let obj =
623            unsafe { ffi::PyException_GetCause(self.value(py).as_ptr()).assume_owned_or_opt(py) };
624        // PyException_GetCause is documented as potentially returning PyNone, but only GraalPy seems to actually do that
625        #[cfg(GraalPy)]
626        if let Some(cause) = &obj {
627            if cause.is_none() {
628                return None;
629            }
630        }
631        obj.map(Self::from_value)
632    }
633
634    /// Set the cause associated with the exception, pass `None` to clear it.
635    pub fn set_cause(&self, py: Python<'_>, cause: Option<Self>) {
636        let value = self.value(py);
637        let cause = cause.map(|err| err.into_value(py));
638        unsafe {
639            // PyException_SetCause _steals_ a reference to cause, so must use .into_ptr()
640            ffi::PyException_SetCause(
641                value.as_ptr(),
642                cause.map_or(std::ptr::null_mut(), Py::into_ptr),
643            );
644        }
645    }
646
647    #[inline]
648    fn from_state(state: PyErrState) -> PyErr {
649        PyErr { state }
650    }
651
652    #[inline]
653    fn normalized(&self, py: Python<'_>) -> &PyErrStateNormalized {
654        self.state.as_normalized(py)
655    }
656}
657
658impl std::fmt::Debug for PyErr {
659    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
660        Python::with_gil(|py| {
661            f.debug_struct("PyErr")
662                .field("type", &self.get_type(py))
663                .field("value", self.value(py))
664                .field(
665                    "traceback",
666                    &self.traceback(py).map(|tb| match tb.format() {
667                        Ok(s) => s,
668                        Err(err) => {
669                            err.write_unraisable(py, Some(&tb));
670                            // It would be nice to format what we can of the
671                            // error, but we can't guarantee that the error
672                            // won't have another unformattable traceback inside
673                            // it and we want to avoid an infinite recursion.
674                            format!("<unformattable {:?}>", tb)
675                        }
676                    }),
677                )
678                .finish()
679        })
680    }
681}
682
683impl std::fmt::Display for PyErr {
684    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
685        Python::with_gil(|py| {
686            let value = self.value(py);
687            let type_name = value.get_type().qualname().map_err(|_| std::fmt::Error)?;
688            write!(f, "{}", type_name)?;
689            if let Ok(s) = value.str() {
690                write!(f, ": {}", &s.to_string_lossy())
691            } else {
692                write!(f, ": <exception str() failed>")
693            }
694        })
695    }
696}
697
698impl std::error::Error for PyErr {}
699
700impl<'py> IntoPyObject<'py> for PyErr {
701    type Target = PyBaseException;
702    type Output = Bound<'py, Self::Target>;
703    type Error = Infallible;
704
705    #[inline]
706    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
707        Ok(self.into_value(py).into_bound(py))
708    }
709}
710
711impl<'py> IntoPyObject<'py> for &PyErr {
712    type Target = PyBaseException;
713    type Output = Bound<'py, Self::Target>;
714    type Error = Infallible;
715
716    #[inline]
717    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
718        self.clone_ref(py).into_pyobject(py)
719    }
720}
721
722struct PyDowncastErrorArguments {
723    from: Py<PyType>,
724    to: Cow<'static, str>,
725}
726
727impl PyErrArguments for PyDowncastErrorArguments {
728    fn arguments(self, py: Python<'_>) -> PyObject {
729        const FAILED_TO_EXTRACT: Cow<'_, str> = Cow::Borrowed("<failed to extract type name>");
730        let from = self.from.bind(py).qualname();
731        let from = match &from {
732            Ok(qn) => qn.to_cow().unwrap_or(FAILED_TO_EXTRACT),
733            Err(_) => FAILED_TO_EXTRACT,
734        };
735        format!("'{}' object cannot be converted to '{}'", from, self.to)
736            .into_pyobject(py)
737            .unwrap()
738            .into_any()
739            .unbind()
740    }
741}
742
743/// Python exceptions that can be converted to [`PyErr`].
744///
745/// This is used to implement [`From<Bound<'_, T>> for PyErr`].
746///
747/// Users should not need to implement this trait directly. It is implemented automatically in the
748/// [`crate::import_exception!`] and [`crate::create_exception!`] macros.
749pub trait ToPyErr {}
750
751impl<'py, T> std::convert::From<Bound<'py, T>> for PyErr
752where
753    T: ToPyErr,
754{
755    #[inline]
756    fn from(err: Bound<'py, T>) -> PyErr {
757        PyErr::from_value(err.into_any())
758    }
759}
760
761/// Convert `DowncastError` to Python `TypeError`.
762impl std::convert::From<DowncastError<'_, '_>> for PyErr {
763    fn from(err: DowncastError<'_, '_>) -> PyErr {
764        let args = PyDowncastErrorArguments {
765            from: err.from.get_type().into(),
766            to: err.to,
767        };
768
769        exceptions::PyTypeError::new_err(args)
770    }
771}
772
773impl std::error::Error for DowncastError<'_, '_> {}
774
775impl std::fmt::Display for DowncastError<'_, '_> {
776    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
777        display_downcast_error(f, &self.from, &self.to)
778    }
779}
780
781/// Convert `DowncastIntoError` to Python `TypeError`.
782impl std::convert::From<DowncastIntoError<'_>> for PyErr {
783    fn from(err: DowncastIntoError<'_>) -> PyErr {
784        let args = PyDowncastErrorArguments {
785            from: err.from.get_type().into(),
786            to: err.to,
787        };
788
789        exceptions::PyTypeError::new_err(args)
790    }
791}
792
793impl std::error::Error for DowncastIntoError<'_> {}
794
795impl std::fmt::Display for DowncastIntoError<'_> {
796    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
797        display_downcast_error(f, &self.from, &self.to)
798    }
799}
800
801fn display_downcast_error(
802    f: &mut std::fmt::Formatter<'_>,
803    from: &Bound<'_, PyAny>,
804    to: &str,
805) -> std::fmt::Result {
806    write!(
807        f,
808        "'{}' object cannot be converted to '{}'",
809        from.get_type().qualname().map_err(|_| std::fmt::Error)?,
810        to
811    )
812}
813
814#[track_caller]
815pub fn panic_after_error(_py: Python<'_>) -> ! {
816    unsafe {
817        ffi::PyErr_Print();
818    }
819    panic!("Python API call failed");
820}
821
822/// Returns Ok if the error code is not -1.
823#[inline]
824pub(crate) fn error_on_minusone<T: SignedInteger>(py: Python<'_>, result: T) -> PyResult<()> {
825    if result != T::MINUS_ONE {
826        Ok(())
827    } else {
828        Err(PyErr::fetch(py))
829    }
830}
831
832pub(crate) trait SignedInteger: Eq {
833    const MINUS_ONE: Self;
834}
835
836macro_rules! impl_signed_integer {
837    ($t:ty) => {
838        impl SignedInteger for $t {
839            const MINUS_ONE: Self = -1;
840        }
841    };
842}
843
844impl_signed_integer!(i8);
845impl_signed_integer!(i16);
846impl_signed_integer!(i32);
847impl_signed_integer!(i64);
848impl_signed_integer!(i128);
849impl_signed_integer!(isize);
850
851#[cfg(test)]
852mod tests {
853    use super::PyErrState;
854    use crate::exceptions::{self, PyTypeError, PyValueError};
855    use crate::{ffi, PyErr, PyTypeInfo, Python};
856
857    #[test]
858    fn no_error() {
859        assert!(Python::with_gil(PyErr::take).is_none());
860    }
861
862    #[test]
863    fn set_valueerror() {
864        Python::with_gil(|py| {
865            let err: PyErr = exceptions::PyValueError::new_err("some exception message");
866            assert!(err.is_instance_of::<exceptions::PyValueError>(py));
867            err.restore(py);
868            assert!(PyErr::occurred(py));
869            let err = PyErr::fetch(py);
870            assert!(err.is_instance_of::<exceptions::PyValueError>(py));
871            assert_eq!(err.to_string(), "ValueError: some exception message");
872        })
873    }
874
875    #[test]
876    fn invalid_error_type() {
877        Python::with_gil(|py| {
878            let err: PyErr = PyErr::new::<crate::types::PyString, _>(());
879            assert!(err.is_instance_of::<exceptions::PyTypeError>(py));
880            err.restore(py);
881            let err = PyErr::fetch(py);
882
883            assert!(err.is_instance_of::<exceptions::PyTypeError>(py));
884            assert_eq!(
885                err.to_string(),
886                "TypeError: exceptions must derive from BaseException"
887            );
888        })
889    }
890
891    #[test]
892    fn set_typeerror() {
893        Python::with_gil(|py| {
894            let err: PyErr = exceptions::PyTypeError::new_err(());
895            err.restore(py);
896            assert!(PyErr::occurred(py));
897            drop(PyErr::fetch(py));
898        });
899    }
900
901    #[test]
902    #[should_panic(expected = "new panic")]
903    fn fetching_panic_exception_resumes_unwind() {
904        use crate::panic::PanicException;
905
906        Python::with_gil(|py| {
907            let err: PyErr = PanicException::new_err("new panic");
908            err.restore(py);
909            assert!(PyErr::occurred(py));
910
911            // should resume unwind
912            let _ = PyErr::fetch(py);
913        });
914    }
915
916    #[test]
917    #[should_panic(expected = "new panic")]
918    #[cfg(not(Py_3_12))]
919    fn fetching_normalized_panic_exception_resumes_unwind() {
920        use crate::panic::PanicException;
921
922        Python::with_gil(|py| {
923            let err: PyErr = PanicException::new_err("new panic");
924            // Restoring an error doesn't normalize it before Python 3.12,
925            // so we have to explicitly test this case.
926            let _ = err.normalized(py);
927            err.restore(py);
928            assert!(PyErr::occurred(py));
929
930            // should resume unwind
931            let _ = PyErr::fetch(py);
932        });
933    }
934
935    #[test]
936    fn err_debug() {
937        // Debug representation should be like the following (without the newlines):
938        // PyErr {
939        //     type: <class 'Exception'>,
940        //     value: Exception('banana'),
941        //     traceback:  Some(\"Traceback (most recent call last):\\n  File \\\"<string>\\\", line 1, in <module>\\n\")
942        // }
943
944        Python::with_gil(|py| {
945            let err = py
946                .run(ffi::c_str!("raise Exception('banana')"), None, None)
947                .expect_err("raising should have given us an error");
948
949            let debug_str = format!("{:?}", err);
950            assert!(debug_str.starts_with("PyErr { "));
951            assert!(debug_str.ends_with(" }"));
952
953            // Strip "PyErr { " and " }". Split into 3 substrings to separate type,
954            // value, and traceback while not splitting the string within traceback.
955            let mut fields = debug_str["PyErr { ".len()..debug_str.len() - 2].splitn(3, ", ");
956
957            assert_eq!(fields.next().unwrap(), "type: <class 'Exception'>");
958            assert_eq!(fields.next().unwrap(), "value: Exception('banana')");
959            assert_eq!(
960                fields.next().unwrap(),
961                "traceback: Some(\"Traceback (most recent call last):\\n  File \\\"<string>\\\", line 1, in <module>\\n\")"
962            );
963
964            assert!(fields.next().is_none());
965        });
966    }
967
968    #[test]
969    fn err_display() {
970        Python::with_gil(|py| {
971            let err = py
972                .run(ffi::c_str!("raise Exception('banana')"), None, None)
973                .expect_err("raising should have given us an error");
974            assert_eq!(err.to_string(), "Exception: banana");
975        });
976    }
977
978    #[test]
979    fn test_pyerr_send_sync() {
980        fn is_send<T: Send>() {}
981        fn is_sync<T: Sync>() {}
982
983        is_send::<PyErr>();
984        is_sync::<PyErr>();
985
986        is_send::<PyErrState>();
987        is_sync::<PyErrState>();
988    }
989
990    #[test]
991    fn test_pyerr_matches() {
992        Python::with_gil(|py| {
993            let err = PyErr::new::<PyValueError, _>("foo");
994            assert!(err.matches(py, PyValueError::type_object(py)).unwrap());
995
996            assert!(err
997                .matches(
998                    py,
999                    (PyValueError::type_object(py), PyTypeError::type_object(py))
1000                )
1001                .unwrap());
1002
1003            assert!(!err.matches(py, PyTypeError::type_object(py)).unwrap());
1004
1005            // String is not a valid exception class, so we should get a TypeError
1006            let err: PyErr = PyErr::from_type(crate::types::PyString::type_object(py), "foo");
1007            assert!(err.matches(py, PyTypeError::type_object(py)).unwrap());
1008        })
1009    }
1010
1011    #[test]
1012    fn test_pyerr_cause() {
1013        Python::with_gil(|py| {
1014            let err = py
1015                .run(ffi::c_str!("raise Exception('banana')"), None, None)
1016                .expect_err("raising should have given us an error");
1017            assert!(err.cause(py).is_none());
1018
1019            let err = py
1020                .run(
1021                    ffi::c_str!("raise Exception('banana') from Exception('apple')"),
1022                    None,
1023                    None,
1024                )
1025                .expect_err("raising should have given us an error");
1026            let cause = err
1027                .cause(py)
1028                .expect("raising from should have given us a cause");
1029            assert_eq!(cause.to_string(), "Exception: apple");
1030
1031            err.set_cause(py, None);
1032            assert!(err.cause(py).is_none());
1033
1034            let new_cause = exceptions::PyValueError::new_err("orange");
1035            err.set_cause(py, Some(new_cause));
1036            let cause = err
1037                .cause(py)
1038                .expect("set_cause should have given us a cause");
1039            assert_eq!(cause.to_string(), "ValueError: orange");
1040        });
1041    }
1042
1043    #[test]
1044    fn warnings() {
1045        use crate::types::any::PyAnyMethods;
1046        // Note: although the warning filter is interpreter global, keeping the
1047        // GIL locked should prevent effects to be visible to other testing
1048        // threads.
1049        Python::with_gil(|py| {
1050            let cls = py.get_type::<exceptions::PyUserWarning>();
1051
1052            // Reset warning filter to default state
1053            let warnings = py.import("warnings").unwrap();
1054            warnings.call_method0("resetwarnings").unwrap();
1055
1056            // First, test the warning is emitted
1057            #[cfg(not(Py_GIL_DISABLED))]
1058            assert_warnings!(
1059                py,
1060                { PyErr::warn(py, &cls, ffi::c_str!("I am warning you"), 0).unwrap() },
1061                [(exceptions::PyUserWarning, "I am warning you")]
1062            );
1063
1064            // Test with raising
1065            warnings
1066                .call_method1("simplefilter", ("error", &cls))
1067                .unwrap();
1068            PyErr::warn(py, &cls, ffi::c_str!("I am warning you"), 0).unwrap_err();
1069
1070            // Test with error for an explicit module
1071            warnings.call_method0("resetwarnings").unwrap();
1072            warnings
1073                .call_method1("filterwarnings", ("error", "", &cls, "pyo3test"))
1074                .unwrap();
1075
1076            // This has the wrong module and will not raise, just be emitted
1077            #[cfg(not(Py_GIL_DISABLED))]
1078            assert_warnings!(
1079                py,
1080                { PyErr::warn(py, &cls, ffi::c_str!("I am warning you"), 0).unwrap() },
1081                [(exceptions::PyUserWarning, "I am warning you")]
1082            );
1083
1084            let err = PyErr::warn_explicit(
1085                py,
1086                &cls,
1087                ffi::c_str!("I am warning you"),
1088                ffi::c_str!("pyo3test.py"),
1089                427,
1090                None,
1091                None,
1092            )
1093            .unwrap_err();
1094            assert!(err
1095                .value(py)
1096                .getattr("args")
1097                .unwrap()
1098                .get_item(0)
1099                .unwrap()
1100                .eq("I am warning you")
1101                .unwrap());
1102
1103            // Finally, reset filter again
1104            warnings.call_method0("resetwarnings").unwrap();
1105        });
1106    }
1107}