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