pyo3/
exceptions.rs

1//! Exception and warning types defined by Python.
2//!
3//! The structs in this module represent Python's built-in exceptions and
4//! warnings, while the modules comprise structs representing errors defined in
5//! Python code.
6//!
7//! The latter are created with the
8//! [`import_exception`](crate::import_exception) macro, which you can use
9//! yourself to import Python classes that are ultimately derived from
10//! `BaseException`.
11
12use crate::{ffi, Bound, PyResult, Python};
13use std::ffi::CStr;
14use std::ops;
15
16/// The boilerplate to convert between a Rust type and a Python exception.
17#[doc(hidden)]
18#[macro_export]
19macro_rules! impl_exception_boilerplate {
20    ($name: ident) => {
21        $crate::impl_exception_boilerplate_bound!($name);
22
23        impl $crate::ToPyErr for $name {}
24    };
25}
26
27#[doc(hidden)]
28#[macro_export]
29macro_rules! impl_exception_boilerplate_bound {
30    ($name: ident) => {
31        impl $name {
32            /// Creates a new [`PyErr`] of this type.
33            ///
34            /// [`PyErr`]: https://docs.rs/pyo3/latest/pyo3/struct.PyErr.html "PyErr in pyo3"
35            #[inline]
36            #[allow(dead_code)]
37            pub fn new_err<A>(args: A) -> $crate::PyErr
38            where
39                A: $crate::PyErrArguments + ::std::marker::Send + ::std::marker::Sync + 'static,
40            {
41                $crate::PyErr::new::<$name, A>(args)
42            }
43        }
44    };
45}
46
47/// Defines a Rust type for an exception defined in Python code.
48///
49/// # Syntax
50///
51/// ```import_exception!(module, MyError)```
52///
53/// * `module` is the name of the containing module.
54/// * `MyError` is the name of the new exception type.
55///
56/// # Examples
57/// ```
58/// use pyo3::import_exception;
59/// use pyo3::types::IntoPyDict;
60/// use pyo3::Python;
61///
62/// import_exception!(socket, gaierror);
63///
64/// # fn main() -> pyo3::PyResult<()> {
65/// Python::with_gil(|py| {
66///     let ctx = [("gaierror", py.get_type::<gaierror>())].into_py_dict(py)?;
67///     pyo3::py_run!(py, *ctx, "import socket; assert gaierror is socket.gaierror");
68/// #   Ok(())
69/// })
70/// # }
71///
72/// ```
73#[macro_export]
74macro_rules! import_exception {
75    ($module: expr, $name: ident) => {
76        /// A Rust type representing an exception defined in Python code.
77        ///
78        /// This type was created by the [`pyo3::import_exception!`] macro - see its documentation
79        /// for more information.
80        ///
81        /// [`pyo3::import_exception!`]: https://docs.rs/pyo3/latest/pyo3/macro.import_exception.html "import_exception in pyo3"
82        #[repr(transparent)]
83        #[allow(non_camel_case_types)] // E.g. `socket.herror`
84        pub struct $name($crate::PyAny);
85
86        $crate::impl_exception_boilerplate!($name);
87
88        $crate::pyobject_native_type_core!(
89            $name,
90            $name::type_object_raw,
91            #module=::std::option::Option::Some(stringify!($module))
92        );
93
94        impl $name {
95            fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
96                use $crate::types::PyTypeMethods;
97                static TYPE_OBJECT: $crate::impl_::exceptions::ImportedExceptionTypeObject =
98                    $crate::impl_::exceptions::ImportedExceptionTypeObject::new(stringify!($module), stringify!($name));
99                TYPE_OBJECT.get(py).as_type_ptr()
100            }
101        }
102    };
103}
104
105/// Variant of [`import_exception`](crate::import_exception) that does not emit code needed to
106/// use the imported exception type as a GIL Ref.
107///
108/// This is useful only during migration as a way to avoid generating needless code.
109#[macro_export]
110macro_rules! import_exception_bound {
111    ($module: expr, $name: ident) => {
112        /// A Rust type representing an exception defined in Python code.
113        ///
114        /// This type was created by the [`pyo3::import_exception_bound!`] macro - see its documentation
115        /// for more information.
116        ///
117        /// [`pyo3::import_exception_bound!`]: https://docs.rs/pyo3/latest/pyo3/macro.import_exception.html "import_exception in pyo3"
118        #[repr(transparent)]
119        #[allow(non_camel_case_types)] // E.g. `socket.herror`
120        pub struct $name($crate::PyAny);
121
122        $crate::impl_exception_boilerplate_bound!($name);
123
124        $crate::pyobject_native_type_info!(
125            $name,
126            $name::type_object_raw,
127            ::std::option::Option::Some(stringify!($module))
128        );
129
130        impl $crate::types::DerefToPyAny for $name {}
131
132        impl $name {
133            fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
134                use $crate::types::PyTypeMethods;
135                static TYPE_OBJECT: $crate::impl_::exceptions::ImportedExceptionTypeObject =
136                    $crate::impl_::exceptions::ImportedExceptionTypeObject::new(
137                        stringify!($module),
138                        stringify!($name),
139                    );
140                TYPE_OBJECT.get(py).as_type_ptr()
141            }
142        }
143    };
144}
145
146/// Defines a new exception type.
147///
148/// # Syntax
149///
150/// * `module` is the name of the containing module.
151/// * `name` is the name of the new exception type.
152/// * `base` is the base class of `MyError`, usually [`PyException`].
153/// * `doc` (optional) is the docstring visible to users (with `.__doc__` and `help()`) and
154///
155/// accompanies your error type in your crate's documentation.
156///
157/// # Examples
158///
159/// ```
160/// use pyo3::prelude::*;
161/// use pyo3::create_exception;
162/// use pyo3::exceptions::PyException;
163///
164/// create_exception!(my_module, MyError, PyException, "Some description.");
165///
166/// #[pyfunction]
167/// fn raise_myerror() -> PyResult<()> {
168///     let err = MyError::new_err("Some error happened.");
169///     Err(err)
170/// }
171///
172/// #[pymodule]
173/// fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
174///     m.add("MyError", m.py().get_type::<MyError>())?;
175///     m.add_function(wrap_pyfunction!(raise_myerror, m)?)?;
176///     Ok(())
177/// }
178/// # fn main() -> PyResult<()> {
179/// #     Python::with_gil(|py| -> PyResult<()> {
180/// #         let fun = wrap_pyfunction!(raise_myerror, py)?;
181/// #         let locals = pyo3::types::PyDict::new(py);
182/// #         locals.set_item("MyError", py.get_type::<MyError>())?;
183/// #         locals.set_item("raise_myerror", fun)?;
184/// #
185/// #         py.run(pyo3::ffi::c_str!(
186/// # "try:
187/// #     raise_myerror()
188/// # except MyError as e:
189/// #     assert e.__doc__ == 'Some description.'
190/// #     assert str(e) == 'Some error happened.'"),
191/// #             None,
192/// #             Some(&locals),
193/// #         )?;
194/// #
195/// #         Ok(())
196/// #     })
197/// # }
198/// ```
199///
200/// Python code can handle this exception like any other exception:
201///
202/// ```python
203/// from my_module import MyError, raise_myerror
204///
205/// try:
206///     raise_myerror()
207/// except MyError as e:
208///     assert e.__doc__ == 'Some description.'
209///     assert str(e) == 'Some error happened.'
210/// ```
211///
212#[macro_export]
213macro_rules! create_exception {
214    ($module: expr, $name: ident, $base: ty) => {
215        #[repr(transparent)]
216        #[allow(non_camel_case_types)] // E.g. `socket.herror`
217        pub struct $name($crate::PyAny);
218
219        $crate::impl_exception_boilerplate!($name);
220
221        $crate::create_exception_type_object!($module, $name, $base, None);
222    };
223    ($module: expr, $name: ident, $base: ty, $doc: expr) => {
224        #[repr(transparent)]
225        #[allow(non_camel_case_types)] // E.g. `socket.herror`
226        #[doc = $doc]
227        pub struct $name($crate::PyAny);
228
229        $crate::impl_exception_boilerplate!($name);
230
231        $crate::create_exception_type_object!($module, $name, $base, Some($doc));
232    };
233}
234
235/// `impl PyTypeInfo for $name` where `$name` is an
236/// exception newly defined in Rust code.
237#[doc(hidden)]
238#[macro_export]
239macro_rules! create_exception_type_object {
240    ($module: expr, $name: ident, $base: ty, None) => {
241        $crate::create_exception_type_object!($module, $name, $base, ::std::option::Option::None);
242    };
243    ($module: expr, $name: ident, $base: ty, Some($doc: expr)) => {
244        $crate::create_exception_type_object!($module, $name, $base, ::std::option::Option::Some($crate::ffi::c_str!($doc)));
245    };
246    ($module: expr, $name: ident, $base: ty, $doc: expr) => {
247        $crate::pyobject_native_type_core!(
248            $name,
249            $name::type_object_raw,
250            #module=::std::option::Option::Some(stringify!($module))
251        );
252
253        impl $name {
254            fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
255                use $crate::sync::GILOnceCell;
256                static TYPE_OBJECT: GILOnceCell<$crate::Py<$crate::types::PyType>> =
257                    GILOnceCell::new();
258
259                TYPE_OBJECT
260                    .get_or_init(py, ||
261                        $crate::PyErr::new_type(
262                            py,
263                            $crate::ffi::c_str!(concat!(stringify!($module), ".", stringify!($name))),
264                            $doc,
265                            ::std::option::Option::Some(&py.get_type::<$base>()),
266                            ::std::option::Option::None,
267                        ).expect("Failed to initialize new exception type.")
268                ).as_ptr() as *mut $crate::ffi::PyTypeObject
269            }
270        }
271    };
272}
273
274macro_rules! impl_native_exception (
275    ($name:ident, $exc_name:ident, $doc:expr, $layout:path $(, #checkfunction=$checkfunction:path)?) => (
276        #[doc = $doc]
277        #[allow(clippy::upper_case_acronyms)]
278        pub struct $name($crate::PyAny);
279
280        $crate::impl_exception_boilerplate!($name);
281        $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject } $(, #checkfunction=$checkfunction)?);
282        $crate::pyobject_subclassable_native_type!($name, $layout);
283    );
284    ($name:ident, $exc_name:ident, $doc:expr) => (
285        impl_native_exception!($name, $exc_name, $doc, $crate::ffi::PyBaseExceptionObject);
286    )
287);
288
289#[cfg(windows)]
290macro_rules! impl_windows_native_exception (
291    ($name:ident, $exc_name:ident, $doc:expr, $layout:path) => (
292        #[cfg(windows)]
293        #[doc = $doc]
294        #[allow(clippy::upper_case_acronyms)]
295        pub struct $name($crate::PyAny);
296
297        $crate::impl_exception_boilerplate!($name);
298        $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject });
299    );
300    ($name:ident, $exc_name:ident, $doc:expr) => (
301        impl_windows_native_exception!($name, $exc_name, $doc, $crate::ffi::PyBaseExceptionObject);
302    )
303);
304
305macro_rules! native_doc(
306    ($name: literal, $alt: literal) => (
307        concat!(
308"Represents Python's [`", $name, "`](https://docs.python.org/3/library/exceptions.html#", $name, ") exception.
309
310", $alt
311        )
312    );
313    ($name: literal) => (
314        concat!(
315"
316Represents Python's [`", $name, "`](https://docs.python.org/3/library/exceptions.html#", $name, ") exception.
317
318# Example: Raising ", $name, " from Rust
319
320This exception can be sent to Python code by converting it into a
321[`PyErr`](crate::PyErr), where Python code can then catch it.
322```
323use pyo3::prelude::*;
324use pyo3::exceptions::Py", $name, ";
325
326#[pyfunction]
327fn always_throws() -> PyResult<()> {
328    let message = \"I'm ", $name ,", and I was raised from Rust.\";
329    Err(Py", $name, "::new_err(message))
330}
331#
332# Python::with_gil(|py| {
333#     let fun = pyo3::wrap_pyfunction!(always_throws, py).unwrap();
334#     let err = fun.call0().expect_err(\"called a function that should always return an error but the return value was Ok\");
335#     assert!(err.is_instance_of::<Py", $name, ">(py))
336# });
337```
338
339Python code:
340 ```python
341 from my_module import always_throws
342
343try:
344    always_throws()
345except ", $name, " as e:
346    print(f\"Caught an exception: {e}\")
347```
348
349# Example: Catching ", $name, " in Rust
350
351```
352use pyo3::prelude::*;
353use pyo3::exceptions::Py", $name, ";
354use pyo3::ffi::c_str;
355
356Python::with_gil(|py| {
357    let result: PyResult<()> = py.run(c_str!(\"raise ", $name, "\"), None, None);
358
359    let error_type = match result {
360        Ok(_) => \"Not an error\",
361        Err(error) if error.is_instance_of::<Py", $name, ">(py) => \"" , $name, "\",
362        Err(_) => \"Some other error\",
363    };
364
365    assert_eq!(error_type, \"", $name, "\");
366});
367```
368"
369        )
370    );
371);
372
373impl_native_exception!(
374    PyBaseException,
375    PyExc_BaseException,
376    native_doc!("BaseException"),
377    ffi::PyBaseExceptionObject,
378    #checkfunction=ffi::PyExceptionInstance_Check
379);
380impl_native_exception!(PyException, PyExc_Exception, native_doc!("Exception"));
381impl_native_exception!(
382    PyStopAsyncIteration,
383    PyExc_StopAsyncIteration,
384    native_doc!("StopAsyncIteration")
385);
386impl_native_exception!(
387    PyStopIteration,
388    PyExc_StopIteration,
389    native_doc!("StopIteration"),
390    ffi::PyStopIterationObject
391);
392impl_native_exception!(
393    PyGeneratorExit,
394    PyExc_GeneratorExit,
395    native_doc!("GeneratorExit")
396);
397impl_native_exception!(
398    PyArithmeticError,
399    PyExc_ArithmeticError,
400    native_doc!("ArithmeticError")
401);
402impl_native_exception!(PyLookupError, PyExc_LookupError, native_doc!("LookupError"));
403
404impl_native_exception!(
405    PyAssertionError,
406    PyExc_AssertionError,
407    native_doc!("AssertionError")
408);
409impl_native_exception!(
410    PyAttributeError,
411    PyExc_AttributeError,
412    native_doc!("AttributeError")
413);
414impl_native_exception!(PyBufferError, PyExc_BufferError, native_doc!("BufferError"));
415impl_native_exception!(PyEOFError, PyExc_EOFError, native_doc!("EOFError"));
416impl_native_exception!(
417    PyFloatingPointError,
418    PyExc_FloatingPointError,
419    native_doc!("FloatingPointError")
420);
421#[cfg(not(any(PyPy, GraalPy)))]
422impl_native_exception!(
423    PyOSError,
424    PyExc_OSError,
425    native_doc!("OSError"),
426    ffi::PyOSErrorObject
427);
428#[cfg(any(PyPy, GraalPy))]
429impl_native_exception!(PyOSError, PyExc_OSError, native_doc!("OSError"));
430impl_native_exception!(PyImportError, PyExc_ImportError, native_doc!("ImportError"));
431
432impl_native_exception!(
433    PyModuleNotFoundError,
434    PyExc_ModuleNotFoundError,
435    native_doc!("ModuleNotFoundError")
436);
437
438impl_native_exception!(PyIndexError, PyExc_IndexError, native_doc!("IndexError"));
439impl_native_exception!(PyKeyError, PyExc_KeyError, native_doc!("KeyError"));
440impl_native_exception!(
441    PyKeyboardInterrupt,
442    PyExc_KeyboardInterrupt,
443    native_doc!("KeyboardInterrupt")
444);
445impl_native_exception!(PyMemoryError, PyExc_MemoryError, native_doc!("MemoryError"));
446impl_native_exception!(PyNameError, PyExc_NameError, native_doc!("NameError"));
447impl_native_exception!(
448    PyOverflowError,
449    PyExc_OverflowError,
450    native_doc!("OverflowError")
451);
452impl_native_exception!(
453    PyRuntimeError,
454    PyExc_RuntimeError,
455    native_doc!("RuntimeError")
456);
457impl_native_exception!(
458    PyRecursionError,
459    PyExc_RecursionError,
460    native_doc!("RecursionError")
461);
462impl_native_exception!(
463    PyNotImplementedError,
464    PyExc_NotImplementedError,
465    native_doc!("NotImplementedError")
466);
467#[cfg(not(any(PyPy, GraalPy)))]
468impl_native_exception!(
469    PySyntaxError,
470    PyExc_SyntaxError,
471    native_doc!("SyntaxError"),
472    ffi::PySyntaxErrorObject
473);
474#[cfg(any(PyPy, GraalPy))]
475impl_native_exception!(PySyntaxError, PyExc_SyntaxError, native_doc!("SyntaxError"));
476impl_native_exception!(
477    PyReferenceError,
478    PyExc_ReferenceError,
479    native_doc!("ReferenceError")
480);
481impl_native_exception!(PySystemError, PyExc_SystemError, native_doc!("SystemError"));
482#[cfg(not(any(PyPy, GraalPy)))]
483impl_native_exception!(
484    PySystemExit,
485    PyExc_SystemExit,
486    native_doc!("SystemExit"),
487    ffi::PySystemExitObject
488);
489#[cfg(any(PyPy, GraalPy))]
490impl_native_exception!(PySystemExit, PyExc_SystemExit, native_doc!("SystemExit"));
491impl_native_exception!(PyTypeError, PyExc_TypeError, native_doc!("TypeError"));
492impl_native_exception!(
493    PyUnboundLocalError,
494    PyExc_UnboundLocalError,
495    native_doc!("UnboundLocalError")
496);
497#[cfg(not(any(PyPy, GraalPy)))]
498impl_native_exception!(
499    PyUnicodeError,
500    PyExc_UnicodeError,
501    native_doc!("UnicodeError"),
502    ffi::PyUnicodeErrorObject
503);
504#[cfg(any(PyPy, GraalPy))]
505impl_native_exception!(
506    PyUnicodeError,
507    PyExc_UnicodeError,
508    native_doc!("UnicodeError")
509);
510// these four errors need arguments, so they're too annoying to write tests for using macros...
511impl_native_exception!(
512    PyUnicodeDecodeError,
513    PyExc_UnicodeDecodeError,
514    native_doc!("UnicodeDecodeError", "")
515);
516impl_native_exception!(
517    PyUnicodeEncodeError,
518    PyExc_UnicodeEncodeError,
519    native_doc!("UnicodeEncodeError", "")
520);
521impl_native_exception!(
522    PyUnicodeTranslateError,
523    PyExc_UnicodeTranslateError,
524    native_doc!("UnicodeTranslateError", "")
525);
526#[cfg(Py_3_11)]
527impl_native_exception!(
528    PyBaseExceptionGroup,
529    PyExc_BaseExceptionGroup,
530    native_doc!("BaseExceptionGroup", "")
531);
532impl_native_exception!(PyValueError, PyExc_ValueError, native_doc!("ValueError"));
533impl_native_exception!(
534    PyZeroDivisionError,
535    PyExc_ZeroDivisionError,
536    native_doc!("ZeroDivisionError")
537);
538
539impl_native_exception!(
540    PyBlockingIOError,
541    PyExc_BlockingIOError,
542    native_doc!("BlockingIOError")
543);
544impl_native_exception!(
545    PyBrokenPipeError,
546    PyExc_BrokenPipeError,
547    native_doc!("BrokenPipeError")
548);
549impl_native_exception!(
550    PyChildProcessError,
551    PyExc_ChildProcessError,
552    native_doc!("ChildProcessError")
553);
554impl_native_exception!(
555    PyConnectionError,
556    PyExc_ConnectionError,
557    native_doc!("ConnectionError")
558);
559impl_native_exception!(
560    PyConnectionAbortedError,
561    PyExc_ConnectionAbortedError,
562    native_doc!("ConnectionAbortedError")
563);
564impl_native_exception!(
565    PyConnectionRefusedError,
566    PyExc_ConnectionRefusedError,
567    native_doc!("ConnectionRefusedError")
568);
569impl_native_exception!(
570    PyConnectionResetError,
571    PyExc_ConnectionResetError,
572    native_doc!("ConnectionResetError")
573);
574impl_native_exception!(
575    PyFileExistsError,
576    PyExc_FileExistsError,
577    native_doc!("FileExistsError")
578);
579impl_native_exception!(
580    PyFileNotFoundError,
581    PyExc_FileNotFoundError,
582    native_doc!("FileNotFoundError")
583);
584impl_native_exception!(
585    PyInterruptedError,
586    PyExc_InterruptedError,
587    native_doc!("InterruptedError")
588);
589impl_native_exception!(
590    PyIsADirectoryError,
591    PyExc_IsADirectoryError,
592    native_doc!("IsADirectoryError")
593);
594impl_native_exception!(
595    PyNotADirectoryError,
596    PyExc_NotADirectoryError,
597    native_doc!("NotADirectoryError")
598);
599impl_native_exception!(
600    PyPermissionError,
601    PyExc_PermissionError,
602    native_doc!("PermissionError")
603);
604impl_native_exception!(
605    PyProcessLookupError,
606    PyExc_ProcessLookupError,
607    native_doc!("ProcessLookupError")
608);
609impl_native_exception!(
610    PyTimeoutError,
611    PyExc_TimeoutError,
612    native_doc!("TimeoutError")
613);
614
615impl_native_exception!(
616    PyEnvironmentError,
617    PyExc_EnvironmentError,
618    native_doc!("EnvironmentError")
619);
620impl_native_exception!(PyIOError, PyExc_IOError, native_doc!("IOError"));
621
622#[cfg(windows)]
623impl_windows_native_exception!(
624    PyWindowsError,
625    PyExc_WindowsError,
626    native_doc!("WindowsError")
627);
628
629impl PyUnicodeDecodeError {
630    /// Creates a Python `UnicodeDecodeError`.
631    pub fn new<'py>(
632        py: Python<'py>,
633        encoding: &CStr,
634        input: &[u8],
635        range: ops::Range<usize>,
636        reason: &CStr,
637    ) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
638        use crate::ffi_ptr_ext::FfiPtrExt;
639        use crate::py_result_ext::PyResultExt;
640        unsafe {
641            ffi::PyUnicodeDecodeError_Create(
642                encoding.as_ptr(),
643                input.as_ptr().cast(),
644                input.len() as ffi::Py_ssize_t,
645                range.start as ffi::Py_ssize_t,
646                range.end as ffi::Py_ssize_t,
647                reason.as_ptr(),
648            )
649            .assume_owned_or_err(py)
650        }
651        .downcast_into()
652    }
653
654    /// Deprecated name for [`PyUnicodeDecodeError::new`].
655    #[deprecated(since = "0.23.0", note = "renamed to `PyUnicodeDecodeError::new`")]
656    #[inline]
657    pub fn new_bound<'py>(
658        py: Python<'py>,
659        encoding: &CStr,
660        input: &[u8],
661        range: ops::Range<usize>,
662        reason: &CStr,
663    ) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
664        Self::new(py, encoding, input, range, reason)
665    }
666
667    /// Creates a Python `UnicodeDecodeError` from a Rust UTF-8 decoding error.
668    ///
669    /// # Examples
670    ///
671    /// ```
672    /// #![cfg_attr(invalid_from_utf8_lint, allow(invalid_from_utf8))]
673    /// use pyo3::prelude::*;
674    /// use pyo3::exceptions::PyUnicodeDecodeError;
675    ///
676    /// # fn main() -> PyResult<()> {
677    /// Python::with_gil(|py| {
678    ///     let invalid_utf8 = b"fo\xd8o";
679    ///     let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
680    ///     let decode_err = PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err)?;
681    ///     assert_eq!(
682    ///         decode_err.to_string(),
683    ///         "'utf-8' codec can't decode byte 0xd8 in position 2: invalid utf-8"
684    ///     );
685    ///     Ok(())
686    /// })
687    /// # }
688    pub fn new_utf8<'py>(
689        py: Python<'py>,
690        input: &[u8],
691        err: std::str::Utf8Error,
692    ) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
693        let pos = err.valid_up_to();
694        PyUnicodeDecodeError::new(
695            py,
696            ffi::c_str!("utf-8"),
697            input,
698            pos..(pos + 1),
699            ffi::c_str!("invalid utf-8"),
700        )
701    }
702
703    /// Deprecated name for [`PyUnicodeDecodeError::new_utf8`].
704    #[deprecated(since = "0.23.0", note = "renamed to `PyUnicodeDecodeError::new_utf8`")]
705    #[inline]
706    pub fn new_utf8_bound<'py>(
707        py: Python<'py>,
708        input: &[u8],
709        err: std::str::Utf8Error,
710    ) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
711        Self::new_utf8(py, input, err)
712    }
713}
714
715impl_native_exception!(PyWarning, PyExc_Warning, native_doc!("Warning"));
716impl_native_exception!(PyUserWarning, PyExc_UserWarning, native_doc!("UserWarning"));
717impl_native_exception!(
718    PyDeprecationWarning,
719    PyExc_DeprecationWarning,
720    native_doc!("DeprecationWarning")
721);
722impl_native_exception!(
723    PyPendingDeprecationWarning,
724    PyExc_PendingDeprecationWarning,
725    native_doc!("PendingDeprecationWarning")
726);
727impl_native_exception!(
728    PySyntaxWarning,
729    PyExc_SyntaxWarning,
730    native_doc!("SyntaxWarning")
731);
732impl_native_exception!(
733    PyRuntimeWarning,
734    PyExc_RuntimeWarning,
735    native_doc!("RuntimeWarning")
736);
737impl_native_exception!(
738    PyFutureWarning,
739    PyExc_FutureWarning,
740    native_doc!("FutureWarning")
741);
742impl_native_exception!(
743    PyImportWarning,
744    PyExc_ImportWarning,
745    native_doc!("ImportWarning")
746);
747impl_native_exception!(
748    PyUnicodeWarning,
749    PyExc_UnicodeWarning,
750    native_doc!("UnicodeWarning")
751);
752impl_native_exception!(
753    PyBytesWarning,
754    PyExc_BytesWarning,
755    native_doc!("BytesWarning")
756);
757impl_native_exception!(
758    PyResourceWarning,
759    PyExc_ResourceWarning,
760    native_doc!("ResourceWarning")
761);
762
763#[cfg(Py_3_10)]
764impl_native_exception!(
765    PyEncodingWarning,
766    PyExc_EncodingWarning,
767    native_doc!("EncodingWarning")
768);
769
770#[cfg(test)]
771macro_rules! test_exception {
772    ($exc_ty:ident $(, |$py:tt| $constructor:expr )?) => {
773        #[allow(non_snake_case)]
774        #[test]
775        fn $exc_ty () {
776            use super::$exc_ty;
777
778            $crate::Python::with_gil(|py| {
779                use $crate::types::PyAnyMethods;
780                let err: $crate::PyErr = {
781                    None
782                    $(
783                        .or(Some({ let $py = py; $constructor }))
784                    )?
785                        .unwrap_or($exc_ty::new_err("a test exception"))
786                };
787
788                assert!(err.is_instance_of::<$exc_ty>(py));
789
790                let value = err.value(py).as_any().downcast::<$exc_ty>().unwrap();
791
792                assert!($crate::PyErr::from(value.clone()).is_instance_of::<$exc_ty>(py));
793            })
794        }
795    };
796}
797
798/// Exceptions defined in Python's [`asyncio`](https://docs.python.org/3/library/asyncio.html)
799/// module.
800pub mod asyncio {
801    import_exception!(asyncio, CancelledError);
802    import_exception!(asyncio, InvalidStateError);
803    import_exception!(asyncio, TimeoutError);
804    import_exception!(asyncio, IncompleteReadError);
805    import_exception!(asyncio, LimitOverrunError);
806    import_exception!(asyncio, QueueEmpty);
807    import_exception!(asyncio, QueueFull);
808
809    #[cfg(test)]
810    mod tests {
811        test_exception!(CancelledError);
812        test_exception!(InvalidStateError);
813        test_exception!(TimeoutError);
814        test_exception!(IncompleteReadError, |_| IncompleteReadError::new_err((
815            "partial", "expected"
816        )));
817        test_exception!(LimitOverrunError, |_| LimitOverrunError::new_err((
818            "message", "consumed"
819        )));
820        test_exception!(QueueEmpty);
821        test_exception!(QueueFull);
822    }
823}
824
825/// Exceptions defined in Python's [`socket`](https://docs.python.org/3/library/socket.html)
826/// module.
827pub mod socket {
828    import_exception!(socket, herror);
829    import_exception!(socket, gaierror);
830    import_exception!(socket, timeout);
831
832    #[cfg(test)]
833    mod tests {
834        test_exception!(herror);
835        test_exception!(gaierror);
836        test_exception!(timeout);
837    }
838}
839
840#[cfg(test)]
841mod tests {
842    use super::*;
843    use crate::types::any::PyAnyMethods;
844    use crate::types::{IntoPyDict, PyDict};
845    use crate::PyErr;
846
847    import_exception_bound!(socket, gaierror);
848    import_exception_bound!(email.errors, MessageError);
849
850    #[test]
851    fn test_check_exception() {
852        Python::with_gil(|py| {
853            let err: PyErr = gaierror::new_err(());
854            let socket = py
855                .import("socket")
856                .map_err(|e| e.display(py))
857                .expect("could not import socket");
858
859            let d = PyDict::new(py);
860            d.set_item("socket", socket)
861                .map_err(|e| e.display(py))
862                .expect("could not setitem");
863
864            d.set_item("exc", err)
865                .map_err(|e| e.display(py))
866                .expect("could not setitem");
867
868            py.run(
869                ffi::c_str!("assert isinstance(exc, socket.gaierror)"),
870                None,
871                Some(&d),
872            )
873            .map_err(|e| e.display(py))
874            .expect("assertion failed");
875        });
876    }
877
878    #[test]
879    fn test_check_exception_nested() {
880        Python::with_gil(|py| {
881            let err: PyErr = MessageError::new_err(());
882            let email = py
883                .import("email")
884                .map_err(|e| e.display(py))
885                .expect("could not import email");
886
887            let d = PyDict::new(py);
888            d.set_item("email", email)
889                .map_err(|e| e.display(py))
890                .expect("could not setitem");
891            d.set_item("exc", err)
892                .map_err(|e| e.display(py))
893                .expect("could not setitem");
894
895            py.run(
896                ffi::c_str!("assert isinstance(exc, email.errors.MessageError)"),
897                None,
898                Some(&d),
899            )
900            .map_err(|e| e.display(py))
901            .expect("assertion failed");
902        });
903    }
904
905    #[test]
906    fn custom_exception() {
907        create_exception!(mymodule, CustomError, PyException);
908
909        Python::with_gil(|py| {
910            let error_type = py.get_type::<CustomError>();
911            let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
912            let type_description: String = py
913                .eval(ffi::c_str!("str(CustomError)"), None, Some(&ctx))
914                .unwrap()
915                .extract()
916                .unwrap();
917            assert_eq!(type_description, "<class 'mymodule.CustomError'>");
918            py.run(
919                ffi::c_str!("assert CustomError('oops').args == ('oops',)"),
920                None,
921                Some(&ctx),
922            )
923            .unwrap();
924            py.run(
925                ffi::c_str!("assert CustomError.__doc__ is None"),
926                None,
927                Some(&ctx),
928            )
929            .unwrap();
930        });
931    }
932
933    #[test]
934    fn custom_exception_dotted_module() {
935        create_exception!(mymodule.exceptions, CustomError, PyException);
936        Python::with_gil(|py| {
937            let error_type = py.get_type::<CustomError>();
938            let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
939            let type_description: String = py
940                .eval(ffi::c_str!("str(CustomError)"), None, Some(&ctx))
941                .unwrap()
942                .extract()
943                .unwrap();
944            assert_eq!(
945                type_description,
946                "<class 'mymodule.exceptions.CustomError'>"
947            );
948        });
949    }
950
951    #[test]
952    fn custom_exception_doc() {
953        create_exception!(mymodule, CustomError, PyException, "Some docs");
954
955        Python::with_gil(|py| {
956            let error_type = py.get_type::<CustomError>();
957            let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
958            let type_description: String = py
959                .eval(ffi::c_str!("str(CustomError)"), None, Some(&ctx))
960                .unwrap()
961                .extract()
962                .unwrap();
963            assert_eq!(type_description, "<class 'mymodule.CustomError'>");
964            py.run(
965                ffi::c_str!("assert CustomError('oops').args == ('oops',)"),
966                None,
967                Some(&ctx),
968            )
969            .unwrap();
970            py.run(
971                ffi::c_str!("assert CustomError.__doc__ == 'Some docs'"),
972                None,
973                Some(&ctx),
974            )
975            .unwrap();
976        });
977    }
978
979    #[test]
980    fn custom_exception_doc_expr() {
981        create_exception!(
982            mymodule,
983            CustomError,
984            PyException,
985            concat!("Some", " more ", stringify!(docs))
986        );
987
988        Python::with_gil(|py| {
989            let error_type = py.get_type::<CustomError>();
990            let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
991            let type_description: String = py
992                .eval(ffi::c_str!("str(CustomError)"), None, Some(&ctx))
993                .unwrap()
994                .extract()
995                .unwrap();
996            assert_eq!(type_description, "<class 'mymodule.CustomError'>");
997            py.run(
998                ffi::c_str!("assert CustomError('oops').args == ('oops',)"),
999                None,
1000                Some(&ctx),
1001            )
1002            .unwrap();
1003            py.run(
1004                ffi::c_str!("assert CustomError.__doc__ == 'Some more docs'"),
1005                None,
1006                Some(&ctx),
1007            )
1008            .unwrap();
1009        });
1010    }
1011
1012    #[test]
1013    fn native_exception_debug() {
1014        Python::with_gil(|py| {
1015            let exc = py
1016                .run(ffi::c_str!("raise Exception('banana')"), None, None)
1017                .expect_err("raising should have given us an error")
1018                .into_value(py)
1019                .into_bound(py);
1020            assert_eq!(
1021                format!("{:?}", exc),
1022                exc.repr().unwrap().extract::<String>().unwrap()
1023            );
1024        });
1025    }
1026
1027    #[test]
1028    fn native_exception_display() {
1029        Python::with_gil(|py| {
1030            let exc = py
1031                .run(ffi::c_str!("raise Exception('banana')"), None, None)
1032                .expect_err("raising should have given us an error")
1033                .into_value(py)
1034                .into_bound(py);
1035            assert_eq!(
1036                exc.to_string(),
1037                exc.str().unwrap().extract::<String>().unwrap()
1038            );
1039        });
1040    }
1041
1042    #[test]
1043    fn unicode_decode_error() {
1044        let invalid_utf8 = b"fo\xd8o";
1045        #[cfg_attr(invalid_from_utf8_lint, allow(invalid_from_utf8))]
1046        let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
1047        Python::with_gil(|py| {
1048            let decode_err = PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err).unwrap();
1049            assert_eq!(
1050                format!("{:?}", decode_err),
1051                "UnicodeDecodeError('utf-8', b'fo\\xd8o', 2, 3, 'invalid utf-8')"
1052            );
1053
1054            // Restoring should preserve the same error
1055            let e: PyErr = decode_err.into();
1056            e.restore(py);
1057
1058            assert_eq!(
1059                PyErr::fetch(py).to_string(),
1060                "UnicodeDecodeError: \'utf-8\' codec can\'t decode byte 0xd8 in position 2: invalid utf-8"
1061            );
1062        });
1063    }
1064    #[cfg(Py_3_11)]
1065    test_exception!(PyBaseExceptionGroup, |_| PyBaseExceptionGroup::new_err((
1066        "msg",
1067        vec![PyValueError::new_err("err")]
1068    )));
1069    test_exception!(PyBaseException);
1070    test_exception!(PyException);
1071    test_exception!(PyStopAsyncIteration);
1072    test_exception!(PyStopIteration);
1073    test_exception!(PyGeneratorExit);
1074    test_exception!(PyArithmeticError);
1075    test_exception!(PyLookupError);
1076    test_exception!(PyAssertionError);
1077    test_exception!(PyAttributeError);
1078    test_exception!(PyBufferError);
1079    test_exception!(PyEOFError);
1080    test_exception!(PyFloatingPointError);
1081    test_exception!(PyOSError);
1082    test_exception!(PyImportError);
1083    test_exception!(PyModuleNotFoundError);
1084    test_exception!(PyIndexError);
1085    test_exception!(PyKeyError);
1086    test_exception!(PyKeyboardInterrupt);
1087    test_exception!(PyMemoryError);
1088    test_exception!(PyNameError);
1089    test_exception!(PyOverflowError);
1090    test_exception!(PyRuntimeError);
1091    test_exception!(PyRecursionError);
1092    test_exception!(PyNotImplementedError);
1093    test_exception!(PySyntaxError);
1094    test_exception!(PyReferenceError);
1095    test_exception!(PySystemError);
1096    test_exception!(PySystemExit);
1097    test_exception!(PyTypeError);
1098    test_exception!(PyUnboundLocalError);
1099    test_exception!(PyUnicodeError);
1100    test_exception!(PyUnicodeDecodeError, |py| {
1101        let invalid_utf8 = b"fo\xd8o";
1102        #[cfg_attr(invalid_from_utf8_lint, allow(invalid_from_utf8))]
1103        let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
1104        PyErr::from_value(
1105            PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err)
1106                .unwrap()
1107                .into_any(),
1108        )
1109    });
1110    test_exception!(PyUnicodeEncodeError, |py| py
1111        .eval(ffi::c_str!("chr(40960).encode('ascii')"), None, None)
1112        .unwrap_err());
1113    test_exception!(PyUnicodeTranslateError, |_| {
1114        PyUnicodeTranslateError::new_err(("\u{3042}", 0, 1, "ouch"))
1115    });
1116    test_exception!(PyValueError);
1117    test_exception!(PyZeroDivisionError);
1118    test_exception!(PyBlockingIOError);
1119    test_exception!(PyBrokenPipeError);
1120    test_exception!(PyChildProcessError);
1121    test_exception!(PyConnectionError);
1122    test_exception!(PyConnectionAbortedError);
1123    test_exception!(PyConnectionRefusedError);
1124    test_exception!(PyConnectionResetError);
1125    test_exception!(PyFileExistsError);
1126    test_exception!(PyFileNotFoundError);
1127    test_exception!(PyInterruptedError);
1128    test_exception!(PyIsADirectoryError);
1129    test_exception!(PyNotADirectoryError);
1130    test_exception!(PyPermissionError);
1131    test_exception!(PyProcessLookupError);
1132    test_exception!(PyTimeoutError);
1133    test_exception!(PyEnvironmentError);
1134    test_exception!(PyIOError);
1135    #[cfg(windows)]
1136    test_exception!(PyWindowsError);
1137
1138    test_exception!(PyWarning);
1139    test_exception!(PyUserWarning);
1140    test_exception!(PyDeprecationWarning);
1141    test_exception!(PyPendingDeprecationWarning);
1142    test_exception!(PySyntaxWarning);
1143    test_exception!(PyRuntimeWarning);
1144    test_exception!(PyFutureWarning);
1145    test_exception!(PyImportWarning);
1146    test_exception!(PyUnicodeWarning);
1147    test_exception!(PyBytesWarning);
1148    #[cfg(Py_3_10)]
1149    test_exception!(PyEncodingWarning);
1150}