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};
6#[cfg(feature = "gil-refs")]
7use crate::PyNativeType;
8use crate::{
9 exceptions::{self, PyBaseException},
10 ffi,
11};
12use crate::{Borrowed, IntoPy, Py, PyAny, PyObject, Python, ToPyObject};
13use std::borrow::Cow;
14use std::cell::UnsafeCell;
15use std::ffi::CString;
16
17mod err_state;
18mod impls;
19
20pub use err_state::PyErrArguments;
21use err_state::{PyErrState, PyErrStateLazyFnOutput, PyErrStateNormalized};
22
23pub struct PyErr {
33 state: UnsafeCell<Option<PyErrState>>,
39}
40
41#[cfg(feature = "nightly")]
43unsafe impl crate::marker::Ungil for PyErr {}
44unsafe impl Send for PyErr {}
45unsafe impl Sync for PyErr {}
46
47pub type PyResult<T> = Result<T, PyErr>;
49
50#[derive(Debug)]
52#[cfg(feature = "gil-refs")]
53pub struct PyDowncastError<'a> {
54 from: &'a PyAny,
55 to: Cow<'static, str>,
56}
57
58#[cfg(feature = "gil-refs")]
59impl<'a> PyDowncastError<'a> {
60 pub fn new(from: &'a PyAny, to: impl Into<Cow<'static, str>>) -> Self {
63 PyDowncastError {
64 from,
65 to: to.into(),
66 }
67 }
68
69 pub(crate) fn from_downcast_err(DowncastError { from, to }: DowncastError<'a, 'a>) -> Self {
72 #[allow(deprecated)]
73 let from = unsafe { from.py().from_borrowed_ptr(from.as_ptr()) };
74 Self { from, to }
75 }
76}
77
78#[derive(Debug)]
80pub struct DowncastError<'a, 'py> {
81 from: Borrowed<'a, 'py, PyAny>,
82 to: Cow<'static, str>,
83}
84
85impl<'a, 'py> DowncastError<'a, 'py> {
86 pub fn new(from: &'a Bound<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self {
89 DowncastError {
90 from: from.as_borrowed(),
91 to: to.into(),
92 }
93 }
94 #[cfg(not(feature = "gil-refs"))]
95 pub(crate) fn new_from_borrowed(
96 from: Borrowed<'a, 'py, PyAny>,
97 to: impl Into<Cow<'static, str>>,
98 ) -> Self {
99 DowncastError {
100 from,
101 to: to.into(),
102 }
103 }
104}
105
106#[derive(Debug)]
108pub struct DowncastIntoError<'py> {
109 from: Bound<'py, PyAny>,
110 to: Cow<'static, str>,
111}
112
113impl<'py> DowncastIntoError<'py> {
114 pub fn new(from: Bound<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self {
117 DowncastIntoError {
118 from,
119 to: to.into(),
120 }
121 }
122
123 pub fn into_inner(self) -> Bound<'py, PyAny> {
128 self.from
129 }
130}
131
132impl PyErr {
133 #[inline]
185 pub fn new<T, A>(args: A) -> PyErr
186 where
187 T: PyTypeInfo,
188 A: PyErrArguments + Send + Sync + 'static,
189 {
190 PyErr::from_state(PyErrState::Lazy(Box::new(move |py| {
191 PyErrStateLazyFnOutput {
192 ptype: T::type_object_bound(py).into(),
193 pvalue: args.arguments(py),
194 }
195 })))
196 }
197
198 #[cfg(feature = "gil-refs")]
200 #[deprecated(
201 since = "0.21.0",
202 note = "`PyErr::from_type` will be replaced by `PyErr::from_type_bound` in a future PyO3 version"
203 )]
204 pub fn from_type<A>(ty: &PyType, args: A) -> PyErr
205 where
206 A: PyErrArguments + Send + Sync + 'static,
207 {
208 PyErr::from_state(PyErrState::lazy(ty.into(), args))
209 }
210
211 pub fn from_type_bound<A>(ty: Bound<'_, PyType>, args: A) -> PyErr
222 where
223 A: PyErrArguments + Send + Sync + 'static,
224 {
225 PyErr::from_state(PyErrState::lazy(ty.unbind().into_any(), args))
226 }
227
228 #[cfg(feature = "gil-refs")]
230 #[deprecated(
231 since = "0.21.0",
232 note = "`PyErr::from_value` will be replaced by `PyErr::from_value_bound` in a future PyO3 version"
233 )]
234 pub fn from_value(obj: &PyAny) -> PyErr {
235 PyErr::from_value_bound(obj.as_borrowed().to_owned())
236 }
237
238 pub fn from_value_bound(obj: Bound<'_, PyAny>) -> PyErr {
272 let state = match obj.downcast_into::<PyBaseException>() {
273 Ok(obj) => PyErrState::normalized(obj),
274 Err(err) => {
275 let obj = err.into_inner();
278 let py = obj.py();
279 PyErrState::lazy(obj.into_py(py), py.None())
280 }
281 };
282
283 PyErr::from_state(state)
284 }
285
286 #[cfg(feature = "gil-refs")]
288 #[deprecated(
289 since = "0.21.0",
290 note = "`PyErr::get_type` will be replaced by `PyErr::get_type_bound` in a future PyO3 version"
291 )]
292 pub fn get_type<'py>(&'py self, py: Python<'py>) -> &'py PyType {
293 self.get_type_bound(py).into_gil_ref()
294 }
295
296 pub fn get_type_bound<'py>(&self, py: Python<'py>) -> Bound<'py, PyType> {
308 self.normalized(py).ptype(py)
309 }
310
311 #[cfg(feature = "gil-refs")]
313 #[deprecated(
314 since = "0.21.0",
315 note = "`PyErr::value` will be replaced by `PyErr::value_bound` in a future PyO3 version"
316 )]
317 pub fn value<'py>(&'py self, py: Python<'py>) -> &'py PyBaseException {
318 self.value_bound(py).as_gil_ref()
319 }
320
321 pub fn value_bound<'py>(&self, py: Python<'py>) -> &Bound<'py, PyBaseException> {
335 self.normalized(py).pvalue.bind(py)
336 }
337
338 pub fn into_value(self, py: Python<'_>) -> Py<PyBaseException> {
340 let normalized = self.normalized(py);
344 let exc = normalized.pvalue.clone_ref(py);
345 if let Some(tb) = normalized.ptraceback(py) {
346 unsafe {
347 ffi::PyException_SetTraceback(exc.as_ptr(), tb.as_ptr());
348 }
349 }
350 exc
351 }
352
353 #[cfg(feature = "gil-refs")]
355 #[deprecated(
356 since = "0.21.0",
357 note = "`PyErr::traceback` will be replaced by `PyErr::traceback_bound` in a future PyO3 version"
358 )]
359 pub fn traceback<'py>(&'py self, py: Python<'py>) -> Option<&'py PyTraceback> {
360 self.normalized(py).ptraceback(py).map(|b| b.into_gil_ref())
361 }
362
363 pub fn traceback_bound<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTraceback>> {
375 self.normalized(py).ptraceback(py)
376 }
377
378 #[inline]
380 pub fn occurred(_: Python<'_>) -> bool {
381 unsafe { !ffi::PyErr_Occurred().is_null() }
382 }
383
384 pub fn take(py: Python<'_>) -> Option<PyErr> {
394 Self::_take(py)
395 }
396
397 #[cfg(not(Py_3_12))]
398 fn _take(py: Python<'_>) -> Option<PyErr> {
399 let (ptype, pvalue, ptraceback) = unsafe {
400 let mut ptype: *mut ffi::PyObject = std::ptr::null_mut();
401 let mut pvalue: *mut ffi::PyObject = std::ptr::null_mut();
402 let mut ptraceback: *mut ffi::PyObject = std::ptr::null_mut();
403 ffi::PyErr_Fetch(&mut ptype, &mut pvalue, &mut ptraceback);
404
405 let ptype = PyObject::from_owned_ptr_or_opt(py, ptype);
407 let pvalue = PyObject::from_owned_ptr_or_opt(py, pvalue);
408 let ptraceback = PyObject::from_owned_ptr_or_opt(py, ptraceback);
409
410 let ptype = match ptype {
413 Some(ptype) => ptype,
414 None => {
415 debug_assert!(
416 pvalue.is_none(),
417 "Exception type was null but value was not null"
418 );
419 debug_assert!(
420 ptraceback.is_none(),
421 "Exception type was null but traceback was not null"
422 );
423 return None;
424 }
425 };
426
427 (ptype, pvalue, ptraceback)
428 };
429
430 if ptype.as_ptr() == PanicException::type_object_raw(py).cast() {
431 let msg = pvalue
432 .as_ref()
433 .and_then(|obj| obj.bind(py).str().ok())
434 .map(|py_str| py_str.to_string_lossy().into())
435 .unwrap_or_else(|| String::from("Unwrapped panic from Python code"));
436
437 let state = PyErrState::FfiTuple {
438 ptype,
439 pvalue,
440 ptraceback,
441 };
442 Self::print_panic_and_unwind(py, state, msg)
443 }
444
445 Some(PyErr::from_state(PyErrState::FfiTuple {
446 ptype,
447 pvalue,
448 ptraceback,
449 }))
450 }
451
452 #[cfg(Py_3_12)]
453 fn _take(py: Python<'_>) -> Option<PyErr> {
454 let state = PyErrStateNormalized::take(py)?;
455 let pvalue = state.pvalue.bind(py);
456 if pvalue.get_type().as_ptr() == PanicException::type_object_raw(py).cast() {
457 let msg: String = pvalue
458 .str()
459 .map(|py_str| py_str.to_string_lossy().into())
460 .unwrap_or_else(|_| String::from("Unwrapped panic from Python code"));
461 Self::print_panic_and_unwind(py, PyErrState::Normalized(state), msg)
462 }
463
464 Some(PyErr::from_state(PyErrState::Normalized(state)))
465 }
466
467 fn print_panic_and_unwind(py: Python<'_>, state: PyErrState, msg: String) -> ! {
468 eprintln!("--- PyO3 is resuming a panic after fetching a PanicException from Python. ---");
469 eprintln!("Python stack trace below:");
470
471 state.restore(py);
472
473 unsafe {
474 ffi::PyErr_PrintEx(0);
475 }
476
477 std::panic::resume_unwind(Box::new(msg))
478 }
479
480 #[cfg_attr(debug_assertions, track_caller)]
492 #[inline]
493 pub fn fetch(py: Python<'_>) -> PyErr {
494 const FAILED_TO_FETCH: &str = "attempted to fetch exception but none was set";
495 match PyErr::take(py) {
496 Some(err) => err,
497 #[cfg(debug_assertions)]
498 None => panic!("{}", FAILED_TO_FETCH),
499 #[cfg(not(debug_assertions))]
500 None => exceptions::PySystemError::new_err(FAILED_TO_FETCH),
501 }
502 }
503
504 #[cfg(feature = "gil-refs")]
506 #[deprecated(
507 since = "0.21.0",
508 note = "`PyErr::new_type` will be replaced by `PyErr::new_type_bound` in a future PyO3 version"
509 )]
510 pub fn new_type(
511 py: Python<'_>,
512 name: &str,
513 doc: Option<&str>,
514 base: Option<&PyType>,
515 dict: Option<PyObject>,
516 ) -> PyResult<Py<PyType>> {
517 Self::new_type_bound(
518 py,
519 name,
520 doc,
521 base.map(PyNativeType::as_borrowed).as_deref(),
522 dict,
523 )
524 }
525
526 pub fn new_type_bound<'py>(
541 py: Python<'py>,
542 name: &str,
543 doc: Option<&str>,
544 base: Option<&Bound<'py, PyType>>,
545 dict: Option<PyObject>,
546 ) -> PyResult<Py<PyType>> {
547 let base: *mut ffi::PyObject = match base {
548 None => std::ptr::null_mut(),
549 Some(obj) => obj.as_ptr(),
550 };
551
552 let dict: *mut ffi::PyObject = match dict {
553 None => std::ptr::null_mut(),
554 Some(obj) => obj.as_ptr(),
555 };
556
557 let null_terminated_name =
558 CString::new(name).expect("Failed to initialize nul terminated exception name");
559
560 let null_terminated_doc =
561 doc.map(|d| CString::new(d).expect("Failed to initialize nul terminated docstring"));
562
563 let null_terminated_doc_ptr = match null_terminated_doc.as_ref() {
564 Some(c) => c.as_ptr(),
565 None => std::ptr::null(),
566 };
567
568 let ptr = unsafe {
569 ffi::PyErr_NewExceptionWithDoc(
570 null_terminated_name.as_ptr(),
571 null_terminated_doc_ptr,
572 base,
573 dict,
574 )
575 };
576
577 unsafe { Py::from_owned_ptr_or_err(py, ptr) }
578 }
579
580 pub fn display(&self, py: Python<'_>) {
582 #[cfg(Py_3_12)]
583 unsafe {
584 ffi::PyErr_DisplayException(self.value_bound(py).as_ptr())
585 }
586
587 #[cfg(not(Py_3_12))]
588 unsafe {
589 let traceback = self.traceback_bound(py);
594 let type_bound = self.get_type_bound(py);
595 ffi::PyErr_Display(
596 type_bound.as_ptr(),
597 self.value_bound(py).as_ptr(),
598 traceback
599 .as_ref()
600 .map_or(std::ptr::null_mut(), |traceback| traceback.as_ptr()),
601 )
602 }
603 }
604
605 pub fn print(&self, py: Python<'_>) {
607 self.clone_ref(py).restore(py);
608 unsafe { ffi::PyErr_PrintEx(0) }
609 }
610
611 pub fn print_and_set_sys_last_vars(&self, py: Python<'_>) {
615 self.clone_ref(py).restore(py);
616 unsafe { ffi::PyErr_PrintEx(1) }
617 }
618
619 pub fn matches<T>(&self, py: Python<'_>, exc: T) -> bool
624 where
625 T: ToPyObject,
626 {
627 self.is_instance_bound(py, exc.to_object(py).bind(py))
628 }
629
630 #[cfg(feature = "gil-refs")]
632 #[deprecated(
633 since = "0.21.0",
634 note = "`PyErr::is_instance` will be replaced by `PyErr::is_instance_bound` in a future PyO3 version"
635 )]
636 #[inline]
637 pub fn is_instance(&self, py: Python<'_>, ty: &PyAny) -> bool {
638 self.is_instance_bound(py, &ty.as_borrowed())
639 }
640
641 #[inline]
643 pub fn is_instance_bound(&self, py: Python<'_>, ty: &Bound<'_, PyAny>) -> bool {
644 let type_bound = self.get_type_bound(py);
645 (unsafe { ffi::PyErr_GivenExceptionMatches(type_bound.as_ptr(), ty.as_ptr()) }) != 0
646 }
647
648 #[inline]
650 pub fn is_instance_of<T>(&self, py: Python<'_>) -> bool
651 where
652 T: PyTypeInfo,
653 {
654 self.is_instance_bound(py, &T::type_object_bound(py))
655 }
656
657 #[inline]
660 pub fn restore(self, py: Python<'_>) {
661 self.state
662 .into_inner()
663 .expect("PyErr state should never be invalid outside of normalization")
664 .restore(py)
665 }
666
667 #[cfg(feature = "gil-refs")]
669 #[deprecated(
670 since = "0.21.0",
671 note = "`PyErr::write_unraisable` will be replaced by `PyErr::write_unraisable_bound` in a future PyO3 version"
672 )]
673 #[inline]
674 pub fn write_unraisable(self, py: Python<'_>, obj: Option<&PyAny>) {
675 self.write_unraisable_bound(py, obj.map(PyAny::as_borrowed).as_deref())
676 }
677
678 #[inline]
707 pub fn write_unraisable_bound(self, py: Python<'_>, obj: Option<&Bound<'_, PyAny>>) {
708 self.restore(py);
709 unsafe { ffi::PyErr_WriteUnraisable(obj.map_or(std::ptr::null_mut(), Bound::as_ptr)) }
710 }
711
712 #[cfg(feature = "gil-refs")]
714 #[deprecated(
715 since = "0.21.0",
716 note = "`PyErr::warn` will be replaced by `PyErr::warn_bound` in a future PyO3 version"
717 )]
718 pub fn warn(py: Python<'_>, category: &PyAny, message: &str, stacklevel: i32) -> PyResult<()> {
719 Self::warn_bound(py, &category.as_borrowed(), message, stacklevel)
720 }
721
722 pub fn warn_bound<'py>(
744 py: Python<'py>,
745 category: &Bound<'py, PyAny>,
746 message: &str,
747 stacklevel: i32,
748 ) -> PyResult<()> {
749 let message = CString::new(message)?;
750 error_on_minusone(py, unsafe {
751 ffi::PyErr_WarnEx(
752 category.as_ptr(),
753 message.as_ptr(),
754 stacklevel as ffi::Py_ssize_t,
755 )
756 })
757 }
758
759 #[cfg(feature = "gil-refs")]
761 #[deprecated(
762 since = "0.21.0",
763 note = "`PyErr::warn_explicit` will be replaced by `PyErr::warn_explicit_bound` in a future PyO3 version"
764 )]
765 pub fn warn_explicit(
766 py: Python<'_>,
767 category: &PyAny,
768 message: &str,
769 filename: &str,
770 lineno: i32,
771 module: Option<&str>,
772 registry: Option<&PyAny>,
773 ) -> PyResult<()> {
774 Self::warn_explicit_bound(
775 py,
776 &category.as_borrowed(),
777 message,
778 filename,
779 lineno,
780 module,
781 registry.map(PyNativeType::as_borrowed).as_deref(),
782 )
783 }
784
785 pub fn warn_explicit_bound<'py>(
794 py: Python<'py>,
795 category: &Bound<'py, PyAny>,
796 message: &str,
797 filename: &str,
798 lineno: i32,
799 module: Option<&str>,
800 registry: Option<&Bound<'py, PyAny>>,
801 ) -> PyResult<()> {
802 let message = CString::new(message)?;
803 let filename = CString::new(filename)?;
804 let module = module.map(CString::new).transpose()?;
805 let module_ptr = match module {
806 None => std::ptr::null_mut(),
807 Some(s) => s.as_ptr(),
808 };
809 let registry: *mut ffi::PyObject = match registry {
810 None => std::ptr::null_mut(),
811 Some(obj) => obj.as_ptr(),
812 };
813 error_on_minusone(py, unsafe {
814 ffi::PyErr_WarnExplicit(
815 category.as_ptr(),
816 message.as_ptr(),
817 filename.as_ptr(),
818 lineno,
819 module_ptr,
820 registry,
821 )
822 })
823 }
824
825 #[inline]
842 pub fn clone_ref(&self, py: Python<'_>) -> PyErr {
843 PyErr::from_state(PyErrState::Normalized(self.normalized(py).clone_ref(py)))
844 }
845
846 pub fn cause(&self, py: Python<'_>) -> Option<PyErr> {
849 use crate::ffi_ptr_ext::FfiPtrExt;
850 let obj = unsafe {
851 ffi::PyException_GetCause(self.value_bound(py).as_ptr()).assume_owned_or_opt(py)
852 };
853 #[cfg(GraalPy)]
855 if let Some(cause) = &obj {
856 if cause.is_none() {
857 return None;
858 }
859 }
860 obj.map(Self::from_value_bound)
861 }
862
863 pub fn set_cause(&self, py: Python<'_>, cause: Option<Self>) {
865 let value = self.value_bound(py);
866 let cause = cause.map(|err| err.into_value(py));
867 unsafe {
868 ffi::PyException_SetCause(
870 value.as_ptr(),
871 cause.map_or(std::ptr::null_mut(), Py::into_ptr),
872 );
873 }
874 }
875
876 #[inline]
877 fn from_state(state: PyErrState) -> PyErr {
878 PyErr {
879 state: UnsafeCell::new(Some(state)),
880 }
881 }
882
883 #[inline]
884 fn normalized(&self, py: Python<'_>) -> &PyErrStateNormalized {
885 if let Some(PyErrState::Normalized(n)) = unsafe {
886 &*self.state.get()
888 } {
889 return n;
890 }
891
892 self.make_normalized(py)
893 }
894
895 #[cold]
896 fn make_normalized(&self, py: Python<'_>) -> &PyErrStateNormalized {
897 let state = unsafe {
904 (*self.state.get())
905 .take()
906 .expect("Cannot normalize a PyErr while already normalizing it.")
907 };
908
909 unsafe {
910 let self_state = &mut *self.state.get();
911 *self_state = Some(PyErrState::Normalized(state.normalize(py)));
912 match self_state {
913 Some(PyErrState::Normalized(n)) => n,
914 _ => unreachable!(),
915 }
916 }
917 }
918}
919
920impl std::fmt::Debug for PyErr {
921 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
922 Python::with_gil(|py| {
923 f.debug_struct("PyErr")
924 .field("type", &self.get_type_bound(py))
925 .field("value", self.value_bound(py))
926 .field("traceback", &self.traceback_bound(py))
927 .finish()
928 })
929 }
930}
931
932impl std::fmt::Display for PyErr {
933 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
934 Python::with_gil(|py| {
935 let value = self.value_bound(py);
936 let type_name = value.get_type().qualname().map_err(|_| std::fmt::Error)?;
937 write!(f, "{}", type_name)?;
938 if let Ok(s) = value.str() {
939 write!(f, ": {}", &s.to_string_lossy())
940 } else {
941 write!(f, ": <exception str() failed>")
942 }
943 })
944 }
945}
946
947impl std::error::Error for PyErr {}
948
949impl IntoPy<PyObject> for PyErr {
950 fn into_py(self, py: Python<'_>) -> PyObject {
951 self.into_value(py).into()
952 }
953}
954
955impl ToPyObject for PyErr {
956 fn to_object(&self, py: Python<'_>) -> PyObject {
957 self.clone_ref(py).into_py(py)
958 }
959}
960
961impl<'a> IntoPy<PyObject> for &'a PyErr {
962 fn into_py(self, py: Python<'_>) -> PyObject {
963 self.clone_ref(py).into_py(py)
964 }
965}
966
967struct PyDowncastErrorArguments {
968 from: Py<PyType>,
969 to: Cow<'static, str>,
970}
971
972impl PyErrArguments for PyDowncastErrorArguments {
973 fn arguments(self, py: Python<'_>) -> PyObject {
974 const FAILED_TO_EXTRACT: Cow<'_, str> = Cow::Borrowed("<failed to extract type name>");
975 let from = self.from.bind(py).qualname();
976 let from = match &from {
977 Ok(qn) => qn.to_cow().unwrap_or(FAILED_TO_EXTRACT),
978 Err(_) => FAILED_TO_EXTRACT,
979 };
980 format!("'{}' object cannot be converted to '{}'", from, self.to).to_object(py)
981 }
982}
983
984pub trait ToPyErr {}
991
992impl<'py, T> std::convert::From<Bound<'py, T>> for PyErr
993where
994 T: ToPyErr,
995{
996 #[inline]
997 fn from(err: Bound<'py, T>) -> PyErr {
998 PyErr::from_value_bound(err.into_any())
999 }
1000}
1001
1002#[cfg(feature = "gil-refs")]
1004impl<'a> std::convert::From<PyDowncastError<'a>> for PyErr {
1005 fn from(err: PyDowncastError<'_>) -> PyErr {
1006 let args = PyDowncastErrorArguments {
1007 from: err.from.get_type().into(),
1008 to: err.to,
1009 };
1010
1011 exceptions::PyTypeError::new_err(args)
1012 }
1013}
1014
1015#[cfg(feature = "gil-refs")]
1016impl<'a> std::error::Error for PyDowncastError<'a> {}
1017
1018#[cfg(feature = "gil-refs")]
1019impl<'a> std::fmt::Display for PyDowncastError<'a> {
1020 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
1021 display_downcast_error(f, &self.from.as_borrowed(), &self.to)
1022 }
1023}
1024
1025impl std::convert::From<DowncastError<'_, '_>> for PyErr {
1027 fn from(err: DowncastError<'_, '_>) -> PyErr {
1028 let args = PyDowncastErrorArguments {
1029 from: err.from.get_type().into(),
1030 to: err.to,
1031 };
1032
1033 exceptions::PyTypeError::new_err(args)
1034 }
1035}
1036
1037impl std::error::Error for DowncastError<'_, '_> {}
1038
1039impl std::fmt::Display for DowncastError<'_, '_> {
1040 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
1041 display_downcast_error(f, &self.from, &self.to)
1042 }
1043}
1044
1045impl std::convert::From<DowncastIntoError<'_>> for PyErr {
1047 fn from(err: DowncastIntoError<'_>) -> PyErr {
1048 let args = PyDowncastErrorArguments {
1049 from: err.from.get_type().into(),
1050 to: err.to,
1051 };
1052
1053 exceptions::PyTypeError::new_err(args)
1054 }
1055}
1056
1057impl std::error::Error for DowncastIntoError<'_> {}
1058
1059impl std::fmt::Display for DowncastIntoError<'_> {
1060 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
1061 display_downcast_error(f, &self.from, &self.to)
1062 }
1063}
1064
1065fn display_downcast_error(
1066 f: &mut std::fmt::Formatter<'_>,
1067 from: &Bound<'_, PyAny>,
1068 to: &str,
1069) -> std::fmt::Result {
1070 write!(
1071 f,
1072 "'{}' object cannot be converted to '{}'",
1073 from.get_type().qualname().map_err(|_| std::fmt::Error)?,
1074 to
1075 )
1076}
1077
1078#[track_caller]
1079pub fn panic_after_error(_py: Python<'_>) -> ! {
1080 unsafe {
1081 ffi::PyErr_Print();
1082 }
1083 panic!("Python API call failed");
1084}
1085
1086#[inline]
1088pub(crate) fn error_on_minusone<T: SignedInteger>(py: Python<'_>, result: T) -> PyResult<()> {
1089 if result != T::MINUS_ONE {
1090 Ok(())
1091 } else {
1092 Err(PyErr::fetch(py))
1093 }
1094}
1095
1096pub(crate) trait SignedInteger: Eq {
1097 const MINUS_ONE: Self;
1098}
1099
1100macro_rules! impl_signed_integer {
1101 ($t:ty) => {
1102 impl SignedInteger for $t {
1103 const MINUS_ONE: Self = -1;
1104 }
1105 };
1106}
1107
1108impl_signed_integer!(i8);
1109impl_signed_integer!(i16);
1110impl_signed_integer!(i32);
1111impl_signed_integer!(i64);
1112impl_signed_integer!(i128);
1113impl_signed_integer!(isize);
1114
1115#[cfg(test)]
1116mod tests {
1117 use super::PyErrState;
1118 use crate::exceptions::{self, PyTypeError, PyValueError};
1119 use crate::{PyErr, PyTypeInfo, Python};
1120
1121 #[test]
1122 fn no_error() {
1123 assert!(Python::with_gil(PyErr::take).is_none());
1124 }
1125
1126 #[test]
1127 fn set_valueerror() {
1128 Python::with_gil(|py| {
1129 let err: PyErr = exceptions::PyValueError::new_err("some exception message");
1130 assert!(err.is_instance_of::<exceptions::PyValueError>(py));
1131 err.restore(py);
1132 assert!(PyErr::occurred(py));
1133 let err = PyErr::fetch(py);
1134 assert!(err.is_instance_of::<exceptions::PyValueError>(py));
1135 assert_eq!(err.to_string(), "ValueError: some exception message");
1136 })
1137 }
1138
1139 #[test]
1140 fn invalid_error_type() {
1141 Python::with_gil(|py| {
1142 let err: PyErr = PyErr::new::<crate::types::PyString, _>(());
1143 assert!(err.is_instance_of::<exceptions::PyTypeError>(py));
1144 err.restore(py);
1145 let err = PyErr::fetch(py);
1146
1147 assert!(err.is_instance_of::<exceptions::PyTypeError>(py));
1148 assert_eq!(
1149 err.to_string(),
1150 "TypeError: exceptions must derive from BaseException"
1151 );
1152 })
1153 }
1154
1155 #[test]
1156 fn set_typeerror() {
1157 Python::with_gil(|py| {
1158 let err: PyErr = exceptions::PyTypeError::new_err(());
1159 err.restore(py);
1160 assert!(PyErr::occurred(py));
1161 drop(PyErr::fetch(py));
1162 });
1163 }
1164
1165 #[test]
1166 #[should_panic(expected = "new panic")]
1167 fn fetching_panic_exception_resumes_unwind() {
1168 use crate::panic::PanicException;
1169
1170 Python::with_gil(|py| {
1171 let err: PyErr = PanicException::new_err("new panic");
1172 err.restore(py);
1173 assert!(PyErr::occurred(py));
1174
1175 let _ = PyErr::fetch(py);
1177 });
1178 }
1179
1180 #[test]
1181 #[should_panic(expected = "new panic")]
1182 #[cfg(not(Py_3_12))]
1183 fn fetching_normalized_panic_exception_resumes_unwind() {
1184 use crate::panic::PanicException;
1185
1186 Python::with_gil(|py| {
1187 let err: PyErr = PanicException::new_err("new panic");
1188 let _ = err.normalized(py);
1191 err.restore(py);
1192 assert!(PyErr::occurred(py));
1193
1194 let _ = PyErr::fetch(py);
1196 });
1197 }
1198
1199 #[test]
1200 fn err_debug() {
1201 Python::with_gil(|py| {
1209 let err = py
1210 .run_bound("raise Exception('banana')", None, None)
1211 .expect_err("raising should have given us an error");
1212
1213 let debug_str = format!("{:?}", err);
1214 assert!(debug_str.starts_with("PyErr { "));
1215 assert!(debug_str.ends_with(" }"));
1216
1217 let mut fields = debug_str["PyErr { ".len()..debug_str.len() - 2].split(", ");
1219
1220 assert_eq!(fields.next().unwrap(), "type: <class 'Exception'>");
1221 assert_eq!(fields.next().unwrap(), "value: Exception('banana')");
1222
1223 let traceback = fields.next().unwrap();
1224 assert!(traceback.starts_with("traceback: Some(<traceback object at 0x"));
1225 assert!(traceback.ends_with(">)"));
1226
1227 assert!(fields.next().is_none());
1228 });
1229 }
1230
1231 #[test]
1232 fn err_display() {
1233 Python::with_gil(|py| {
1234 let err = py
1235 .run_bound("raise Exception('banana')", None, None)
1236 .expect_err("raising should have given us an error");
1237 assert_eq!(err.to_string(), "Exception: banana");
1238 });
1239 }
1240
1241 #[test]
1242 fn test_pyerr_send_sync() {
1243 fn is_send<T: Send>() {}
1244 fn is_sync<T: Sync>() {}
1245
1246 is_send::<PyErr>();
1247 is_sync::<PyErr>();
1248
1249 is_send::<PyErrState>();
1250 is_sync::<PyErrState>();
1251 }
1252
1253 #[test]
1254 fn test_pyerr_matches() {
1255 Python::with_gil(|py| {
1256 let err = PyErr::new::<PyValueError, _>("foo");
1257 assert!(err.matches(py, PyValueError::type_object_bound(py)));
1258
1259 assert!(err.matches(
1260 py,
1261 (
1262 PyValueError::type_object_bound(py),
1263 PyTypeError::type_object_bound(py)
1264 )
1265 ));
1266
1267 assert!(!err.matches(py, PyTypeError::type_object_bound(py)));
1268
1269 let err: PyErr =
1271 PyErr::from_type_bound(crate::types::PyString::type_object_bound(py), "foo");
1272 assert!(err.matches(py, PyTypeError::type_object_bound(py)));
1273 })
1274 }
1275
1276 #[test]
1277 fn test_pyerr_cause() {
1278 Python::with_gil(|py| {
1279 let err = py
1280 .run_bound("raise Exception('banana')", None, None)
1281 .expect_err("raising should have given us an error");
1282 assert!(err.cause(py).is_none());
1283
1284 let err = py
1285 .run_bound(
1286 "raise Exception('banana') from Exception('apple')",
1287 None,
1288 None,
1289 )
1290 .expect_err("raising should have given us an error");
1291 let cause = err
1292 .cause(py)
1293 .expect("raising from should have given us a cause");
1294 assert_eq!(cause.to_string(), "Exception: apple");
1295
1296 err.set_cause(py, None);
1297 assert!(err.cause(py).is_none());
1298
1299 let new_cause = exceptions::PyValueError::new_err("orange");
1300 err.set_cause(py, Some(new_cause));
1301 let cause = err
1302 .cause(py)
1303 .expect("set_cause should have given us a cause");
1304 assert_eq!(cause.to_string(), "ValueError: orange");
1305 });
1306 }
1307
1308 #[test]
1309 fn warnings() {
1310 use crate::types::any::PyAnyMethods;
1311 Python::with_gil(|py| {
1315 let cls = py.get_type_bound::<exceptions::PyUserWarning>();
1316
1317 let warnings = py.import_bound("warnings").unwrap();
1319 warnings.call_method0("resetwarnings").unwrap();
1320
1321 assert_warnings!(
1323 py,
1324 { PyErr::warn_bound(py, &cls, "I am warning you", 0).unwrap() },
1325 [(exceptions::PyUserWarning, "I am warning you")]
1326 );
1327
1328 warnings
1330 .call_method1("simplefilter", ("error", &cls))
1331 .unwrap();
1332 PyErr::warn_bound(py, &cls, "I am warning you", 0).unwrap_err();
1333
1334 warnings.call_method0("resetwarnings").unwrap();
1336 warnings
1337 .call_method1("filterwarnings", ("error", "", &cls, "pyo3test"))
1338 .unwrap();
1339
1340 assert_warnings!(
1342 py,
1343 { PyErr::warn_bound(py, &cls, "I am warning you", 0).unwrap() },
1344 [(exceptions::PyUserWarning, "I am warning you")]
1345 );
1346
1347 let err = PyErr::warn_explicit_bound(
1348 py,
1349 &cls,
1350 "I am warning you",
1351 "pyo3test.py",
1352 427,
1353 None,
1354 None,
1355 )
1356 .unwrap_err();
1357 assert!(err
1358 .value_bound(py)
1359 .getattr("args")
1360 .unwrap()
1361 .get_item(0)
1362 .unwrap()
1363 .eq("I am warning you")
1364 .unwrap());
1365
1366 warnings.call_method0("resetwarnings").unwrap();
1368 });
1369 }
1370}