1use crate::{ffi, Bound, PyResult, Python};
13use std::ffi::CStr;
14use std::ops;
15
16#[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 #[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#[macro_export]
74macro_rules! import_exception {
75 ($module: expr, $name: ident) => {
76 #[repr(transparent)]
83 #[allow(non_camel_case_types)] 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#[macro_export]
110macro_rules! import_exception_bound {
111 ($module: expr, $name: ident) => {
112 #[repr(transparent)]
119 #[allow(non_camel_case_types)] 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#[macro_export]
213macro_rules! create_exception {
214 ($module: expr, $name: ident, $base: ty) => {
215 #[repr(transparent)]
216 #[allow(non_camel_case_types)] 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)] #[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#[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);
510impl_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 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(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 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(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
798pub 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
825pub 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 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}