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        #[repr(transparent)]
278        #[allow(clippy::upper_case_acronyms)]
279        pub struct $name($crate::PyAny);
280
281        $crate::impl_exception_boilerplate!($name);
282        $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject } $(, #checkfunction=$checkfunction)?);
283        $crate::pyobject_subclassable_native_type!($name, $layout);
284    );
285    ($name:ident, $exc_name:ident, $doc:expr) => (
286        impl_native_exception!($name, $exc_name, $doc, $crate::ffi::PyBaseExceptionObject);
287    )
288);
289
290#[cfg(windows)]
291macro_rules! impl_windows_native_exception (
292    ($name:ident, $exc_name:ident, $doc:expr, $layout:path) => (
293        #[cfg(windows)]
294        #[doc = $doc]
295        #[repr(transparent)]
296        #[allow(clippy::upper_case_acronyms)]
297        pub struct $name($crate::PyAny);
298
299        $crate::impl_exception_boilerplate!($name);
300        $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject });
301    );
302    ($name:ident, $exc_name:ident, $doc:expr) => (
303        impl_windows_native_exception!($name, $exc_name, $doc, $crate::ffi::PyBaseExceptionObject);
304    )
305);
306
307macro_rules! native_doc(
308    ($name: literal, $alt: literal) => (
309        concat!(
310"Represents Python's [`", $name, "`](https://docs.python.org/3/library/exceptions.html#", $name, ") exception.
311
312", $alt
313        )
314    );
315    ($name: literal) => (
316        concat!(
317"
318Represents Python's [`", $name, "`](https://docs.python.org/3/library/exceptions.html#", $name, ") exception.
319
320# Example: Raising ", $name, " from Rust
321
322This exception can be sent to Python code by converting it into a
323[`PyErr`](crate::PyErr), where Python code can then catch it.
324```
325use pyo3::prelude::*;
326use pyo3::exceptions::Py", $name, ";
327
328#[pyfunction]
329fn always_throws() -> PyResult<()> {
330    let message = \"I'm ", $name ,", and I was raised from Rust.\";
331    Err(Py", $name, "::new_err(message))
332}
333#
334# Python::with_gil(|py| {
335#     let fun = pyo3::wrap_pyfunction!(always_throws, py).unwrap();
336#     let err = fun.call0().expect_err(\"called a function that should always return an error but the return value was Ok\");
337#     assert!(err.is_instance_of::<Py", $name, ">(py))
338# });
339```
340
341Python code:
342 ```python
343 from my_module import always_throws
344
345try:
346    always_throws()
347except ", $name, " as e:
348    print(f\"Caught an exception: {e}\")
349```
350
351# Example: Catching ", $name, " in Rust
352
353```
354use pyo3::prelude::*;
355use pyo3::exceptions::Py", $name, ";
356use pyo3::ffi::c_str;
357
358Python::with_gil(|py| {
359    let result: PyResult<()> = py.run(c_str!(\"raise ", $name, "\"), None, None);
360
361    let error_type = match result {
362        Ok(_) => \"Not an error\",
363        Err(error) if error.is_instance_of::<Py", $name, ">(py) => \"" , $name, "\",
364        Err(_) => \"Some other error\",
365    };
366
367    assert_eq!(error_type, \"", $name, "\");
368});
369```
370"
371        )
372    );
373);
374
375impl_native_exception!(
376    PyBaseException,
377    PyExc_BaseException,
378    native_doc!("BaseException"),
379    ffi::PyBaseExceptionObject,
380    #checkfunction=ffi::PyExceptionInstance_Check
381);
382impl_native_exception!(PyException, PyExc_Exception, native_doc!("Exception"));
383impl_native_exception!(
384    PyStopAsyncIteration,
385    PyExc_StopAsyncIteration,
386    native_doc!("StopAsyncIteration")
387);
388impl_native_exception!(
389    PyStopIteration,
390    PyExc_StopIteration,
391    native_doc!("StopIteration"),
392    ffi::PyStopIterationObject
393);
394impl_native_exception!(
395    PyGeneratorExit,
396    PyExc_GeneratorExit,
397    native_doc!("GeneratorExit")
398);
399impl_native_exception!(
400    PyArithmeticError,
401    PyExc_ArithmeticError,
402    native_doc!("ArithmeticError")
403);
404impl_native_exception!(PyLookupError, PyExc_LookupError, native_doc!("LookupError"));
405
406impl_native_exception!(
407    PyAssertionError,
408    PyExc_AssertionError,
409    native_doc!("AssertionError")
410);
411impl_native_exception!(
412    PyAttributeError,
413    PyExc_AttributeError,
414    native_doc!("AttributeError")
415);
416impl_native_exception!(PyBufferError, PyExc_BufferError, native_doc!("BufferError"));
417impl_native_exception!(PyEOFError, PyExc_EOFError, native_doc!("EOFError"));
418impl_native_exception!(
419    PyFloatingPointError,
420    PyExc_FloatingPointError,
421    native_doc!("FloatingPointError")
422);
423#[cfg(not(any(PyPy, GraalPy)))]
424impl_native_exception!(
425    PyOSError,
426    PyExc_OSError,
427    native_doc!("OSError"),
428    ffi::PyOSErrorObject
429);
430#[cfg(any(PyPy, GraalPy))]
431impl_native_exception!(PyOSError, PyExc_OSError, native_doc!("OSError"));
432impl_native_exception!(PyImportError, PyExc_ImportError, native_doc!("ImportError"));
433
434impl_native_exception!(
435    PyModuleNotFoundError,
436    PyExc_ModuleNotFoundError,
437    native_doc!("ModuleNotFoundError")
438);
439
440impl_native_exception!(PyIndexError, PyExc_IndexError, native_doc!("IndexError"));
441impl_native_exception!(PyKeyError, PyExc_KeyError, native_doc!("KeyError"));
442impl_native_exception!(
443    PyKeyboardInterrupt,
444    PyExc_KeyboardInterrupt,
445    native_doc!("KeyboardInterrupt")
446);
447impl_native_exception!(PyMemoryError, PyExc_MemoryError, native_doc!("MemoryError"));
448impl_native_exception!(PyNameError, PyExc_NameError, native_doc!("NameError"));
449impl_native_exception!(
450    PyOverflowError,
451    PyExc_OverflowError,
452    native_doc!("OverflowError")
453);
454impl_native_exception!(
455    PyRuntimeError,
456    PyExc_RuntimeError,
457    native_doc!("RuntimeError")
458);
459impl_native_exception!(
460    PyRecursionError,
461    PyExc_RecursionError,
462    native_doc!("RecursionError")
463);
464impl_native_exception!(
465    PyNotImplementedError,
466    PyExc_NotImplementedError,
467    native_doc!("NotImplementedError")
468);
469#[cfg(not(any(PyPy, GraalPy)))]
470impl_native_exception!(
471    PySyntaxError,
472    PyExc_SyntaxError,
473    native_doc!("SyntaxError"),
474    ffi::PySyntaxErrorObject
475);
476#[cfg(any(PyPy, GraalPy))]
477impl_native_exception!(PySyntaxError, PyExc_SyntaxError, native_doc!("SyntaxError"));
478impl_native_exception!(
479    PyReferenceError,
480    PyExc_ReferenceError,
481    native_doc!("ReferenceError")
482);
483impl_native_exception!(PySystemError, PyExc_SystemError, native_doc!("SystemError"));
484#[cfg(not(any(PyPy, GraalPy)))]
485impl_native_exception!(
486    PySystemExit,
487    PyExc_SystemExit,
488    native_doc!("SystemExit"),
489    ffi::PySystemExitObject
490);
491#[cfg(any(PyPy, GraalPy))]
492impl_native_exception!(PySystemExit, PyExc_SystemExit, native_doc!("SystemExit"));
493impl_native_exception!(PyTypeError, PyExc_TypeError, native_doc!("TypeError"));
494impl_native_exception!(
495    PyUnboundLocalError,
496    PyExc_UnboundLocalError,
497    native_doc!("UnboundLocalError")
498);
499#[cfg(not(any(PyPy, GraalPy)))]
500impl_native_exception!(
501    PyUnicodeError,
502    PyExc_UnicodeError,
503    native_doc!("UnicodeError"),
504    ffi::PyUnicodeErrorObject
505);
506#[cfg(any(PyPy, GraalPy))]
507impl_native_exception!(
508    PyUnicodeError,
509    PyExc_UnicodeError,
510    native_doc!("UnicodeError")
511);
512// these four errors need arguments, so they're too annoying to write tests for using macros...
513impl_native_exception!(
514    PyUnicodeDecodeError,
515    PyExc_UnicodeDecodeError,
516    native_doc!("UnicodeDecodeError", "")
517);
518impl_native_exception!(
519    PyUnicodeEncodeError,
520    PyExc_UnicodeEncodeError,
521    native_doc!("UnicodeEncodeError", "")
522);
523impl_native_exception!(
524    PyUnicodeTranslateError,
525    PyExc_UnicodeTranslateError,
526    native_doc!("UnicodeTranslateError", "")
527);
528#[cfg(Py_3_11)]
529impl_native_exception!(
530    PyBaseExceptionGroup,
531    PyExc_BaseExceptionGroup,
532    native_doc!("BaseExceptionGroup", "")
533);
534impl_native_exception!(PyValueError, PyExc_ValueError, native_doc!("ValueError"));
535impl_native_exception!(
536    PyZeroDivisionError,
537    PyExc_ZeroDivisionError,
538    native_doc!("ZeroDivisionError")
539);
540
541impl_native_exception!(
542    PyBlockingIOError,
543    PyExc_BlockingIOError,
544    native_doc!("BlockingIOError")
545);
546impl_native_exception!(
547    PyBrokenPipeError,
548    PyExc_BrokenPipeError,
549    native_doc!("BrokenPipeError")
550);
551impl_native_exception!(
552    PyChildProcessError,
553    PyExc_ChildProcessError,
554    native_doc!("ChildProcessError")
555);
556impl_native_exception!(
557    PyConnectionError,
558    PyExc_ConnectionError,
559    native_doc!("ConnectionError")
560);
561impl_native_exception!(
562    PyConnectionAbortedError,
563    PyExc_ConnectionAbortedError,
564    native_doc!("ConnectionAbortedError")
565);
566impl_native_exception!(
567    PyConnectionRefusedError,
568    PyExc_ConnectionRefusedError,
569    native_doc!("ConnectionRefusedError")
570);
571impl_native_exception!(
572    PyConnectionResetError,
573    PyExc_ConnectionResetError,
574    native_doc!("ConnectionResetError")
575);
576impl_native_exception!(
577    PyFileExistsError,
578    PyExc_FileExistsError,
579    native_doc!("FileExistsError")
580);
581impl_native_exception!(
582    PyFileNotFoundError,
583    PyExc_FileNotFoundError,
584    native_doc!("FileNotFoundError")
585);
586impl_native_exception!(
587    PyInterruptedError,
588    PyExc_InterruptedError,
589    native_doc!("InterruptedError")
590);
591impl_native_exception!(
592    PyIsADirectoryError,
593    PyExc_IsADirectoryError,
594    native_doc!("IsADirectoryError")
595);
596impl_native_exception!(
597    PyNotADirectoryError,
598    PyExc_NotADirectoryError,
599    native_doc!("NotADirectoryError")
600);
601impl_native_exception!(
602    PyPermissionError,
603    PyExc_PermissionError,
604    native_doc!("PermissionError")
605);
606impl_native_exception!(
607    PyProcessLookupError,
608    PyExc_ProcessLookupError,
609    native_doc!("ProcessLookupError")
610);
611impl_native_exception!(
612    PyTimeoutError,
613    PyExc_TimeoutError,
614    native_doc!("TimeoutError")
615);
616
617impl_native_exception!(
618    PyEnvironmentError,
619    PyExc_EnvironmentError,
620    native_doc!("EnvironmentError")
621);
622impl_native_exception!(PyIOError, PyExc_IOError, native_doc!("IOError"));
623
624#[cfg(windows)]
625impl_windows_native_exception!(
626    PyWindowsError,
627    PyExc_WindowsError,
628    native_doc!("WindowsError")
629);
630
631impl PyUnicodeDecodeError {
632    /// Creates a Python `UnicodeDecodeError`.
633    pub fn new<'py>(
634        py: Python<'py>,
635        encoding: &CStr,
636        input: &[u8],
637        range: ops::Range<usize>,
638        reason: &CStr,
639    ) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
640        use crate::ffi_ptr_ext::FfiPtrExt;
641        use crate::py_result_ext::PyResultExt;
642        unsafe {
643            ffi::PyUnicodeDecodeError_Create(
644                encoding.as_ptr(),
645                input.as_ptr().cast(),
646                input.len() as ffi::Py_ssize_t,
647                range.start as ffi::Py_ssize_t,
648                range.end as ffi::Py_ssize_t,
649                reason.as_ptr(),
650            )
651            .assume_owned_or_err(py)
652        }
653        .downcast_into()
654    }
655
656    /// Deprecated name for [`PyUnicodeDecodeError::new`].
657    #[deprecated(since = "0.23.0", note = "renamed to `PyUnicodeDecodeError::new`")]
658    #[inline]
659    pub fn new_bound<'py>(
660        py: Python<'py>,
661        encoding: &CStr,
662        input: &[u8],
663        range: ops::Range<usize>,
664        reason: &CStr,
665    ) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
666        Self::new(py, encoding, input, range, reason)
667    }
668
669    /// Creates a Python `UnicodeDecodeError` from a Rust UTF-8 decoding error.
670    ///
671    /// # Examples
672    ///
673    /// ```
674    /// #![cfg_attr(invalid_from_utf8_lint, allow(invalid_from_utf8))]
675    /// use pyo3::prelude::*;
676    /// use pyo3::exceptions::PyUnicodeDecodeError;
677    ///
678    /// # fn main() -> PyResult<()> {
679    /// Python::with_gil(|py| {
680    ///     let invalid_utf8 = b"fo\xd8o";
681    ///     let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
682    ///     let decode_err = PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err)?;
683    ///     assert_eq!(
684    ///         decode_err.to_string(),
685    ///         "'utf-8' codec can't decode byte 0xd8 in position 2: invalid utf-8"
686    ///     );
687    ///     Ok(())
688    /// })
689    /// # }
690    pub fn new_utf8<'py>(
691        py: Python<'py>,
692        input: &[u8],
693        err: std::str::Utf8Error,
694    ) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
695        let pos = err.valid_up_to();
696        PyUnicodeDecodeError::new(
697            py,
698            ffi::c_str!("utf-8"),
699            input,
700            pos..(pos + 1),
701            ffi::c_str!("invalid utf-8"),
702        )
703    }
704
705    /// Deprecated name for [`PyUnicodeDecodeError::new_utf8`].
706    #[deprecated(since = "0.23.0", note = "renamed to `PyUnicodeDecodeError::new_utf8`")]
707    #[inline]
708    pub fn new_utf8_bound<'py>(
709        py: Python<'py>,
710        input: &[u8],
711        err: std::str::Utf8Error,
712    ) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
713        Self::new_utf8(py, input, err)
714    }
715}
716
717impl_native_exception!(PyWarning, PyExc_Warning, native_doc!("Warning"));
718impl_native_exception!(PyUserWarning, PyExc_UserWarning, native_doc!("UserWarning"));
719impl_native_exception!(
720    PyDeprecationWarning,
721    PyExc_DeprecationWarning,
722    native_doc!("DeprecationWarning")
723);
724impl_native_exception!(
725    PyPendingDeprecationWarning,
726    PyExc_PendingDeprecationWarning,
727    native_doc!("PendingDeprecationWarning")
728);
729impl_native_exception!(
730    PySyntaxWarning,
731    PyExc_SyntaxWarning,
732    native_doc!("SyntaxWarning")
733);
734impl_native_exception!(
735    PyRuntimeWarning,
736    PyExc_RuntimeWarning,
737    native_doc!("RuntimeWarning")
738);
739impl_native_exception!(
740    PyFutureWarning,
741    PyExc_FutureWarning,
742    native_doc!("FutureWarning")
743);
744impl_native_exception!(
745    PyImportWarning,
746    PyExc_ImportWarning,
747    native_doc!("ImportWarning")
748);
749impl_native_exception!(
750    PyUnicodeWarning,
751    PyExc_UnicodeWarning,
752    native_doc!("UnicodeWarning")
753);
754impl_native_exception!(
755    PyBytesWarning,
756    PyExc_BytesWarning,
757    native_doc!("BytesWarning")
758);
759impl_native_exception!(
760    PyResourceWarning,
761    PyExc_ResourceWarning,
762    native_doc!("ResourceWarning")
763);
764
765#[cfg(Py_3_10)]
766impl_native_exception!(
767    PyEncodingWarning,
768    PyExc_EncodingWarning,
769    native_doc!("EncodingWarning")
770);
771
772#[cfg(test)]
773macro_rules! test_exception {
774    ($exc_ty:ident $(, |$py:tt| $constructor:expr )?) => {
775        #[allow(non_snake_case)]
776        #[test]
777        fn $exc_ty () {
778            use super::$exc_ty;
779
780            $crate::Python::with_gil(|py| {
781                use $crate::types::PyAnyMethods;
782                let err: $crate::PyErr = {
783                    None
784                    $(
785                        .or(Some({ let $py = py; $constructor }))
786                    )?
787                        .unwrap_or($exc_ty::new_err("a test exception"))
788                };
789
790                assert!(err.is_instance_of::<$exc_ty>(py));
791
792                let value = err.value(py).as_any().downcast::<$exc_ty>().unwrap();
793
794                assert!($crate::PyErr::from(value.clone()).is_instance_of::<$exc_ty>(py));
795            })
796        }
797    };
798}
799
800/// Exceptions defined in Python's [`asyncio`](https://docs.python.org/3/library/asyncio.html)
801/// module.
802pub mod asyncio {
803    import_exception!(asyncio, CancelledError);
804    import_exception!(asyncio, InvalidStateError);
805    import_exception!(asyncio, TimeoutError);
806    import_exception!(asyncio, IncompleteReadError);
807    import_exception!(asyncio, LimitOverrunError);
808    import_exception!(asyncio, QueueEmpty);
809    import_exception!(asyncio, QueueFull);
810
811    #[cfg(test)]
812    mod tests {
813        test_exception!(CancelledError);
814        test_exception!(InvalidStateError);
815        test_exception!(TimeoutError);
816        test_exception!(IncompleteReadError, |_| IncompleteReadError::new_err((
817            "partial", "expected"
818        )));
819        test_exception!(LimitOverrunError, |_| LimitOverrunError::new_err((
820            "message", "consumed"
821        )));
822        test_exception!(QueueEmpty);
823        test_exception!(QueueFull);
824    }
825}
826
827/// Exceptions defined in Python's [`socket`](https://docs.python.org/3/library/socket.html)
828/// module.
829pub mod socket {
830    import_exception!(socket, herror);
831    import_exception!(socket, gaierror);
832    import_exception!(socket, timeout);
833
834    #[cfg(test)]
835    mod tests {
836        test_exception!(herror);
837        test_exception!(gaierror);
838        test_exception!(timeout);
839    }
840}
841
842#[cfg(test)]
843mod tests {
844    use super::*;
845    use crate::types::any::PyAnyMethods;
846    use crate::types::{IntoPyDict, PyDict};
847    use crate::PyErr;
848
849    import_exception_bound!(socket, gaierror);
850    import_exception_bound!(email.errors, MessageError);
851
852    #[test]
853    fn test_check_exception() {
854        Python::with_gil(|py| {
855            let err: PyErr = gaierror::new_err(());
856            let socket = py
857                .import("socket")
858                .map_err(|e| e.display(py))
859                .expect("could not import socket");
860
861            let d = PyDict::new(py);
862            d.set_item("socket", socket)
863                .map_err(|e| e.display(py))
864                .expect("could not setitem");
865
866            d.set_item("exc", err)
867                .map_err(|e| e.display(py))
868                .expect("could not setitem");
869
870            py.run(
871                ffi::c_str!("assert isinstance(exc, socket.gaierror)"),
872                None,
873                Some(&d),
874            )
875            .map_err(|e| e.display(py))
876            .expect("assertion failed");
877        });
878    }
879
880    #[test]
881    fn test_check_exception_nested() {
882        Python::with_gil(|py| {
883            let err: PyErr = MessageError::new_err(());
884            let email = py
885                .import("email")
886                .map_err(|e| e.display(py))
887                .expect("could not import email");
888
889            let d = PyDict::new(py);
890            d.set_item("email", email)
891                .map_err(|e| e.display(py))
892                .expect("could not setitem");
893            d.set_item("exc", err)
894                .map_err(|e| e.display(py))
895                .expect("could not setitem");
896
897            py.run(
898                ffi::c_str!("assert isinstance(exc, email.errors.MessageError)"),
899                None,
900                Some(&d),
901            )
902            .map_err(|e| e.display(py))
903            .expect("assertion failed");
904        });
905    }
906
907    #[test]
908    fn custom_exception() {
909        create_exception!(mymodule, CustomError, PyException);
910
911        Python::with_gil(|py| {
912            let error_type = py.get_type::<CustomError>();
913            let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
914            let type_description: String = py
915                .eval(ffi::c_str!("str(CustomError)"), None, Some(&ctx))
916                .unwrap()
917                .extract()
918                .unwrap();
919            assert_eq!(type_description, "<class 'mymodule.CustomError'>");
920            py.run(
921                ffi::c_str!("assert CustomError('oops').args == ('oops',)"),
922                None,
923                Some(&ctx),
924            )
925            .unwrap();
926            py.run(
927                ffi::c_str!("assert CustomError.__doc__ is None"),
928                None,
929                Some(&ctx),
930            )
931            .unwrap();
932        });
933    }
934
935    #[test]
936    fn custom_exception_dotted_module() {
937        create_exception!(mymodule.exceptions, CustomError, PyException);
938        Python::with_gil(|py| {
939            let error_type = py.get_type::<CustomError>();
940            let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
941            let type_description: String = py
942                .eval(ffi::c_str!("str(CustomError)"), None, Some(&ctx))
943                .unwrap()
944                .extract()
945                .unwrap();
946            assert_eq!(
947                type_description,
948                "<class 'mymodule.exceptions.CustomError'>"
949            );
950        });
951    }
952
953    #[test]
954    fn custom_exception_doc() {
955        create_exception!(mymodule, CustomError, PyException, "Some docs");
956
957        Python::with_gil(|py| {
958            let error_type = py.get_type::<CustomError>();
959            let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
960            let type_description: String = py
961                .eval(ffi::c_str!("str(CustomError)"), None, Some(&ctx))
962                .unwrap()
963                .extract()
964                .unwrap();
965            assert_eq!(type_description, "<class 'mymodule.CustomError'>");
966            py.run(
967                ffi::c_str!("assert CustomError('oops').args == ('oops',)"),
968                None,
969                Some(&ctx),
970            )
971            .unwrap();
972            py.run(
973                ffi::c_str!("assert CustomError.__doc__ == 'Some docs'"),
974                None,
975                Some(&ctx),
976            )
977            .unwrap();
978        });
979    }
980
981    #[test]
982    fn custom_exception_doc_expr() {
983        create_exception!(
984            mymodule,
985            CustomError,
986            PyException,
987            concat!("Some", " more ", stringify!(docs))
988        );
989
990        Python::with_gil(|py| {
991            let error_type = py.get_type::<CustomError>();
992            let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
993            let type_description: String = py
994                .eval(ffi::c_str!("str(CustomError)"), None, Some(&ctx))
995                .unwrap()
996                .extract()
997                .unwrap();
998            assert_eq!(type_description, "<class 'mymodule.CustomError'>");
999            py.run(
1000                ffi::c_str!("assert CustomError('oops').args == ('oops',)"),
1001                None,
1002                Some(&ctx),
1003            )
1004            .unwrap();
1005            py.run(
1006                ffi::c_str!("assert CustomError.__doc__ == 'Some more docs'"),
1007                None,
1008                Some(&ctx),
1009            )
1010            .unwrap();
1011        });
1012    }
1013
1014    #[test]
1015    fn native_exception_debug() {
1016        Python::with_gil(|py| {
1017            let exc = py
1018                .run(ffi::c_str!("raise Exception('banana')"), None, None)
1019                .expect_err("raising should have given us an error")
1020                .into_value(py)
1021                .into_bound(py);
1022            assert_eq!(
1023                format!("{:?}", exc),
1024                exc.repr().unwrap().extract::<String>().unwrap()
1025            );
1026        });
1027    }
1028
1029    #[test]
1030    fn native_exception_display() {
1031        Python::with_gil(|py| {
1032            let exc = py
1033                .run(ffi::c_str!("raise Exception('banana')"), None, None)
1034                .expect_err("raising should have given us an error")
1035                .into_value(py)
1036                .into_bound(py);
1037            assert_eq!(
1038                exc.to_string(),
1039                exc.str().unwrap().extract::<String>().unwrap()
1040            );
1041        });
1042    }
1043
1044    #[test]
1045    fn unicode_decode_error() {
1046        let invalid_utf8 = b"fo\xd8o";
1047        #[cfg_attr(invalid_from_utf8_lint, allow(invalid_from_utf8))]
1048        let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
1049        Python::with_gil(|py| {
1050            let decode_err = PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err).unwrap();
1051            assert_eq!(
1052                format!("{:?}", decode_err),
1053                "UnicodeDecodeError('utf-8', b'fo\\xd8o', 2, 3, 'invalid utf-8')"
1054            );
1055
1056            // Restoring should preserve the same error
1057            let e: PyErr = decode_err.into();
1058            e.restore(py);
1059
1060            assert_eq!(
1061                PyErr::fetch(py).to_string(),
1062                "UnicodeDecodeError: \'utf-8\' codec can\'t decode byte 0xd8 in position 2: invalid utf-8"
1063            );
1064        });
1065    }
1066    #[cfg(Py_3_11)]
1067    test_exception!(PyBaseExceptionGroup, |_| PyBaseExceptionGroup::new_err((
1068        "msg",
1069        vec![PyValueError::new_err("err")]
1070    )));
1071    test_exception!(PyBaseException);
1072    test_exception!(PyException);
1073    test_exception!(PyStopAsyncIteration);
1074    test_exception!(PyStopIteration);
1075    test_exception!(PyGeneratorExit);
1076    test_exception!(PyArithmeticError);
1077    test_exception!(PyLookupError);
1078    test_exception!(PyAssertionError);
1079    test_exception!(PyAttributeError);
1080    test_exception!(PyBufferError);
1081    test_exception!(PyEOFError);
1082    test_exception!(PyFloatingPointError);
1083    test_exception!(PyOSError);
1084    test_exception!(PyImportError);
1085    test_exception!(PyModuleNotFoundError);
1086    test_exception!(PyIndexError);
1087    test_exception!(PyKeyError);
1088    test_exception!(PyKeyboardInterrupt);
1089    test_exception!(PyMemoryError);
1090    test_exception!(PyNameError);
1091    test_exception!(PyOverflowError);
1092    test_exception!(PyRuntimeError);
1093    test_exception!(PyRecursionError);
1094    test_exception!(PyNotImplementedError);
1095    test_exception!(PySyntaxError);
1096    test_exception!(PyReferenceError);
1097    test_exception!(PySystemError);
1098    test_exception!(PySystemExit);
1099    test_exception!(PyTypeError);
1100    test_exception!(PyUnboundLocalError);
1101    test_exception!(PyUnicodeError);
1102    test_exception!(PyUnicodeDecodeError, |py| {
1103        let invalid_utf8 = b"fo\xd8o";
1104        #[cfg_attr(invalid_from_utf8_lint, allow(invalid_from_utf8))]
1105        let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
1106        PyErr::from_value(
1107            PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err)
1108                .unwrap()
1109                .into_any(),
1110        )
1111    });
1112    test_exception!(PyUnicodeEncodeError, |py| py
1113        .eval(ffi::c_str!("chr(40960).encode('ascii')"), None, None)
1114        .unwrap_err());
1115    test_exception!(PyUnicodeTranslateError, |_| {
1116        PyUnicodeTranslateError::new_err(("\u{3042}", 0, 1, "ouch"))
1117    });
1118    test_exception!(PyValueError);
1119    test_exception!(PyZeroDivisionError);
1120    test_exception!(PyBlockingIOError);
1121    test_exception!(PyBrokenPipeError);
1122    test_exception!(PyChildProcessError);
1123    test_exception!(PyConnectionError);
1124    test_exception!(PyConnectionAbortedError);
1125    test_exception!(PyConnectionRefusedError);
1126    test_exception!(PyConnectionResetError);
1127    test_exception!(PyFileExistsError);
1128    test_exception!(PyFileNotFoundError);
1129    test_exception!(PyInterruptedError);
1130    test_exception!(PyIsADirectoryError);
1131    test_exception!(PyNotADirectoryError);
1132    test_exception!(PyPermissionError);
1133    test_exception!(PyProcessLookupError);
1134    test_exception!(PyTimeoutError);
1135    test_exception!(PyEnvironmentError);
1136    test_exception!(PyIOError);
1137    #[cfg(windows)]
1138    test_exception!(PyWindowsError);
1139
1140    test_exception!(PyWarning);
1141    test_exception!(PyUserWarning);
1142    test_exception!(PyDeprecationWarning);
1143    test_exception!(PyPendingDeprecationWarning);
1144    test_exception!(PySyntaxWarning);
1145    test_exception!(PyRuntimeWarning);
1146    test_exception!(PyFutureWarning);
1147    test_exception!(PyImportWarning);
1148    test_exception!(PyUnicodeWarning);
1149    test_exception!(PyBytesWarning);
1150    #[cfg(Py_3_10)]
1151    test_exception!(PyEncodingWarning);
1152}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here