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