Skip to main content

pyo3/err/
mod.rs

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