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