pyo3/err/
mod.rs

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