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 #[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);
512impl_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 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(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 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(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
800pub 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
827pub 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 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}