pyo3/err/
mod.rs

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