pyo3/err/
mod.rs

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