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