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