1use crate::instance::Bound;
2use crate::panic::PanicException;
3use crate::type_object::PyTypeInfo;
4use crate::types::any::PyAnyMethods;
5use crate::types::{string::PyStringMethods, typeobject::PyTypeMethods, PyTraceback, PyType};
6use crate::{
7 exceptions::{self, PyBaseException},
8 ffi,
9};
10use crate::{Borrowed, BoundObject, Py, PyAny, PyObject, Python};
11#[allow(deprecated)]
12use crate::{IntoPy, ToPyObject};
13use std::borrow::Cow;
14use std::ffi::{CStr, CString};
15
16mod err_state;
17mod impls;
18
19use crate::conversion::IntoPyObject;
20use err_state::{PyErrState, PyErrStateLazyFnOutput, PyErrStateNormalized};
21use std::convert::Infallible;
22
23pub struct PyErr {
33 state: PyErrState,
34}
35
36#[cfg(feature = "nightly")]
38unsafe impl crate::marker::Ungil for PyErr {}
39
40pub type PyResult<T> = Result<T, PyErr>;
42
43#[derive(Debug)]
45pub struct DowncastError<'a, 'py> {
46 from: Borrowed<'a, 'py, PyAny>,
47 to: Cow<'static, str>,
48}
49
50impl<'a, 'py> DowncastError<'a, 'py> {
51 pub fn new(from: &'a Bound<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self {
54 DowncastError {
55 from: from.as_borrowed(),
56 to: to.into(),
57 }
58 }
59 pub(crate) fn new_from_borrowed(
60 from: Borrowed<'a, 'py, PyAny>,
61 to: impl Into<Cow<'static, str>>,
62 ) -> Self {
63 DowncastError {
64 from,
65 to: to.into(),
66 }
67 }
68}
69
70#[derive(Debug)]
72pub struct DowncastIntoError<'py> {
73 from: Bound<'py, PyAny>,
74 to: Cow<'static, str>,
75}
76
77impl<'py> DowncastIntoError<'py> {
78 pub fn new(from: Bound<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self {
81 DowncastIntoError {
82 from,
83 to: to.into(),
84 }
85 }
86
87 pub fn into_inner(self) -> Bound<'py, PyAny> {
92 self.from
93 }
94}
95
96pub trait PyErrArguments: Send + Sync {
98 fn arguments(self, py: Python<'_>) -> PyObject;
100}
101
102impl<T> PyErrArguments for T
103where
104 T: for<'py> IntoPyObject<'py> + Send + Sync,
105{
106 fn arguments(self, py: Python<'_>) -> PyObject {
107 match self.into_pyobject(py) {
109 Ok(obj) => obj.into_any().unbind(),
110 Err(e) => panic!("Converting PyErr arguments failed: {}", e.into()),
111 }
112 }
113}
114
115impl PyErr {
116 #[inline]
168 pub fn new<T, A>(args: A) -> PyErr
169 where
170 T: PyTypeInfo,
171 A: PyErrArguments + Send + Sync + 'static,
172 {
173 PyErr::from_state(PyErrState::lazy(Box::new(move |py| {
174 PyErrStateLazyFnOutput {
175 ptype: T::type_object(py).into(),
176 pvalue: args.arguments(py),
177 }
178 })))
179 }
180
181 pub fn from_type<A>(ty: Bound<'_, PyType>, args: A) -> PyErr
192 where
193 A: PyErrArguments + Send + Sync + 'static,
194 {
195 PyErr::from_state(PyErrState::lazy_arguments(ty.unbind().into_any(), args))
196 }
197
198 #[deprecated(since = "0.23.0", note = "renamed to `PyErr::from_type`")]
200 #[inline]
201 pub fn from_type_bound<A>(ty: Bound<'_, PyType>, args: A) -> PyErr
202 where
203 A: PyErrArguments + Send + Sync + 'static,
204 {
205 Self::from_type(ty, args)
206 }
207
208 pub fn from_value(obj: Bound<'_, PyAny>) -> PyErr {
242 let state = match obj.downcast_into::<PyBaseException>() {
243 Ok(obj) => PyErrState::normalized(PyErrStateNormalized::new(obj)),
244 Err(err) => {
245 let obj = err.into_inner();
248 let py = obj.py();
249 PyErrState::lazy_arguments(obj.unbind(), py.None())
250 }
251 };
252
253 PyErr::from_state(state)
254 }
255
256 #[deprecated(since = "0.23.0", note = "renamed to `PyErr::from_value`")]
258 #[inline]
259 pub fn from_value_bound(obj: Bound<'_, PyAny>) -> PyErr {
260 Self::from_value(obj)
261 }
262
263 pub fn get_type<'py>(&self, py: Python<'py>) -> Bound<'py, PyType> {
275 self.normalized(py).ptype(py)
276 }
277
278 #[deprecated(since = "0.23.0", note = "renamed to `PyErr::get_type`")]
280 #[inline]
281 pub fn get_type_bound<'py>(&self, py: Python<'py>) -> Bound<'py, PyType> {
282 self.get_type(py)
283 }
284
285 pub fn value<'py>(&self, py: Python<'py>) -> &Bound<'py, PyBaseException> {
299 self.normalized(py).pvalue.bind(py)
300 }
301
302 #[deprecated(since = "0.23.0", note = "renamed to `PyErr::value`")]
304 #[inline]
305 pub fn value_bound<'py>(&self, py: Python<'py>) -> &Bound<'py, PyBaseException> {
306 self.value(py)
307 }
308
309 pub fn into_value(self, py: Python<'_>) -> Py<PyBaseException> {
311 let normalized = self.normalized(py);
315 let exc = normalized.pvalue.clone_ref(py);
316 if let Some(tb) = normalized.ptraceback(py) {
317 unsafe {
318 ffi::PyException_SetTraceback(exc.as_ptr(), tb.as_ptr());
319 }
320 }
321 exc
322 }
323
324 pub fn traceback<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTraceback>> {
336 self.normalized(py).ptraceback(py)
337 }
338
339 #[deprecated(since = "0.23.0", note = "renamed to `PyErr::traceback`")]
341 #[inline]
342 pub fn traceback_bound<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTraceback>> {
343 self.traceback(py)
344 }
345
346 #[inline]
348 pub fn occurred(_: Python<'_>) -> bool {
349 unsafe { !ffi::PyErr_Occurred().is_null() }
350 }
351
352 pub fn take(py: Python<'_>) -> Option<PyErr> {
362 let state = PyErrStateNormalized::take(py)?;
363 let pvalue = state.pvalue.bind(py);
364 if pvalue.get_type().as_ptr() == PanicException::type_object_raw(py).cast() {
365 let msg: String = pvalue
366 .str()
367 .map(|py_str| py_str.to_string_lossy().into())
368 .unwrap_or_else(|_| String::from("Unwrapped panic from Python code"));
369 Self::print_panic_and_unwind(py, PyErrState::normalized(state), msg)
370 }
371
372 Some(PyErr::from_state(PyErrState::normalized(state)))
373 }
374
375 fn print_panic_and_unwind(py: Python<'_>, state: PyErrState, msg: String) -> ! {
376 eprintln!("--- PyO3 is resuming a panic after fetching a PanicException from Python. ---");
377 eprintln!("Python stack trace below:");
378
379 state.restore(py);
380
381 unsafe {
382 ffi::PyErr_PrintEx(0);
383 }
384
385 std::panic::resume_unwind(Box::new(msg))
386 }
387
388 #[cfg_attr(debug_assertions, track_caller)]
400 #[inline]
401 pub fn fetch(py: Python<'_>) -> PyErr {
402 const FAILED_TO_FETCH: &str = "attempted to fetch exception but none was set";
403 match PyErr::take(py) {
404 Some(err) => err,
405 #[cfg(debug_assertions)]
406 None => panic!("{}", FAILED_TO_FETCH),
407 #[cfg(not(debug_assertions))]
408 None => exceptions::PySystemError::new_err(FAILED_TO_FETCH),
409 }
410 }
411
412 pub fn new_type<'py>(
423 py: Python<'py>,
424 name: &CStr,
425 doc: Option<&CStr>,
426 base: Option<&Bound<'py, PyType>>,
427 dict: Option<PyObject>,
428 ) -> PyResult<Py<PyType>> {
429 let base: *mut ffi::PyObject = match base {
430 None => std::ptr::null_mut(),
431 Some(obj) => obj.as_ptr(),
432 };
433
434 let dict: *mut ffi::PyObject = match dict {
435 None => std::ptr::null_mut(),
436 Some(obj) => obj.as_ptr(),
437 };
438
439 let doc_ptr = match doc.as_ref() {
440 Some(c) => c.as_ptr(),
441 None => std::ptr::null(),
442 };
443
444 let ptr = unsafe { ffi::PyErr_NewExceptionWithDoc(name.as_ptr(), doc_ptr, base, dict) };
445
446 unsafe { Py::from_owned_ptr_or_err(py, ptr) }
447 }
448
449 #[deprecated(since = "0.23.0", note = "renamed to `PyErr::new_type`")]
451 #[inline]
452 pub fn new_type_bound<'py>(
453 py: Python<'py>,
454 name: &str,
455 doc: Option<&str>,
456 base: Option<&Bound<'py, PyType>>,
457 dict: Option<PyObject>,
458 ) -> PyResult<Py<PyType>> {
459 let null_terminated_name =
460 CString::new(name).expect("Failed to initialize nul terminated exception name");
461 let null_terminated_doc =
462 doc.map(|d| CString::new(d).expect("Failed to initialize nul terminated docstring"));
463 Self::new_type(
464 py,
465 &null_terminated_name,
466 null_terminated_doc.as_deref(),
467 base,
468 dict,
469 )
470 }
471
472 pub fn display(&self, py: Python<'_>) {
474 #[cfg(Py_3_12)]
475 unsafe {
476 ffi::PyErr_DisplayException(self.value(py).as_ptr())
477 }
478
479 #[cfg(not(Py_3_12))]
480 unsafe {
481 let traceback = self.traceback(py);
486 let type_bound = self.get_type(py);
487 ffi::PyErr_Display(
488 type_bound.as_ptr(),
489 self.value(py).as_ptr(),
490 traceback
491 .as_ref()
492 .map_or(std::ptr::null_mut(), |traceback| traceback.as_ptr()),
493 )
494 }
495 }
496
497 pub fn print(&self, py: Python<'_>) {
499 self.clone_ref(py).restore(py);
500 unsafe { ffi::PyErr_PrintEx(0) }
501 }
502
503 pub fn print_and_set_sys_last_vars(&self, py: Python<'_>) {
507 self.clone_ref(py).restore(py);
508 unsafe { ffi::PyErr_PrintEx(1) }
509 }
510
511 pub fn matches<'py, T>(&self, py: Python<'py>, exc: T) -> Result<bool, T::Error>
516 where
517 T: IntoPyObject<'py>,
518 {
519 Ok(self.is_instance(py, &exc.into_pyobject(py)?.into_any().as_borrowed()))
520 }
521
522 #[inline]
524 pub fn is_instance(&self, py: Python<'_>, ty: &Bound<'_, PyAny>) -> bool {
525 let type_bound = self.get_type(py);
526 (unsafe { ffi::PyErr_GivenExceptionMatches(type_bound.as_ptr(), ty.as_ptr()) }) != 0
527 }
528
529 #[deprecated(since = "0.23.0", note = "renamed to `PyErr::is_instance`")]
531 #[inline]
532 pub fn is_instance_bound(&self, py: Python<'_>, ty: &Bound<'_, PyAny>) -> bool {
533 self.is_instance(py, ty)
534 }
535
536 #[inline]
538 pub fn is_instance_of<T>(&self, py: Python<'_>) -> bool
539 where
540 T: PyTypeInfo,
541 {
542 self.is_instance(py, &T::type_object(py))
543 }
544
545 #[inline]
548 pub fn restore(self, py: Python<'_>) {
549 self.state.restore(py)
550 }
551
552 #[inline]
581 pub fn write_unraisable(self, py: Python<'_>, obj: Option<&Bound<'_, PyAny>>) {
582 self.restore(py);
583 unsafe { ffi::PyErr_WriteUnraisable(obj.map_or(std::ptr::null_mut(), Bound::as_ptr)) }
584 }
585
586 #[deprecated(since = "0.23.0", note = "renamed to `PyErr::write_unraisable`")]
588 #[inline]
589 pub fn write_unraisable_bound(self, py: Python<'_>, obj: Option<&Bound<'_, PyAny>>) {
590 self.write_unraisable(py, obj)
591 }
592
593 pub fn warn<'py>(
616 py: Python<'py>,
617 category: &Bound<'py, PyAny>,
618 message: &CStr,
619 stacklevel: i32,
620 ) -> PyResult<()> {
621 error_on_minusone(py, unsafe {
622 ffi::PyErr_WarnEx(
623 category.as_ptr(),
624 message.as_ptr(),
625 stacklevel as ffi::Py_ssize_t,
626 )
627 })
628 }
629
630 #[deprecated(since = "0.23.0", note = "renamed to `PyErr::warn`")]
632 #[inline]
633 pub fn warn_bound<'py>(
634 py: Python<'py>,
635 category: &Bound<'py, PyAny>,
636 message: &str,
637 stacklevel: i32,
638 ) -> PyResult<()> {
639 let message = CString::new(message)?;
640 Self::warn(py, category, &message, stacklevel)
641 }
642
643 pub fn warn_explicit<'py>(
652 py: Python<'py>,
653 category: &Bound<'py, PyAny>,
654 message: &CStr,
655 filename: &CStr,
656 lineno: i32,
657 module: Option<&CStr>,
658 registry: Option<&Bound<'py, PyAny>>,
659 ) -> PyResult<()> {
660 let module_ptr = match module {
661 None => std::ptr::null_mut(),
662 Some(s) => s.as_ptr(),
663 };
664 let registry: *mut ffi::PyObject = match registry {
665 None => std::ptr::null_mut(),
666 Some(obj) => obj.as_ptr(),
667 };
668 error_on_minusone(py, unsafe {
669 ffi::PyErr_WarnExplicit(
670 category.as_ptr(),
671 message.as_ptr(),
672 filename.as_ptr(),
673 lineno,
674 module_ptr,
675 registry,
676 )
677 })
678 }
679
680 #[deprecated(since = "0.23.0", note = "renamed to `PyErr::warn`")]
682 #[inline]
683 pub fn warn_explicit_bound<'py>(
684 py: Python<'py>,
685 category: &Bound<'py, PyAny>,
686 message: &str,
687 filename: &str,
688 lineno: i32,
689 module: Option<&str>,
690 registry: Option<&Bound<'py, PyAny>>,
691 ) -> PyResult<()> {
692 let message = CString::new(message)?;
693 let filename = CString::new(filename)?;
694 let module = module.map(CString::new).transpose()?;
695 Self::warn_explicit(
696 py,
697 category,
698 &message,
699 &filename,
700 lineno,
701 module.as_deref(),
702 registry,
703 )
704 }
705
706 #[inline]
723 pub fn clone_ref(&self, py: Python<'_>) -> PyErr {
724 PyErr::from_state(PyErrState::normalized(self.normalized(py).clone_ref(py)))
725 }
726
727 pub fn cause(&self, py: Python<'_>) -> Option<PyErr> {
730 use crate::ffi_ptr_ext::FfiPtrExt;
731 let obj =
732 unsafe { ffi::PyException_GetCause(self.value(py).as_ptr()).assume_owned_or_opt(py) };
733 #[cfg(GraalPy)]
735 if let Some(cause) = &obj {
736 if cause.is_none() {
737 return None;
738 }
739 }
740 obj.map(Self::from_value)
741 }
742
743 pub fn set_cause(&self, py: Python<'_>, cause: Option<Self>) {
745 let value = self.value(py);
746 let cause = cause.map(|err| err.into_value(py));
747 unsafe {
748 ffi::PyException_SetCause(
750 value.as_ptr(),
751 cause.map_or(std::ptr::null_mut(), Py::into_ptr),
752 );
753 }
754 }
755
756 #[inline]
757 fn from_state(state: PyErrState) -> PyErr {
758 PyErr { state }
759 }
760
761 #[inline]
762 fn normalized(&self, py: Python<'_>) -> &PyErrStateNormalized {
763 self.state.as_normalized(py)
764 }
765}
766
767impl std::fmt::Debug for PyErr {
768 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
769 Python::with_gil(|py| {
770 f.debug_struct("PyErr")
771 .field("type", &self.get_type(py))
772 .field("value", self.value(py))
773 .field("traceback", &self.traceback(py))
774 .finish()
775 })
776 }
777}
778
779impl std::fmt::Display for PyErr {
780 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
781 Python::with_gil(|py| {
782 let value = self.value(py);
783 let type_name = value.get_type().qualname().map_err(|_| std::fmt::Error)?;
784 write!(f, "{}", type_name)?;
785 if let Ok(s) = value.str() {
786 write!(f, ": {}", &s.to_string_lossy())
787 } else {
788 write!(f, ": <exception str() failed>")
789 }
790 })
791 }
792}
793
794impl std::error::Error for PyErr {}
795
796#[allow(deprecated)]
797impl IntoPy<PyObject> for PyErr {
798 #[inline]
799 fn into_py(self, py: Python<'_>) -> PyObject {
800 self.into_pyobject(py).unwrap().into_any().unbind()
801 }
802}
803
804#[allow(deprecated)]
805impl ToPyObject for PyErr {
806 #[inline]
807 fn to_object(&self, py: Python<'_>) -> PyObject {
808 self.into_pyobject(py).unwrap().into_any().unbind()
809 }
810}
811
812#[allow(deprecated)]
813impl IntoPy<PyObject> for &PyErr {
814 #[inline]
815 fn into_py(self, py: Python<'_>) -> PyObject {
816 self.into_pyobject(py).unwrap().into_any().unbind()
817 }
818}
819
820impl<'py> IntoPyObject<'py> for PyErr {
821 type Target = PyBaseException;
822 type Output = Bound<'py, Self::Target>;
823 type Error = Infallible;
824
825 #[inline]
826 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
827 Ok(self.into_value(py).into_bound(py))
828 }
829}
830
831impl<'py> IntoPyObject<'py> for &PyErr {
832 type Target = PyBaseException;
833 type Output = Bound<'py, Self::Target>;
834 type Error = Infallible;
835
836 #[inline]
837 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
838 self.clone_ref(py).into_pyobject(py)
839 }
840}
841
842struct PyDowncastErrorArguments {
843 from: Py<PyType>,
844 to: Cow<'static, str>,
845}
846
847impl PyErrArguments for PyDowncastErrorArguments {
848 fn arguments(self, py: Python<'_>) -> PyObject {
849 const FAILED_TO_EXTRACT: Cow<'_, str> = Cow::Borrowed("<failed to extract type name>");
850 let from = self.from.bind(py).qualname();
851 let from = match &from {
852 Ok(qn) => qn.to_cow().unwrap_or(FAILED_TO_EXTRACT),
853 Err(_) => FAILED_TO_EXTRACT,
854 };
855 format!("'{}' object cannot be converted to '{}'", from, self.to)
856 .into_pyobject(py)
857 .unwrap()
858 .into_any()
859 .unbind()
860 }
861}
862
863pub trait ToPyErr {}
870
871impl<'py, T> std::convert::From<Bound<'py, T>> for PyErr
872where
873 T: ToPyErr,
874{
875 #[inline]
876 fn from(err: Bound<'py, T>) -> PyErr {
877 PyErr::from_value(err.into_any())
878 }
879}
880
881impl std::convert::From<DowncastError<'_, '_>> for PyErr {
883 fn from(err: DowncastError<'_, '_>) -> PyErr {
884 let args = PyDowncastErrorArguments {
885 from: err.from.get_type().into(),
886 to: err.to,
887 };
888
889 exceptions::PyTypeError::new_err(args)
890 }
891}
892
893impl std::error::Error for DowncastError<'_, '_> {}
894
895impl std::fmt::Display for DowncastError<'_, '_> {
896 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
897 display_downcast_error(f, &self.from, &self.to)
898 }
899}
900
901impl std::convert::From<DowncastIntoError<'_>> for PyErr {
903 fn from(err: DowncastIntoError<'_>) -> PyErr {
904 let args = PyDowncastErrorArguments {
905 from: err.from.get_type().into(),
906 to: err.to,
907 };
908
909 exceptions::PyTypeError::new_err(args)
910 }
911}
912
913impl std::error::Error for DowncastIntoError<'_> {}
914
915impl std::fmt::Display for DowncastIntoError<'_> {
916 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
917 display_downcast_error(f, &self.from, &self.to)
918 }
919}
920
921fn display_downcast_error(
922 f: &mut std::fmt::Formatter<'_>,
923 from: &Bound<'_, PyAny>,
924 to: &str,
925) -> std::fmt::Result {
926 write!(
927 f,
928 "'{}' object cannot be converted to '{}'",
929 from.get_type().qualname().map_err(|_| std::fmt::Error)?,
930 to
931 )
932}
933
934#[track_caller]
935pub fn panic_after_error(_py: Python<'_>) -> ! {
936 unsafe {
937 ffi::PyErr_Print();
938 }
939 panic!("Python API call failed");
940}
941
942#[inline]
944pub(crate) fn error_on_minusone<T: SignedInteger>(py: Python<'_>, result: T) -> PyResult<()> {
945 if result != T::MINUS_ONE {
946 Ok(())
947 } else {
948 Err(PyErr::fetch(py))
949 }
950}
951
952pub(crate) trait SignedInteger: Eq {
953 const MINUS_ONE: Self;
954}
955
956macro_rules! impl_signed_integer {
957 ($t:ty) => {
958 impl SignedInteger for $t {
959 const MINUS_ONE: Self = -1;
960 }
961 };
962}
963
964impl_signed_integer!(i8);
965impl_signed_integer!(i16);
966impl_signed_integer!(i32);
967impl_signed_integer!(i64);
968impl_signed_integer!(i128);
969impl_signed_integer!(isize);
970
971#[cfg(test)]
972mod tests {
973 use super::PyErrState;
974 use crate::exceptions::{self, PyTypeError, PyValueError};
975 use crate::{ffi, PyErr, PyTypeInfo, Python};
976
977 #[test]
978 fn no_error() {
979 assert!(Python::with_gil(PyErr::take).is_none());
980 }
981
982 #[test]
983 fn set_valueerror() {
984 Python::with_gil(|py| {
985 let err: PyErr = exceptions::PyValueError::new_err("some exception message");
986 assert!(err.is_instance_of::<exceptions::PyValueError>(py));
987 err.restore(py);
988 assert!(PyErr::occurred(py));
989 let err = PyErr::fetch(py);
990 assert!(err.is_instance_of::<exceptions::PyValueError>(py));
991 assert_eq!(err.to_string(), "ValueError: some exception message");
992 })
993 }
994
995 #[test]
996 fn invalid_error_type() {
997 Python::with_gil(|py| {
998 let err: PyErr = PyErr::new::<crate::types::PyString, _>(());
999 assert!(err.is_instance_of::<exceptions::PyTypeError>(py));
1000 err.restore(py);
1001 let err = PyErr::fetch(py);
1002
1003 assert!(err.is_instance_of::<exceptions::PyTypeError>(py));
1004 assert_eq!(
1005 err.to_string(),
1006 "TypeError: exceptions must derive from BaseException"
1007 );
1008 })
1009 }
1010
1011 #[test]
1012 fn set_typeerror() {
1013 Python::with_gil(|py| {
1014 let err: PyErr = exceptions::PyTypeError::new_err(());
1015 err.restore(py);
1016 assert!(PyErr::occurred(py));
1017 drop(PyErr::fetch(py));
1018 });
1019 }
1020
1021 #[test]
1022 #[should_panic(expected = "new panic")]
1023 fn fetching_panic_exception_resumes_unwind() {
1024 use crate::panic::PanicException;
1025
1026 Python::with_gil(|py| {
1027 let err: PyErr = PanicException::new_err("new panic");
1028 err.restore(py);
1029 assert!(PyErr::occurred(py));
1030
1031 let _ = PyErr::fetch(py);
1033 });
1034 }
1035
1036 #[test]
1037 #[should_panic(expected = "new panic")]
1038 #[cfg(not(Py_3_12))]
1039 fn fetching_normalized_panic_exception_resumes_unwind() {
1040 use crate::panic::PanicException;
1041
1042 Python::with_gil(|py| {
1043 let err: PyErr = PanicException::new_err("new panic");
1044 let _ = err.normalized(py);
1047 err.restore(py);
1048 assert!(PyErr::occurred(py));
1049
1050 let _ = PyErr::fetch(py);
1052 });
1053 }
1054
1055 #[test]
1056 fn err_debug() {
1057 Python::with_gil(|py| {
1065 let err = py
1066 .run(ffi::c_str!("raise Exception('banana')"), None, None)
1067 .expect_err("raising should have given us an error");
1068
1069 let debug_str = format!("{:?}", err);
1070 assert!(debug_str.starts_with("PyErr { "));
1071 assert!(debug_str.ends_with(" }"));
1072
1073 let mut fields = debug_str["PyErr { ".len()..debug_str.len() - 2].split(", ");
1075
1076 assert_eq!(fields.next().unwrap(), "type: <class 'Exception'>");
1077 assert_eq!(fields.next().unwrap(), "value: Exception('banana')");
1078
1079 let traceback = fields.next().unwrap();
1080 assert!(traceback.starts_with("traceback: Some(<traceback object at 0x"));
1081 assert!(traceback.ends_with(">)"));
1082
1083 assert!(fields.next().is_none());
1084 });
1085 }
1086
1087 #[test]
1088 fn err_display() {
1089 Python::with_gil(|py| {
1090 let err = py
1091 .run(ffi::c_str!("raise Exception('banana')"), None, None)
1092 .expect_err("raising should have given us an error");
1093 assert_eq!(err.to_string(), "Exception: banana");
1094 });
1095 }
1096
1097 #[test]
1098 fn test_pyerr_send_sync() {
1099 fn is_send<T: Send>() {}
1100 fn is_sync<T: Sync>() {}
1101
1102 is_send::<PyErr>();
1103 is_sync::<PyErr>();
1104
1105 is_send::<PyErrState>();
1106 is_sync::<PyErrState>();
1107 }
1108
1109 #[test]
1110 fn test_pyerr_matches() {
1111 Python::with_gil(|py| {
1112 let err = PyErr::new::<PyValueError, _>("foo");
1113 assert!(err.matches(py, PyValueError::type_object(py)).unwrap());
1114
1115 assert!(err
1116 .matches(
1117 py,
1118 (PyValueError::type_object(py), PyTypeError::type_object(py))
1119 )
1120 .unwrap());
1121
1122 assert!(!err.matches(py, PyTypeError::type_object(py)).unwrap());
1123
1124 let err: PyErr = PyErr::from_type(crate::types::PyString::type_object(py), "foo");
1126 assert!(err.matches(py, PyTypeError::type_object(py)).unwrap());
1127 })
1128 }
1129
1130 #[test]
1131 fn test_pyerr_cause() {
1132 Python::with_gil(|py| {
1133 let err = py
1134 .run(ffi::c_str!("raise Exception('banana')"), None, None)
1135 .expect_err("raising should have given us an error");
1136 assert!(err.cause(py).is_none());
1137
1138 let err = py
1139 .run(
1140 ffi::c_str!("raise Exception('banana') from Exception('apple')"),
1141 None,
1142 None,
1143 )
1144 .expect_err("raising should have given us an error");
1145 let cause = err
1146 .cause(py)
1147 .expect("raising from should have given us a cause");
1148 assert_eq!(cause.to_string(), "Exception: apple");
1149
1150 err.set_cause(py, None);
1151 assert!(err.cause(py).is_none());
1152
1153 let new_cause = exceptions::PyValueError::new_err("orange");
1154 err.set_cause(py, Some(new_cause));
1155 let cause = err
1156 .cause(py)
1157 .expect("set_cause should have given us a cause");
1158 assert_eq!(cause.to_string(), "ValueError: orange");
1159 });
1160 }
1161
1162 #[test]
1163 fn warnings() {
1164 use crate::types::any::PyAnyMethods;
1165 Python::with_gil(|py| {
1169 let cls = py.get_type::<exceptions::PyUserWarning>();
1170
1171 let warnings = py.import("warnings").unwrap();
1173 warnings.call_method0("resetwarnings").unwrap();
1174
1175 #[cfg(not(Py_GIL_DISABLED))]
1177 assert_warnings!(
1178 py,
1179 { PyErr::warn(py, &cls, ffi::c_str!("I am warning you"), 0).unwrap() },
1180 [(exceptions::PyUserWarning, "I am warning you")]
1181 );
1182
1183 warnings
1185 .call_method1("simplefilter", ("error", &cls))
1186 .unwrap();
1187 PyErr::warn(py, &cls, ffi::c_str!("I am warning you"), 0).unwrap_err();
1188
1189 warnings.call_method0("resetwarnings").unwrap();
1191 warnings
1192 .call_method1("filterwarnings", ("error", "", &cls, "pyo3test"))
1193 .unwrap();
1194
1195 #[cfg(not(Py_GIL_DISABLED))]
1197 assert_warnings!(
1198 py,
1199 { PyErr::warn(py, &cls, ffi::c_str!("I am warning you"), 0).unwrap() },
1200 [(exceptions::PyUserWarning, "I am warning you")]
1201 );
1202
1203 let err = PyErr::warn_explicit(
1204 py,
1205 &cls,
1206 ffi::c_str!("I am warning you"),
1207 ffi::c_str!("pyo3test.py"),
1208 427,
1209 None,
1210 None,
1211 )
1212 .unwrap_err();
1213 assert!(err
1214 .value(py)
1215 .getattr("args")
1216 .unwrap()
1217 .get_item(0)
1218 .unwrap()
1219 .eq("I am warning you")
1220 .unwrap());
1221
1222 warnings.call_method0("resetwarnings").unwrap();
1224 });
1225 }
1226}