Skip to main content

pyo3/err/
mod.rs

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