1#![allow(clippy::undocumented_unsafe_blocks)]
3
4use crate::{ffi, Bound, PyResult, Python};
16use core::ffi::CStr;
17use core::ops;
18
19#[doc(hidden)]
21#[macro_export]
22macro_rules! impl_exception_boilerplate {
23 ($name: ident) => {
24 impl $name {
25 #[inline]
29 #[allow(dead_code, reason = "user may not call this function")]
30 pub fn new_err<A>(args: A) -> $crate::PyErr
31 where
32 A: $crate::PyErrArguments + ::core::marker::Send + ::core::marker::Sync + 'static,
33 {
34 $crate::PyErr::new::<$name, A>(args)
35 }
36 }
37
38 impl $crate::ToPyErr for $name {}
39 };
40}
41
42#[macro_export]
69macro_rules! import_exception {
70 ($module: expr, $name: ident) => {
71 #[repr(transparent)]
78 #[allow(non_camel_case_types, reason = "matches imported exception name, e.g. `socket.herror`")]
79 pub struct $name($crate::PyAny);
80
81 $crate::impl_exception_boilerplate!($name);
82
83 $crate::pyobject_native_type_core!(
84 $name,
85 $name::type_object_raw,
86 stringify!($name),
87 stringify!($module),
88 #module=::core::option::Option::Some(stringify!($module))
89 );
90
91 impl $name {
92 fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
93 use $crate::types::PyTypeMethods;
94 static TYPE_OBJECT: $crate::impl_::exceptions::ImportedExceptionTypeObject =
95 $crate::impl_::exceptions::ImportedExceptionTypeObject::new(stringify!($module), stringify!($name));
96 TYPE_OBJECT.get(py).as_type_ptr()
97 }
98 }
99 };
100}
101
102#[macro_export]
169macro_rules! create_exception {
170 ($module: expr, $name: ident, $base: ty) => {
171 #[repr(transparent)]
172 pub struct $name($crate::PyAny);
173
174 $crate::impl_exception_boilerplate!($name);
175
176 $crate::create_exception_type_object!($module, $name, $base, None);
177 };
178 ($module: expr, $name: ident, $base: ty, $doc: expr) => {
179 #[repr(transparent)]
180 #[doc = $doc]
181 pub struct $name($crate::PyAny);
182
183 $crate::impl_exception_boilerplate!($name);
184
185 $crate::create_exception_type_object!($module, $name, $base, Some($doc));
186 };
187}
188
189#[doc(hidden)]
192#[macro_export]
193macro_rules! create_exception_type_object {
194 ($module: expr, $name: ident, $base: ty, None) => {
195 $crate::create_exception_type_object!($module, $name, $base, ::core::option::Option::None);
196 };
197 ($module: expr, $name: ident, $base: ty, Some($doc: expr)) => {
198 $crate::create_exception_type_object!(
199 $module,
200 $name,
201 $base,
202 ::core::option::Option::Some($crate::ffi::c_str!($doc))
203 );
204 };
205 ($module: expr, $name: ident, $base: ty, $doc: expr) => {
206 $crate::pyobject_native_type_named!($name);
207
208 unsafe impl $crate::type_object::PyTypeInfo for $name {
210 const NAME: &'static str = stringify!($name);
211 const MODULE: ::core::option::Option<&'static str> =
212 ::core::option::Option::Some(stringify!($module));
213 $crate::create_exception_type_hint!($module, $name);
214
215 #[inline]
216 #[allow(clippy::redundant_closure_call)]
217 fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
218 use $crate::sync::PyOnceLock;
219 static TYPE_OBJECT: PyOnceLock<$crate::Py<$crate::types::PyType>> =
220 PyOnceLock::new();
221
222 TYPE_OBJECT
223 .get_or_init(py, || {
224 $crate::PyErr::new_type(
225 py,
226 $crate::ffi::c_str!(concat!(
227 stringify!($module),
228 ".",
229 stringify!($name)
230 )),
231 $doc,
232 ::core::option::Option::Some(&py.get_type::<$base>()),
233 ::core::option::Option::None,
234 )
235 .expect("Failed to initialize new exception type.")
236 })
237 .as_ptr()
238 .cast()
239 }
240 }
241
242 impl $name {
243 #[doc(hidden)]
244 pub const _PYO3_DEF: $crate::impl_::pymodule::AddTypeToModule<Self> =
245 $crate::impl_::pymodule::AddTypeToModule::new();
246
247 #[allow(dead_code)]
248 #[doc(hidden)]
249 pub const _PYO3_INTROSPECTION_ID: &'static str =
250 concat!(stringify!($module), stringify!($name));
251 }
252 };
253}
254
255#[cfg(not(feature = "experimental-inspect"))]
257#[doc(hidden)]
258#[macro_export]
259macro_rules! create_exception_type_hint(
260 ($module: expr, $name: ident) => {};
261);
262
263#[cfg(feature = "experimental-inspect")]
264#[doc(hidden)]
265#[macro_export]
266macro_rules! create_exception_type_hint(
267 ($module: expr, $name: ident) => {
268 const TYPE_HINT: $crate::inspect::PyStaticExpr = $crate::inspect::PyStaticExpr::PyClass($crate::inspect::PyClassNameStaticExpr::new(
269 &$crate::type_hint_identifier!(stringify!($module), stringify!($name)),
270 Self::_PYO3_INTROSPECTION_ID
271 ));
272 };
273);
274
275macro_rules! impl_native_exception (
276 ($name:ident, $exc_name:ident, $python_name:expr, $doc:expr, $layout:path $(, #checkfunction=$checkfunction:path)?) => (
277 #[doc = $doc]
278 #[repr(transparent)]
279 #[allow(clippy::upper_case_acronyms, reason = "Python exception names")]
280 pub struct $name($crate::PyAny);
281
282 $crate::impl_exception_boilerplate!($name);
283 $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject }, "builtins", $python_name $(, #checkfunction=$checkfunction)?);
284 $crate::pyobject_subclassable_native_type!($name, $layout);
285 );
286 ($name:ident, $exc_name:ident, $python_name:expr, $doc:expr) => (
287 impl_native_exception!($name, $exc_name, $python_name, $doc, $crate::ffi::PyBaseExceptionObject);
288 )
289);
290
291macro_rules! native_doc(
292 ($name: literal, $alt: literal) => (
293 concat!(
294"Represents Python's [`", $name, "`](https://docs.python.org/3/library/exceptions.html#", $name, ") exception.
295
296", $alt
297 )
298 );
299 ($name: literal) => (
300 concat!(
301"
302Represents Python's [`", $name, "`](https://docs.python.org/3/library/exceptions.html#", $name, ") exception.
303
304# Example: Raising ", $name, " from Rust
305
306This exception can be sent to Python code by converting it into a
307[`PyErr`](crate::PyErr), where Python code can then catch it.
308```
309use pyo3::prelude::*;
310use pyo3::exceptions::Py", $name, ";
311
312#[pyfunction]
313fn always_throws() -> PyResult<()> {
314 let message = \"I'm ", $name ,", and I was raised from Rust.\";
315 Err(Py", $name, "::new_err(message))
316}
317#
318# Python::attach(|py| {
319# let fun = pyo3::wrap_pyfunction!(always_throws, py).unwrap();
320# let err = fun.call0().expect_err(\"called a function that should always return an error but the return value was Ok\");
321# assert!(err.is_instance_of::<Py", $name, ">(py))
322# });
323```
324
325Python code:
326 ```python
327 from my_module import always_throws
328
329try:
330 always_throws()
331except ", $name, " as e:
332 print(f\"Caught an exception: {e}\")
333```
334
335# Example: Catching ", $name, " in Rust
336
337```
338use pyo3::prelude::*;
339use pyo3::exceptions::Py", $name, ";
340use pyo3::ffi::c_str;
341
342Python::attach(|py| {
343 let result: PyResult<()> = py.run(c_str!(\"raise ", $name, "\"), None, None);
344
345 let error_type = match result {
346 Ok(_) => \"Not an error\",
347 Err(error) if error.is_instance_of::<Py", $name, ">(py) => \"" , $name, "\",
348 Err(_) => \"Some other error\",
349 };
350
351 assert_eq!(error_type, \"", $name, "\");
352});
353```
354"
355 )
356 );
357);
358
359impl_native_exception!(
360 PyBaseException,
361 PyExc_BaseException,
362 "BaseException",
363 native_doc!("BaseException"),
364 ffi::PyBaseExceptionObject,
365 #checkfunction=ffi::PyExceptionInstance_Check
366);
367impl_native_exception!(
368 PyException,
369 PyExc_Exception,
370 "Exception",
371 native_doc!("Exception")
372);
373impl_native_exception!(
374 PyStopAsyncIteration,
375 PyExc_StopAsyncIteration,
376 "StopAsyncIteration",
377 native_doc!("StopAsyncIteration")
378);
379impl_native_exception!(
380 PyStopIteration,
381 PyExc_StopIteration,
382 "StopIteration",
383 native_doc!("StopIteration"),
384 ffi::PyStopIterationObject
385);
386impl_native_exception!(
387 PyGeneratorExit,
388 PyExc_GeneratorExit,
389 "GeneratorExit",
390 native_doc!("GeneratorExit")
391);
392impl_native_exception!(
393 PyArithmeticError,
394 PyExc_ArithmeticError,
395 "ArithmeticError",
396 native_doc!("ArithmeticError")
397);
398impl_native_exception!(
399 PyLookupError,
400 PyExc_LookupError,
401 "LookupError",
402 native_doc!("LookupError")
403);
404
405impl_native_exception!(
406 PyAssertionError,
407 PyExc_AssertionError,
408 "AssertionError",
409 native_doc!("AssertionError")
410);
411impl_native_exception!(
412 PyAttributeError,
413 PyExc_AttributeError,
414 "AttributeError",
415 native_doc!("AttributeError")
416);
417impl_native_exception!(
418 PyBufferError,
419 PyExc_BufferError,
420 "BufferError",
421 native_doc!("BufferError")
422);
423impl_native_exception!(
424 PyEOFError,
425 PyExc_EOFError,
426 "EOFError",
427 native_doc!("EOFError")
428);
429impl_native_exception!(
430 PyFloatingPointError,
431 PyExc_FloatingPointError,
432 "FloatingPointError",
433 native_doc!("FloatingPointError")
434);
435#[cfg(not(any(PyPy, GraalPy)))]
436impl_native_exception!(
437 PyOSError,
438 PyExc_OSError,
439 "OSError",
440 native_doc!("OSError"),
441 ffi::PyOSErrorObject
442);
443#[cfg(any(PyPy, GraalPy))]
444impl_native_exception!(PyOSError, PyExc_OSError, "OSError", native_doc!("OSError"));
445impl_native_exception!(
446 PyImportError,
447 PyExc_ImportError,
448 "ImportError",
449 native_doc!("ImportError")
450);
451
452impl_native_exception!(
453 PyModuleNotFoundError,
454 PyExc_ModuleNotFoundError,
455 "ModuleNotFoundError",
456 native_doc!("ModuleNotFoundError")
457);
458
459impl_native_exception!(
460 PyIndexError,
461 PyExc_IndexError,
462 "IndexError",
463 native_doc!("IndexError")
464);
465impl_native_exception!(
466 PyKeyError,
467 PyExc_KeyError,
468 "KeyError",
469 native_doc!("KeyError")
470);
471impl_native_exception!(
472 PyKeyboardInterrupt,
473 PyExc_KeyboardInterrupt,
474 "KeyboardInterrupt",
475 native_doc!("KeyboardInterrupt")
476);
477impl_native_exception!(
478 PyMemoryError,
479 PyExc_MemoryError,
480 "MemoryError",
481 native_doc!("MemoryError")
482);
483impl_native_exception!(
484 PyNameError,
485 PyExc_NameError,
486 "NameError",
487 native_doc!("NameError")
488);
489impl_native_exception!(
490 PyOverflowError,
491 PyExc_OverflowError,
492 "OverflowError",
493 native_doc!("OverflowError")
494);
495impl_native_exception!(
496 PyRuntimeError,
497 PyExc_RuntimeError,
498 "RuntimeError",
499 native_doc!("RuntimeError")
500);
501impl_native_exception!(
502 PyRecursionError,
503 PyExc_RecursionError,
504 "RecursionError",
505 native_doc!("RecursionError")
506);
507impl_native_exception!(
508 PyNotImplementedError,
509 PyExc_NotImplementedError,
510 "NotImplementedError",
511 native_doc!("NotImplementedError")
512);
513#[cfg(not(any(PyPy, GraalPy)))]
514impl_native_exception!(
515 PySyntaxError,
516 PyExc_SyntaxError,
517 "SyntaxError",
518 native_doc!("SyntaxError"),
519 ffi::PySyntaxErrorObject
520);
521#[cfg(any(PyPy, GraalPy))]
522impl_native_exception!(
523 PySyntaxError,
524 PyExc_SyntaxError,
525 "SyntaxError",
526 native_doc!("SyntaxError")
527);
528impl_native_exception!(
529 PyReferenceError,
530 PyExc_ReferenceError,
531 "ReferenceError",
532 native_doc!("ReferenceError")
533);
534impl_native_exception!(
535 PySystemError,
536 PyExc_SystemError,
537 "SystemError",
538 native_doc!("SystemError")
539);
540#[cfg(not(any(PyPy, GraalPy)))]
541impl_native_exception!(
542 PySystemExit,
543 PyExc_SystemExit,
544 "SystemExit",
545 native_doc!("SystemExit"),
546 ffi::PySystemExitObject
547);
548#[cfg(any(PyPy, GraalPy))]
549impl_native_exception!(
550 PySystemExit,
551 PyExc_SystemExit,
552 "SystemExit",
553 native_doc!("SystemExit")
554);
555impl_native_exception!(
556 PyTypeError,
557 PyExc_TypeError,
558 "TypeError",
559 native_doc!("TypeError")
560);
561impl_native_exception!(
562 PyUnboundLocalError,
563 PyExc_UnboundLocalError,
564 "UnboundLocalError",
565 native_doc!("UnboundLocalError")
566);
567#[cfg(not(any(PyPy, GraalPy)))]
568impl_native_exception!(
569 PyUnicodeError,
570 PyExc_UnicodeError,
571 "UnicodeError",
572 native_doc!("UnicodeError"),
573 ffi::PyUnicodeErrorObject
574);
575#[cfg(any(PyPy, GraalPy))]
576impl_native_exception!(
577 PyUnicodeError,
578 PyExc_UnicodeError,
579 "UnicodeError",
580 native_doc!("UnicodeError")
581);
582impl_native_exception!(
584 PyUnicodeDecodeError,
585 PyExc_UnicodeDecodeError,
586 "UnicodeDecodeError",
587 native_doc!("UnicodeDecodeError", "")
588);
589impl_native_exception!(
590 PyUnicodeEncodeError,
591 PyExc_UnicodeEncodeError,
592 "UnicodeEncodeError",
593 native_doc!("UnicodeEncodeError", "")
594);
595impl_native_exception!(
596 PyUnicodeTranslateError,
597 PyExc_UnicodeTranslateError,
598 "UnicodeTranslateError",
599 native_doc!("UnicodeTranslateError", "")
600);
601#[cfg(Py_3_11)]
602impl_native_exception!(
603 PyBaseExceptionGroup,
604 PyExc_BaseExceptionGroup,
605 "BaseExceptionGroup",
606 native_doc!("BaseExceptionGroup", "")
607);
608impl_native_exception!(
609 PyValueError,
610 PyExc_ValueError,
611 "ValueError",
612 native_doc!("ValueError")
613);
614impl_native_exception!(
615 PyZeroDivisionError,
616 PyExc_ZeroDivisionError,
617 "ZeroDivisionError",
618 native_doc!("ZeroDivisionError")
619);
620
621impl_native_exception!(
622 PyBlockingIOError,
623 PyExc_BlockingIOError,
624 "BlockingIOError",
625 native_doc!("BlockingIOError")
626);
627impl_native_exception!(
628 PyBrokenPipeError,
629 PyExc_BrokenPipeError,
630 "BrokenPipeError",
631 native_doc!("BrokenPipeError")
632);
633impl_native_exception!(
634 PyChildProcessError,
635 PyExc_ChildProcessError,
636 "ChildProcessError",
637 native_doc!("ChildProcessError")
638);
639impl_native_exception!(
640 PyConnectionError,
641 PyExc_ConnectionError,
642 "ConnectionError",
643 native_doc!("ConnectionError")
644);
645impl_native_exception!(
646 PyConnectionAbortedError,
647 PyExc_ConnectionAbortedError,
648 "ConnectionAbortedError",
649 native_doc!("ConnectionAbortedError")
650);
651impl_native_exception!(
652 PyConnectionRefusedError,
653 PyExc_ConnectionRefusedError,
654 "ConnectionRefusedError",
655 native_doc!("ConnectionRefusedError")
656);
657impl_native_exception!(
658 PyConnectionResetError,
659 PyExc_ConnectionResetError,
660 "ConnectionResetError",
661 native_doc!("ConnectionResetError")
662);
663impl_native_exception!(
664 PyFileExistsError,
665 PyExc_FileExistsError,
666 "FileExistsError",
667 native_doc!("FileExistsError")
668);
669impl_native_exception!(
670 PyFileNotFoundError,
671 PyExc_FileNotFoundError,
672 "FileNotFoundError",
673 native_doc!("FileNotFoundError")
674);
675impl_native_exception!(
676 PyInterruptedError,
677 PyExc_InterruptedError,
678 "InterruptedError",
679 native_doc!("InterruptedError")
680);
681impl_native_exception!(
682 PyIsADirectoryError,
683 PyExc_IsADirectoryError,
684 "IsADirectoryError",
685 native_doc!("IsADirectoryError")
686);
687impl_native_exception!(
688 PyNotADirectoryError,
689 PyExc_NotADirectoryError,
690 "NotADirectoryError",
691 native_doc!("NotADirectoryError")
692);
693impl_native_exception!(
694 PyPermissionError,
695 PyExc_PermissionError,
696 "PermissionError",
697 native_doc!("PermissionError")
698);
699impl_native_exception!(
700 PyProcessLookupError,
701 PyExc_ProcessLookupError,
702 "ProcessLookupError",
703 native_doc!("ProcessLookupError")
704);
705impl_native_exception!(
706 PyTimeoutError,
707 PyExc_TimeoutError,
708 "TimeoutError",
709 native_doc!("TimeoutError")
710);
711
712pub type PyEnvironmentError = PyOSError;
714
715pub type PyIOError = PyOSError;
717
718#[cfg(windows)]
719pub type PyWindowsError = PyOSError;
721
722impl PyUnicodeDecodeError {
723 pub fn new<'py>(
725 py: Python<'py>,
726 encoding: &CStr,
727 input: &[u8],
728 range: ops::Range<usize>,
729 reason: &CStr,
730 ) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
731 use crate::ffi_ptr_ext::FfiPtrExt;
732 use crate::py_result_ext::PyResultExt;
733 unsafe {
734 ffi::PyUnicodeDecodeError_Create(
735 encoding.as_ptr(),
736 input.as_ptr().cast(),
737 input.len() as ffi::Py_ssize_t,
738 range.start as ffi::Py_ssize_t,
739 range.end as ffi::Py_ssize_t,
740 reason.as_ptr(),
741 )
742 .assume_owned_or_err(py)
743 }
744 .cast_into()
745 }
746
747 pub fn new_utf8<'py>(
769 py: Python<'py>,
770 input: &[u8],
771 err: core::str::Utf8Error,
772 ) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
773 let start = err.valid_up_to();
774 let end = err.error_len().map_or(input.len(), |l| start + l);
775 PyUnicodeDecodeError::new(py, c"utf-8", input, start..end, c"invalid utf-8")
776 }
777
778 pub fn new_err_from_utf8(
797 py: Python<'_>,
798 bytes: &[u8],
799 err: core::str::Utf8Error,
800 ) -> crate::PyErr {
801 match Self::new_utf8(py, bytes, err) {
802 Ok(e) => crate::PyErr::from_value(e.into_any()),
803 Err(e) => e,
804 }
805 }
806}
807
808impl_native_exception!(PyWarning, PyExc_Warning, "Warning", native_doc!("Warning"));
809impl_native_exception!(
810 PyUserWarning,
811 PyExc_UserWarning,
812 "UserWarning",
813 native_doc!("UserWarning")
814);
815impl_native_exception!(
816 PyDeprecationWarning,
817 PyExc_DeprecationWarning,
818 "DeprecationWarning",
819 native_doc!("DeprecationWarning")
820);
821impl_native_exception!(
822 PyPendingDeprecationWarning,
823 PyExc_PendingDeprecationWarning,
824 "PendingDeprecationWarning",
825 native_doc!("PendingDeprecationWarning")
826);
827impl_native_exception!(
828 PySyntaxWarning,
829 PyExc_SyntaxWarning,
830 "SyntaxWarning",
831 native_doc!("SyntaxWarning")
832);
833impl_native_exception!(
834 PyRuntimeWarning,
835 PyExc_RuntimeWarning,
836 "RuntimeWarning",
837 native_doc!("RuntimeWarning")
838);
839impl_native_exception!(
840 PyFutureWarning,
841 PyExc_FutureWarning,
842 "FutureWarning",
843 native_doc!("FutureWarning")
844);
845impl_native_exception!(
846 PyImportWarning,
847 PyExc_ImportWarning,
848 "ImportWarning",
849 native_doc!("ImportWarning")
850);
851impl_native_exception!(
852 PyUnicodeWarning,
853 PyExc_UnicodeWarning,
854 "UnicodeWarning",
855 native_doc!("UnicodeWarning")
856);
857impl_native_exception!(
858 PyBytesWarning,
859 PyExc_BytesWarning,
860 "BytesWarning",
861 native_doc!("BytesWarning")
862);
863impl_native_exception!(
864 PyResourceWarning,
865 PyExc_ResourceWarning,
866 "ResourceWarning",
867 native_doc!("ResourceWarning")
868);
869
870#[cfg(Py_3_10)]
871impl_native_exception!(
872 PyEncodingWarning,
873 PyExc_EncodingWarning,
874 "EncodingWarning",
875 native_doc!("EncodingWarning")
876);
877
878#[cfg(test)]
879macro_rules! test_exception {
880 ($exc_ty:ident $(, |$py:tt| $constructor:expr )?) => {
881 #[allow(non_snake_case, reason = "test matches exception name")]
882 #[test]
883 fn $exc_ty () {
884 use super::$exc_ty;
885
886 $crate::Python::attach(|py| {
887 let err: $crate::PyErr = {
888 None
889 $(
890 .or(Some({ let $py = py; $constructor }))
891 )?
892 .unwrap_or($exc_ty::new_err("a test exception"))
893 };
894
895 assert!(err.is_instance_of::<$exc_ty>(py));
896
897 let value = err.value(py).as_any().cast::<$exc_ty>().unwrap();
898
899 assert!($crate::PyErr::from(value.clone()).is_instance_of::<$exc_ty>(py));
900 })
901 }
902 };
903}
904
905pub mod asyncio {
908 import_exception!(asyncio, CancelledError);
909 import_exception!(asyncio, InvalidStateError);
910 import_exception!(asyncio, TimeoutError);
911 import_exception!(asyncio, IncompleteReadError);
912 import_exception!(asyncio, LimitOverrunError);
913 import_exception!(asyncio, QueueEmpty);
914 import_exception!(asyncio, QueueFull);
915
916 #[cfg(test)]
917 mod tests {
918 test_exception!(CancelledError);
919 test_exception!(InvalidStateError);
920 test_exception!(TimeoutError);
921 test_exception!(IncompleteReadError, |_| IncompleteReadError::new_err((
922 "partial", "expected"
923 )));
924 test_exception!(LimitOverrunError, |_| LimitOverrunError::new_err((
925 "message", "consumed"
926 )));
927 test_exception!(QueueEmpty);
928 test_exception!(QueueFull);
929 }
930}
931
932pub mod socket {
935 import_exception!(socket, herror);
936 import_exception!(socket, gaierror);
937 import_exception!(socket, timeout);
938
939 #[cfg(test)]
940 mod tests {
941 test_exception!(herror);
942 test_exception!(gaierror);
943 test_exception!(timeout);
944 }
945}
946
947#[cfg(test)]
948mod tests {
949 use super::*;
950 use crate::types::any::PyAnyMethods;
951 use crate::types::{IntoPyDict, PyDict};
952 use crate::{IntoPyObjectExt as _, PyErr};
953
954 import_exception!(socket, gaierror);
955 import_exception!(email.errors, MessageError);
956
957 #[test]
958 fn test_check_exception() {
959 Python::attach(|py| {
960 let err: PyErr = gaierror::new_err(());
961 let socket = py
962 .import("socket")
963 .map_err(|e| e.display(py))
964 .expect("could not import socket");
965
966 let d = PyDict::new(py);
967 d.set_item("socket", socket)
968 .map_err(|e| e.display(py))
969 .expect("could not setitem");
970
971 d.set_item("exc", err)
972 .map_err(|e| e.display(py))
973 .expect("could not setitem");
974
975 py.run(c"assert isinstance(exc, socket.gaierror)", None, Some(&d))
976 .map_err(|e| e.display(py))
977 .expect("assertion failed");
978 });
979 }
980
981 #[test]
982 fn test_check_exception_nested() {
983 Python::attach(|py| {
984 let err: PyErr = MessageError::new_err(());
985 let email = py
986 .import("email")
987 .map_err(|e| e.display(py))
988 .expect("could not import email");
989
990 let d = PyDict::new(py);
991 d.set_item("email", email)
992 .map_err(|e| e.display(py))
993 .expect("could not setitem");
994 d.set_item("exc", err)
995 .map_err(|e| e.display(py))
996 .expect("could not setitem");
997
998 py.run(
999 c"assert isinstance(exc, email.errors.MessageError)",
1000 None,
1001 Some(&d),
1002 )
1003 .map_err(|e| e.display(py))
1004 .expect("assertion failed");
1005 });
1006 }
1007
1008 #[test]
1009 fn custom_exception() {
1010 create_exception!(mymodule, CustomError, PyException);
1011
1012 Python::attach(|py| {
1013 let error_type = py.get_type::<CustomError>();
1014 let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
1015 let type_description: String = py
1016 .eval(c"str(CustomError)", None, Some(&ctx))
1017 .unwrap()
1018 .extract()
1019 .unwrap();
1020 assert_eq!(type_description, "<class 'mymodule.CustomError'>");
1021 py.run(
1022 c"assert CustomError('oops').args == ('oops',)",
1023 None,
1024 Some(&ctx),
1025 )
1026 .unwrap();
1027 py.run(c"assert CustomError.__doc__ is None", None, Some(&ctx))
1028 .unwrap();
1029 });
1030 }
1031
1032 #[test]
1033 fn custom_exception_dotted_module() {
1034 create_exception!(mymodule.exceptions, CustomError, PyException);
1035 Python::attach(|py| {
1036 let error_type = py.get_type::<CustomError>();
1037 let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
1038 let type_description: String = py
1039 .eval(c"str(CustomError)", None, Some(&ctx))
1040 .unwrap()
1041 .extract()
1042 .unwrap();
1043 assert_eq!(
1044 type_description,
1045 "<class 'mymodule.exceptions.CustomError'>"
1046 );
1047 });
1048 }
1049
1050 #[test]
1051 fn custom_exception_doc() {
1052 create_exception!(mymodule, CustomError, PyException, "Some docs");
1053
1054 Python::attach(|py| {
1055 let error_type = py.get_type::<CustomError>();
1056 let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
1057 let type_description: String = py
1058 .eval(c"str(CustomError)", None, Some(&ctx))
1059 .unwrap()
1060 .extract()
1061 .unwrap();
1062 assert_eq!(type_description, "<class 'mymodule.CustomError'>");
1063 py.run(
1064 c"assert CustomError('oops').args == ('oops',)",
1065 None,
1066 Some(&ctx),
1067 )
1068 .unwrap();
1069 py.run(
1070 c"assert CustomError.__doc__ == 'Some docs'",
1071 None,
1072 Some(&ctx),
1073 )
1074 .unwrap();
1075 });
1076 }
1077
1078 #[test]
1079 fn custom_exception_doc_expr() {
1080 create_exception!(
1081 mymodule,
1082 CustomError,
1083 PyException,
1084 concat!("Some", " more ", stringify!(docs))
1085 );
1086
1087 Python::attach(|py| {
1088 let error_type = py.get_type::<CustomError>();
1089 let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
1090 let type_description: String = py
1091 .eval(c"str(CustomError)", None, Some(&ctx))
1092 .unwrap()
1093 .extract()
1094 .unwrap();
1095 assert_eq!(type_description, "<class 'mymodule.CustomError'>");
1096 py.run(
1097 c"assert CustomError('oops').args == ('oops',)",
1098 None,
1099 Some(&ctx),
1100 )
1101 .unwrap();
1102 py.run(
1103 c"assert CustomError.__doc__ == 'Some more docs'",
1104 None,
1105 Some(&ctx),
1106 )
1107 .unwrap();
1108 });
1109 }
1110
1111 #[test]
1112 fn native_exception_debug() {
1113 Python::attach(|py| {
1114 let exc = py
1115 .run(c"raise Exception('banana')", None, None)
1116 .expect_err("raising should have given us an error")
1117 .into_value(py)
1118 .into_bound(py);
1119 assert_eq!(
1120 format!("{exc:?}"),
1121 exc.repr().unwrap().extract::<String>().unwrap()
1122 );
1123 });
1124 }
1125
1126 #[test]
1127 fn native_exception_display() {
1128 Python::attach(|py| {
1129 let exc = py
1130 .run(c"raise Exception('banana')", None, None)
1131 .expect_err("raising should have given us an error")
1132 .into_value(py)
1133 .into_bound(py);
1134 assert_eq!(
1135 exc.to_string(),
1136 exc.str().unwrap().extract::<String>().unwrap()
1137 );
1138 });
1139 }
1140
1141 #[test]
1142 fn unicode_decode_error() {
1143 let invalid_utf8 = b"fo\xd8o";
1144 #[expect(invalid_from_utf8)]
1145 let err = core::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
1146 Python::attach(|py| {
1147 let decode_err = PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err).unwrap();
1148 assert_eq!(
1149 format!("{decode_err:?}"),
1150 "UnicodeDecodeError('utf-8', b'fo\\xd8o', 2, 3, 'invalid utf-8')"
1151 );
1152
1153 let e: PyErr = decode_err.into();
1155 e.restore(py);
1156
1157 assert_eq!(
1158 PyErr::fetch(py).to_string(),
1159 "UnicodeDecodeError: \'utf-8\' codec can\'t decode byte 0xd8 in position 2: invalid utf-8"
1160 );
1161 });
1162 }
1163 #[cfg(Py_3_11)]
1164 test_exception!(PyBaseExceptionGroup, |_| PyBaseExceptionGroup::new_err((
1165 "msg",
1166 vec![PyValueError::new_err("err")]
1167 )));
1168 test_exception!(PyBaseException);
1169 test_exception!(PyException);
1170 test_exception!(PyStopAsyncIteration);
1171 test_exception!(PyStopIteration);
1172 test_exception!(PyGeneratorExit);
1173 test_exception!(PyArithmeticError);
1174 test_exception!(PyLookupError);
1175 test_exception!(PyAssertionError);
1176 test_exception!(PyAttributeError);
1177 test_exception!(PyBufferError);
1178 test_exception!(PyEOFError);
1179 test_exception!(PyFloatingPointError);
1180 test_exception!(PyOSError);
1181 test_exception!(PyImportError);
1182 test_exception!(PyModuleNotFoundError);
1183 test_exception!(PyIndexError);
1184 test_exception!(PyKeyError);
1185 test_exception!(PyKeyboardInterrupt);
1186 test_exception!(PyMemoryError);
1187 test_exception!(PyNameError);
1188 test_exception!(PyOverflowError);
1189 test_exception!(PyRuntimeError);
1190 test_exception!(PyRecursionError);
1191 test_exception!(PyNotImplementedError);
1192 test_exception!(PySyntaxError);
1193 test_exception!(PyReferenceError);
1194 test_exception!(PySystemError);
1195 test_exception!(PySystemExit);
1196 test_exception!(PyTypeError);
1197 test_exception!(PyUnboundLocalError);
1198 test_exception!(PyUnicodeError);
1199 test_exception!(PyUnicodeDecodeError, |py| {
1200 let invalid_utf8 = b"fo\xd8o";
1201 #[expect(invalid_from_utf8)]
1202 let err = core::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
1203 PyErr::from_value(
1204 PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err)
1205 .unwrap()
1206 .into_any(),
1207 )
1208 });
1209 test_exception!(PyUnicodeEncodeError, |py| py
1210 .eval(c"chr(40960).encode('ascii')", None, None)
1211 .unwrap_err());
1212 test_exception!(PyUnicodeTranslateError, |_| {
1213 PyUnicodeTranslateError::new_err(("\u{3042}", 0, 1, "ouch"))
1214 });
1215 test_exception!(PyValueError);
1216 test_exception!(PyZeroDivisionError);
1217 test_exception!(PyBlockingIOError);
1218 test_exception!(PyBrokenPipeError);
1219 test_exception!(PyChildProcessError);
1220 test_exception!(PyConnectionError);
1221 test_exception!(PyConnectionAbortedError);
1222 test_exception!(PyConnectionRefusedError);
1223 test_exception!(PyConnectionResetError);
1224 test_exception!(PyFileExistsError);
1225 test_exception!(PyFileNotFoundError);
1226 test_exception!(PyInterruptedError);
1227 test_exception!(PyIsADirectoryError);
1228 test_exception!(PyNotADirectoryError);
1229 test_exception!(PyPermissionError);
1230 test_exception!(PyProcessLookupError);
1231 test_exception!(PyTimeoutError);
1232 test_exception!(PyEnvironmentError);
1233 test_exception!(PyIOError);
1234 #[cfg(windows)]
1235 test_exception!(PyWindowsError);
1236
1237 test_exception!(PyWarning);
1238 test_exception!(PyUserWarning);
1239 test_exception!(PyDeprecationWarning);
1240 test_exception!(PyPendingDeprecationWarning);
1241 test_exception!(PySyntaxWarning);
1242 test_exception!(PyRuntimeWarning);
1243 test_exception!(PyFutureWarning);
1244 test_exception!(PyImportWarning);
1245 test_exception!(PyUnicodeWarning);
1246 test_exception!(PyBytesWarning);
1247 #[cfg(Py_3_10)]
1248 test_exception!(PyEncodingWarning);
1249
1250 #[test]
1251 #[allow(invalid_from_utf8)]
1252 fn unicode_decode_error_from_utf8() {
1253 Python::attach(|py| {
1254 let bytes = b"abc\xffdef".to_vec();
1255
1256 let check_err = |py_err: PyErr| {
1257 let py_err = py_err.into_bound_py_any(py).unwrap();
1258
1259 assert!(py_err.is_instance_of::<PyUnicodeDecodeError>());
1260 assert_eq!(
1261 py_err
1262 .getattr("encoding")
1263 .unwrap()
1264 .extract::<String>()
1265 .unwrap(),
1266 "utf-8"
1267 );
1268 assert_eq!(
1269 py_err
1270 .getattr("object")
1271 .unwrap()
1272 .extract::<Vec<u8>>()
1273 .unwrap(),
1274 &*bytes
1275 );
1276 assert_eq!(
1277 py_err.getattr("start").unwrap().extract::<usize>().unwrap(),
1278 3
1279 );
1280 assert_eq!(
1281 py_err.getattr("end").unwrap().extract::<usize>().unwrap(),
1282 4
1283 );
1284 assert_eq!(
1285 py_err
1286 .getattr("reason")
1287 .unwrap()
1288 .extract::<String>()
1289 .unwrap(),
1290 "invalid utf-8"
1291 );
1292 };
1293
1294 let utf8_err_with_bytes = PyUnicodeDecodeError::new_err_from_utf8(
1295 py,
1296 &bytes,
1297 core::str::from_utf8(&bytes).expect_err("\\xff is invalid utf-8"),
1298 );
1299 check_err(utf8_err_with_bytes);
1300 })
1301 }
1302}