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