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