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)]
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)] 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 #module=::std::option::Option::Some(stringify!($module))
84 );
85
86 impl $name {
87 fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
88 use $crate::types::PyTypeMethods;
89 static TYPE_OBJECT: $crate::impl_::exceptions::ImportedExceptionTypeObject =
90 $crate::impl_::exceptions::ImportedExceptionTypeObject::new(stringify!($module), stringify!($name));
91 TYPE_OBJECT.get(py).as_type_ptr()
92 }
93 }
94 };
95}
96
97#[macro_export]
99#[deprecated(since = "0.27.0", note = "renamed to `import_exception!` instead")]
100macro_rules! import_exception_bound {
101 ($module: expr, $name: ident) => {
102 $crate::import_exception!($module, $name);
103 };
104}
105
106#[macro_export]
173macro_rules! create_exception {
174 ($module: expr, $name: ident, $base: ty) => {
175 #[repr(transparent)]
176 #[allow(non_camel_case_types)] pub struct $name($crate::PyAny);
178
179 $crate::impl_exception_boilerplate!($name);
180
181 $crate::create_exception_type_object!($module, $name, $base, None);
182 };
183 ($module: expr, $name: ident, $base: ty, $doc: expr) => {
184 #[repr(transparent)]
185 #[allow(non_camel_case_types)] #[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!($module, $name, $base, ::std::option::Option::Some($crate::ffi::c_str!($doc)));
205 };
206 ($module: expr, $name: ident, $base: ty, $doc: expr) => {
207 $crate::pyobject_native_type_core!(
208 $name,
209 $name::type_object_raw,
210 #module=::std::option::Option::Some(stringify!($module))
211 );
212
213 impl $name {
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!(stringify!($module), ".", stringify!($name))),
224 $doc,
225 ::std::option::Option::Some(&py.get_type::<$base>()),
226 ::std::option::Option::None,
227 ).expect("Failed to initialize new exception type.")
228 ).as_ptr() as *mut $crate::ffi::PyTypeObject
229 }
230 }
231 };
232}
233
234macro_rules! impl_native_exception (
235 ($name:ident, $exc_name:ident, $doc:expr, $layout:path $(, #checkfunction=$checkfunction:path)?) => (
236 #[doc = $doc]
237 #[repr(transparent)]
238 #[allow(clippy::upper_case_acronyms)]
239 pub struct $name($crate::PyAny);
240
241 $crate::impl_exception_boilerplate!($name);
242 $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject } $(, #checkfunction=$checkfunction)?);
243 $crate::pyobject_subclassable_native_type!($name, $layout);
244 );
245 ($name:ident, $exc_name:ident, $doc:expr) => (
246 impl_native_exception!($name, $exc_name, $doc, $crate::ffi::PyBaseExceptionObject);
247 )
248);
249
250#[cfg(windows)]
251macro_rules! impl_windows_native_exception (
252 ($name:ident, $exc_name:ident, $doc:expr, $layout:path) => (
253 #[cfg(windows)]
254 #[doc = $doc]
255 #[repr(transparent)]
256 #[allow(clippy::upper_case_acronyms)]
257 pub struct $name($crate::PyAny);
258
259 $crate::impl_exception_boilerplate!($name);
260 $crate::pyobject_native_type!($name, $layout, |_py| unsafe { $crate::ffi::$exc_name as *mut $crate::ffi::PyTypeObject });
261 );
262 ($name:ident, $exc_name:ident, $doc:expr) => (
263 impl_windows_native_exception!($name, $exc_name, $doc, $crate::ffi::PyBaseExceptionObject);
264 )
265);
266
267macro_rules! native_doc(
268 ($name: literal, $alt: literal) => (
269 concat!(
270"Represents Python's [`", $name, "`](https://docs.python.org/3/library/exceptions.html#", $name, ") exception.
271
272", $alt
273 )
274 );
275 ($name: literal) => (
276 concat!(
277"
278Represents Python's [`", $name, "`](https://docs.python.org/3/library/exceptions.html#", $name, ") exception.
279
280# Example: Raising ", $name, " from Rust
281
282This exception can be sent to Python code by converting it into a
283[`PyErr`](crate::PyErr), where Python code can then catch it.
284```
285use pyo3::prelude::*;
286use pyo3::exceptions::Py", $name, ";
287
288#[pyfunction]
289fn always_throws() -> PyResult<()> {
290 let message = \"I'm ", $name ,", and I was raised from Rust.\";
291 Err(Py", $name, "::new_err(message))
292}
293#
294# Python::attach(|py| {
295# let fun = pyo3::wrap_pyfunction!(always_throws, py).unwrap();
296# let err = fun.call0().expect_err(\"called a function that should always return an error but the return value was Ok\");
297# assert!(err.is_instance_of::<Py", $name, ">(py))
298# });
299```
300
301Python code:
302 ```python
303 from my_module import always_throws
304
305try:
306 always_throws()
307except ", $name, " as e:
308 print(f\"Caught an exception: {e}\")
309```
310
311# Example: Catching ", $name, " in Rust
312
313```
314use pyo3::prelude::*;
315use pyo3::exceptions::Py", $name, ";
316use pyo3::ffi::c_str;
317
318Python::attach(|py| {
319 let result: PyResult<()> = py.run(c_str!(\"raise ", $name, "\"), None, None);
320
321 let error_type = match result {
322 Ok(_) => \"Not an error\",
323 Err(error) if error.is_instance_of::<Py", $name, ">(py) => \"" , $name, "\",
324 Err(_) => \"Some other error\",
325 };
326
327 assert_eq!(error_type, \"", $name, "\");
328});
329```
330"
331 )
332 );
333);
334
335impl_native_exception!(
336 PyBaseException,
337 PyExc_BaseException,
338 native_doc!("BaseException"),
339 ffi::PyBaseExceptionObject,
340 #checkfunction=ffi::PyExceptionInstance_Check
341);
342impl_native_exception!(PyException, PyExc_Exception, native_doc!("Exception"));
343impl_native_exception!(
344 PyStopAsyncIteration,
345 PyExc_StopAsyncIteration,
346 native_doc!("StopAsyncIteration")
347);
348impl_native_exception!(
349 PyStopIteration,
350 PyExc_StopIteration,
351 native_doc!("StopIteration"),
352 ffi::PyStopIterationObject
353);
354impl_native_exception!(
355 PyGeneratorExit,
356 PyExc_GeneratorExit,
357 native_doc!("GeneratorExit")
358);
359impl_native_exception!(
360 PyArithmeticError,
361 PyExc_ArithmeticError,
362 native_doc!("ArithmeticError")
363);
364impl_native_exception!(PyLookupError, PyExc_LookupError, native_doc!("LookupError"));
365
366impl_native_exception!(
367 PyAssertionError,
368 PyExc_AssertionError,
369 native_doc!("AssertionError")
370);
371impl_native_exception!(
372 PyAttributeError,
373 PyExc_AttributeError,
374 native_doc!("AttributeError")
375);
376impl_native_exception!(PyBufferError, PyExc_BufferError, native_doc!("BufferError"));
377impl_native_exception!(PyEOFError, PyExc_EOFError, native_doc!("EOFError"));
378impl_native_exception!(
379 PyFloatingPointError,
380 PyExc_FloatingPointError,
381 native_doc!("FloatingPointError")
382);
383#[cfg(not(any(PyPy, GraalPy)))]
384impl_native_exception!(
385 PyOSError,
386 PyExc_OSError,
387 native_doc!("OSError"),
388 ffi::PyOSErrorObject
389);
390#[cfg(any(PyPy, GraalPy))]
391impl_native_exception!(PyOSError, PyExc_OSError, native_doc!("OSError"));
392impl_native_exception!(PyImportError, PyExc_ImportError, native_doc!("ImportError"));
393
394impl_native_exception!(
395 PyModuleNotFoundError,
396 PyExc_ModuleNotFoundError,
397 native_doc!("ModuleNotFoundError")
398);
399
400impl_native_exception!(PyIndexError, PyExc_IndexError, native_doc!("IndexError"));
401impl_native_exception!(PyKeyError, PyExc_KeyError, native_doc!("KeyError"));
402impl_native_exception!(
403 PyKeyboardInterrupt,
404 PyExc_KeyboardInterrupt,
405 native_doc!("KeyboardInterrupt")
406);
407impl_native_exception!(PyMemoryError, PyExc_MemoryError, native_doc!("MemoryError"));
408impl_native_exception!(PyNameError, PyExc_NameError, native_doc!("NameError"));
409impl_native_exception!(
410 PyOverflowError,
411 PyExc_OverflowError,
412 native_doc!("OverflowError")
413);
414impl_native_exception!(
415 PyRuntimeError,
416 PyExc_RuntimeError,
417 native_doc!("RuntimeError")
418);
419impl_native_exception!(
420 PyRecursionError,
421 PyExc_RecursionError,
422 native_doc!("RecursionError")
423);
424impl_native_exception!(
425 PyNotImplementedError,
426 PyExc_NotImplementedError,
427 native_doc!("NotImplementedError")
428);
429#[cfg(not(any(PyPy, GraalPy)))]
430impl_native_exception!(
431 PySyntaxError,
432 PyExc_SyntaxError,
433 native_doc!("SyntaxError"),
434 ffi::PySyntaxErrorObject
435);
436#[cfg(any(PyPy, GraalPy))]
437impl_native_exception!(PySyntaxError, PyExc_SyntaxError, native_doc!("SyntaxError"));
438impl_native_exception!(
439 PyReferenceError,
440 PyExc_ReferenceError,
441 native_doc!("ReferenceError")
442);
443impl_native_exception!(PySystemError, PyExc_SystemError, native_doc!("SystemError"));
444#[cfg(not(any(PyPy, GraalPy)))]
445impl_native_exception!(
446 PySystemExit,
447 PyExc_SystemExit,
448 native_doc!("SystemExit"),
449 ffi::PySystemExitObject
450);
451#[cfg(any(PyPy, GraalPy))]
452impl_native_exception!(PySystemExit, PyExc_SystemExit, native_doc!("SystemExit"));
453impl_native_exception!(PyTypeError, PyExc_TypeError, native_doc!("TypeError"));
454impl_native_exception!(
455 PyUnboundLocalError,
456 PyExc_UnboundLocalError,
457 native_doc!("UnboundLocalError")
458);
459#[cfg(not(any(PyPy, GraalPy)))]
460impl_native_exception!(
461 PyUnicodeError,
462 PyExc_UnicodeError,
463 native_doc!("UnicodeError"),
464 ffi::PyUnicodeErrorObject
465);
466#[cfg(any(PyPy, GraalPy))]
467impl_native_exception!(
468 PyUnicodeError,
469 PyExc_UnicodeError,
470 native_doc!("UnicodeError")
471);
472impl_native_exception!(
474 PyUnicodeDecodeError,
475 PyExc_UnicodeDecodeError,
476 native_doc!("UnicodeDecodeError", "")
477);
478impl_native_exception!(
479 PyUnicodeEncodeError,
480 PyExc_UnicodeEncodeError,
481 native_doc!("UnicodeEncodeError", "")
482);
483impl_native_exception!(
484 PyUnicodeTranslateError,
485 PyExc_UnicodeTranslateError,
486 native_doc!("UnicodeTranslateError", "")
487);
488#[cfg(Py_3_11)]
489impl_native_exception!(
490 PyBaseExceptionGroup,
491 PyExc_BaseExceptionGroup,
492 native_doc!("BaseExceptionGroup", "")
493);
494impl_native_exception!(PyValueError, PyExc_ValueError, native_doc!("ValueError"));
495impl_native_exception!(
496 PyZeroDivisionError,
497 PyExc_ZeroDivisionError,
498 native_doc!("ZeroDivisionError")
499);
500
501impl_native_exception!(
502 PyBlockingIOError,
503 PyExc_BlockingIOError,
504 native_doc!("BlockingIOError")
505);
506impl_native_exception!(
507 PyBrokenPipeError,
508 PyExc_BrokenPipeError,
509 native_doc!("BrokenPipeError")
510);
511impl_native_exception!(
512 PyChildProcessError,
513 PyExc_ChildProcessError,
514 native_doc!("ChildProcessError")
515);
516impl_native_exception!(
517 PyConnectionError,
518 PyExc_ConnectionError,
519 native_doc!("ConnectionError")
520);
521impl_native_exception!(
522 PyConnectionAbortedError,
523 PyExc_ConnectionAbortedError,
524 native_doc!("ConnectionAbortedError")
525);
526impl_native_exception!(
527 PyConnectionRefusedError,
528 PyExc_ConnectionRefusedError,
529 native_doc!("ConnectionRefusedError")
530);
531impl_native_exception!(
532 PyConnectionResetError,
533 PyExc_ConnectionResetError,
534 native_doc!("ConnectionResetError")
535);
536impl_native_exception!(
537 PyFileExistsError,
538 PyExc_FileExistsError,
539 native_doc!("FileExistsError")
540);
541impl_native_exception!(
542 PyFileNotFoundError,
543 PyExc_FileNotFoundError,
544 native_doc!("FileNotFoundError")
545);
546impl_native_exception!(
547 PyInterruptedError,
548 PyExc_InterruptedError,
549 native_doc!("InterruptedError")
550);
551impl_native_exception!(
552 PyIsADirectoryError,
553 PyExc_IsADirectoryError,
554 native_doc!("IsADirectoryError")
555);
556impl_native_exception!(
557 PyNotADirectoryError,
558 PyExc_NotADirectoryError,
559 native_doc!("NotADirectoryError")
560);
561impl_native_exception!(
562 PyPermissionError,
563 PyExc_PermissionError,
564 native_doc!("PermissionError")
565);
566impl_native_exception!(
567 PyProcessLookupError,
568 PyExc_ProcessLookupError,
569 native_doc!("ProcessLookupError")
570);
571impl_native_exception!(
572 PyTimeoutError,
573 PyExc_TimeoutError,
574 native_doc!("TimeoutError")
575);
576
577impl_native_exception!(
578 PyEnvironmentError,
579 PyExc_EnvironmentError,
580 native_doc!("EnvironmentError")
581);
582impl_native_exception!(PyIOError, PyExc_IOError, native_doc!("IOError"));
583
584#[cfg(windows)]
585impl_windows_native_exception!(
586 PyWindowsError,
587 PyExc_WindowsError,
588 native_doc!("WindowsError")
589);
590
591impl PyUnicodeDecodeError {
592 pub fn new<'py>(
594 py: Python<'py>,
595 encoding: &CStr,
596 input: &[u8],
597 range: ops::Range<usize>,
598 reason: &CStr,
599 ) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
600 use crate::ffi_ptr_ext::FfiPtrExt;
601 use crate::py_result_ext::PyResultExt;
602 unsafe {
603 ffi::PyUnicodeDecodeError_Create(
604 encoding.as_ptr(),
605 input.as_ptr().cast(),
606 input.len() as ffi::Py_ssize_t,
607 range.start as ffi::Py_ssize_t,
608 range.end as ffi::Py_ssize_t,
609 reason.as_ptr(),
610 )
611 .assume_owned_or_err(py)
612 }
613 .cast_into()
614 }
615
616 pub fn new_utf8<'py>(
638 py: Python<'py>,
639 input: &[u8],
640 err: std::str::Utf8Error,
641 ) -> PyResult<Bound<'py, PyUnicodeDecodeError>> {
642 let pos = err.valid_up_to();
643 PyUnicodeDecodeError::new(
644 py,
645 ffi::c_str!("utf-8"),
646 input,
647 pos..(pos + 1),
648 ffi::c_str!("invalid utf-8"),
649 )
650 }
651}
652
653impl_native_exception!(PyWarning, PyExc_Warning, native_doc!("Warning"));
654impl_native_exception!(PyUserWarning, PyExc_UserWarning, native_doc!("UserWarning"));
655impl_native_exception!(
656 PyDeprecationWarning,
657 PyExc_DeprecationWarning,
658 native_doc!("DeprecationWarning")
659);
660impl_native_exception!(
661 PyPendingDeprecationWarning,
662 PyExc_PendingDeprecationWarning,
663 native_doc!("PendingDeprecationWarning")
664);
665impl_native_exception!(
666 PySyntaxWarning,
667 PyExc_SyntaxWarning,
668 native_doc!("SyntaxWarning")
669);
670impl_native_exception!(
671 PyRuntimeWarning,
672 PyExc_RuntimeWarning,
673 native_doc!("RuntimeWarning")
674);
675impl_native_exception!(
676 PyFutureWarning,
677 PyExc_FutureWarning,
678 native_doc!("FutureWarning")
679);
680impl_native_exception!(
681 PyImportWarning,
682 PyExc_ImportWarning,
683 native_doc!("ImportWarning")
684);
685impl_native_exception!(
686 PyUnicodeWarning,
687 PyExc_UnicodeWarning,
688 native_doc!("UnicodeWarning")
689);
690impl_native_exception!(
691 PyBytesWarning,
692 PyExc_BytesWarning,
693 native_doc!("BytesWarning")
694);
695impl_native_exception!(
696 PyResourceWarning,
697 PyExc_ResourceWarning,
698 native_doc!("ResourceWarning")
699);
700
701#[cfg(Py_3_10)]
702impl_native_exception!(
703 PyEncodingWarning,
704 PyExc_EncodingWarning,
705 native_doc!("EncodingWarning")
706);
707
708#[cfg(test)]
709macro_rules! test_exception {
710 ($exc_ty:ident $(, |$py:tt| $constructor:expr )?) => {
711 #[allow(non_snake_case)]
712 #[test]
713 fn $exc_ty () {
714 use super::$exc_ty;
715
716 $crate::Python::attach(|py| {
717 let err: $crate::PyErr = {
718 None
719 $(
720 .or(Some({ let $py = py; $constructor }))
721 )?
722 .unwrap_or($exc_ty::new_err("a test exception"))
723 };
724
725 assert!(err.is_instance_of::<$exc_ty>(py));
726
727 let value = err.value(py).as_any().cast::<$exc_ty>().unwrap();
728
729 assert!($crate::PyErr::from(value.clone()).is_instance_of::<$exc_ty>(py));
730 })
731 }
732 };
733}
734
735pub mod asyncio {
738 import_exception!(asyncio, CancelledError);
739 import_exception!(asyncio, InvalidStateError);
740 import_exception!(asyncio, TimeoutError);
741 import_exception!(asyncio, IncompleteReadError);
742 import_exception!(asyncio, LimitOverrunError);
743 import_exception!(asyncio, QueueEmpty);
744 import_exception!(asyncio, QueueFull);
745
746 #[cfg(test)]
747 mod tests {
748 test_exception!(CancelledError);
749 test_exception!(InvalidStateError);
750 test_exception!(TimeoutError);
751 test_exception!(IncompleteReadError, |_| IncompleteReadError::new_err((
752 "partial", "expected"
753 )));
754 test_exception!(LimitOverrunError, |_| LimitOverrunError::new_err((
755 "message", "consumed"
756 )));
757 test_exception!(QueueEmpty);
758 test_exception!(QueueFull);
759 }
760}
761
762pub mod socket {
765 import_exception!(socket, herror);
766 import_exception!(socket, gaierror);
767 import_exception!(socket, timeout);
768
769 #[cfg(test)]
770 mod tests {
771 test_exception!(herror);
772 test_exception!(gaierror);
773 test_exception!(timeout);
774 }
775}
776
777#[cfg(test)]
778mod tests {
779 use super::*;
780 use crate::types::any::PyAnyMethods;
781 use crate::types::{IntoPyDict, PyDict};
782 use crate::PyErr;
783
784 import_exception!(socket, gaierror);
785 import_exception!(email.errors, MessageError);
786
787 #[test]
788 fn test_check_exception() {
789 Python::attach(|py| {
790 let err: PyErr = gaierror::new_err(());
791 let socket = py
792 .import("socket")
793 .map_err(|e| e.display(py))
794 .expect("could not import socket");
795
796 let d = PyDict::new(py);
797 d.set_item("socket", socket)
798 .map_err(|e| e.display(py))
799 .expect("could not setitem");
800
801 d.set_item("exc", err)
802 .map_err(|e| e.display(py))
803 .expect("could not setitem");
804
805 py.run(
806 ffi::c_str!("assert isinstance(exc, socket.gaierror)"),
807 None,
808 Some(&d),
809 )
810 .map_err(|e| e.display(py))
811 .expect("assertion failed");
812 });
813 }
814
815 #[test]
816 fn test_check_exception_nested() {
817 Python::attach(|py| {
818 let err: PyErr = MessageError::new_err(());
819 let email = py
820 .import("email")
821 .map_err(|e| e.display(py))
822 .expect("could not import email");
823
824 let d = PyDict::new(py);
825 d.set_item("email", email)
826 .map_err(|e| e.display(py))
827 .expect("could not setitem");
828 d.set_item("exc", err)
829 .map_err(|e| e.display(py))
830 .expect("could not setitem");
831
832 py.run(
833 ffi::c_str!("assert isinstance(exc, email.errors.MessageError)"),
834 None,
835 Some(&d),
836 )
837 .map_err(|e| e.display(py))
838 .expect("assertion failed");
839 });
840 }
841
842 #[test]
843 fn custom_exception() {
844 create_exception!(mymodule, CustomError, PyException);
845
846 Python::attach(|py| {
847 let error_type = py.get_type::<CustomError>();
848 let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
849 let type_description: String = py
850 .eval(ffi::c_str!("str(CustomError)"), None, Some(&ctx))
851 .unwrap()
852 .extract()
853 .unwrap();
854 assert_eq!(type_description, "<class 'mymodule.CustomError'>");
855 py.run(
856 ffi::c_str!("assert CustomError('oops').args == ('oops',)"),
857 None,
858 Some(&ctx),
859 )
860 .unwrap();
861 py.run(
862 ffi::c_str!("assert CustomError.__doc__ is None"),
863 None,
864 Some(&ctx),
865 )
866 .unwrap();
867 });
868 }
869
870 #[test]
871 fn custom_exception_dotted_module() {
872 create_exception!(mymodule.exceptions, CustomError, PyException);
873 Python::attach(|py| {
874 let error_type = py.get_type::<CustomError>();
875 let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
876 let type_description: String = py
877 .eval(ffi::c_str!("str(CustomError)"), None, Some(&ctx))
878 .unwrap()
879 .extract()
880 .unwrap();
881 assert_eq!(
882 type_description,
883 "<class 'mymodule.exceptions.CustomError'>"
884 );
885 });
886 }
887
888 #[test]
889 fn custom_exception_doc() {
890 create_exception!(mymodule, CustomError, PyException, "Some docs");
891
892 Python::attach(|py| {
893 let error_type = py.get_type::<CustomError>();
894 let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
895 let type_description: String = py
896 .eval(ffi::c_str!("str(CustomError)"), None, Some(&ctx))
897 .unwrap()
898 .extract()
899 .unwrap();
900 assert_eq!(type_description, "<class 'mymodule.CustomError'>");
901 py.run(
902 ffi::c_str!("assert CustomError('oops').args == ('oops',)"),
903 None,
904 Some(&ctx),
905 )
906 .unwrap();
907 py.run(
908 ffi::c_str!("assert CustomError.__doc__ == 'Some docs'"),
909 None,
910 Some(&ctx),
911 )
912 .unwrap();
913 });
914 }
915
916 #[test]
917 fn custom_exception_doc_expr() {
918 create_exception!(
919 mymodule,
920 CustomError,
921 PyException,
922 concat!("Some", " more ", stringify!(docs))
923 );
924
925 Python::attach(|py| {
926 let error_type = py.get_type::<CustomError>();
927 let ctx = [("CustomError", error_type)].into_py_dict(py).unwrap();
928 let type_description: String = py
929 .eval(ffi::c_str!("str(CustomError)"), None, Some(&ctx))
930 .unwrap()
931 .extract()
932 .unwrap();
933 assert_eq!(type_description, "<class 'mymodule.CustomError'>");
934 py.run(
935 ffi::c_str!("assert CustomError('oops').args == ('oops',)"),
936 None,
937 Some(&ctx),
938 )
939 .unwrap();
940 py.run(
941 ffi::c_str!("assert CustomError.__doc__ == 'Some more docs'"),
942 None,
943 Some(&ctx),
944 )
945 .unwrap();
946 });
947 }
948
949 #[test]
950 fn native_exception_debug() {
951 Python::attach(|py| {
952 let exc = py
953 .run(ffi::c_str!("raise Exception('banana')"), None, None)
954 .expect_err("raising should have given us an error")
955 .into_value(py)
956 .into_bound(py);
957 assert_eq!(
958 format!("{exc:?}"),
959 exc.repr().unwrap().extract::<String>().unwrap()
960 );
961 });
962 }
963
964 #[test]
965 fn native_exception_display() {
966 Python::attach(|py| {
967 let exc = py
968 .run(ffi::c_str!("raise Exception('banana')"), None, None)
969 .expect_err("raising should have given us an error")
970 .into_value(py)
971 .into_bound(py);
972 assert_eq!(
973 exc.to_string(),
974 exc.str().unwrap().extract::<String>().unwrap()
975 );
976 });
977 }
978
979 #[test]
980 fn unicode_decode_error() {
981 let invalid_utf8 = b"fo\xd8o";
982 #[allow(invalid_from_utf8)]
983 let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
984 Python::attach(|py| {
985 let decode_err = PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err).unwrap();
986 assert_eq!(
987 format!("{decode_err:?}"),
988 "UnicodeDecodeError('utf-8', b'fo\\xd8o', 2, 3, 'invalid utf-8')"
989 );
990
991 let e: PyErr = decode_err.into();
993 e.restore(py);
994
995 assert_eq!(
996 PyErr::fetch(py).to_string(),
997 "UnicodeDecodeError: \'utf-8\' codec can\'t decode byte 0xd8 in position 2: invalid utf-8"
998 );
999 });
1000 }
1001 #[cfg(Py_3_11)]
1002 test_exception!(PyBaseExceptionGroup, |_| PyBaseExceptionGroup::new_err((
1003 "msg",
1004 vec![PyValueError::new_err("err")]
1005 )));
1006 test_exception!(PyBaseException);
1007 test_exception!(PyException);
1008 test_exception!(PyStopAsyncIteration);
1009 test_exception!(PyStopIteration);
1010 test_exception!(PyGeneratorExit);
1011 test_exception!(PyArithmeticError);
1012 test_exception!(PyLookupError);
1013 test_exception!(PyAssertionError);
1014 test_exception!(PyAttributeError);
1015 test_exception!(PyBufferError);
1016 test_exception!(PyEOFError);
1017 test_exception!(PyFloatingPointError);
1018 test_exception!(PyOSError);
1019 test_exception!(PyImportError);
1020 test_exception!(PyModuleNotFoundError);
1021 test_exception!(PyIndexError);
1022 test_exception!(PyKeyError);
1023 test_exception!(PyKeyboardInterrupt);
1024 test_exception!(PyMemoryError);
1025 test_exception!(PyNameError);
1026 test_exception!(PyOverflowError);
1027 test_exception!(PyRuntimeError);
1028 test_exception!(PyRecursionError);
1029 test_exception!(PyNotImplementedError);
1030 test_exception!(PySyntaxError);
1031 test_exception!(PyReferenceError);
1032 test_exception!(PySystemError);
1033 test_exception!(PySystemExit);
1034 test_exception!(PyTypeError);
1035 test_exception!(PyUnboundLocalError);
1036 test_exception!(PyUnicodeError);
1037 test_exception!(PyUnicodeDecodeError, |py| {
1038 let invalid_utf8 = b"fo\xd8o";
1039 #[allow(invalid_from_utf8)]
1040 let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
1041 PyErr::from_value(
1042 PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err)
1043 .unwrap()
1044 .into_any(),
1045 )
1046 });
1047 test_exception!(PyUnicodeEncodeError, |py| py
1048 .eval(ffi::c_str!("chr(40960).encode('ascii')"), None, None)
1049 .unwrap_err());
1050 test_exception!(PyUnicodeTranslateError, |_| {
1051 PyUnicodeTranslateError::new_err(("\u{3042}", 0, 1, "ouch"))
1052 });
1053 test_exception!(PyValueError);
1054 test_exception!(PyZeroDivisionError);
1055 test_exception!(PyBlockingIOError);
1056 test_exception!(PyBrokenPipeError);
1057 test_exception!(PyChildProcessError);
1058 test_exception!(PyConnectionError);
1059 test_exception!(PyConnectionAbortedError);
1060 test_exception!(PyConnectionRefusedError);
1061 test_exception!(PyConnectionResetError);
1062 test_exception!(PyFileExistsError);
1063 test_exception!(PyFileNotFoundError);
1064 test_exception!(PyInterruptedError);
1065 test_exception!(PyIsADirectoryError);
1066 test_exception!(PyNotADirectoryError);
1067 test_exception!(PyPermissionError);
1068 test_exception!(PyProcessLookupError);
1069 test_exception!(PyTimeoutError);
1070 test_exception!(PyEnvironmentError);
1071 test_exception!(PyIOError);
1072 #[cfg(windows)]
1073 test_exception!(PyWindowsError);
1074
1075 test_exception!(PyWarning);
1076 test_exception!(PyUserWarning);
1077 test_exception!(PyDeprecationWarning);
1078 test_exception!(PyPendingDeprecationWarning);
1079 test_exception!(PySyntaxWarning);
1080 test_exception!(PyRuntimeWarning);
1081 test_exception!(PyFutureWarning);
1082 test_exception!(PyImportWarning);
1083 test_exception!(PyUnicodeWarning);
1084 test_exception!(PyBytesWarning);
1085 #[cfg(Py_3_10)]
1086 test_exception!(PyEncodingWarning);
1087}