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