pyo3/err/mod.rs
1use crate::instance::Bound;
2#[cfg(Py_3_11)]
3use crate::intern;
4use crate::panic::PanicException;
5use crate::type_object::PyTypeInfo;
6use crate::types::any::PyAnyMethods;
7#[cfg(Py_3_11)]
8use crate::types::PyString;
9use crate::types::{
10 string::PyStringMethods, traceback::PyTracebackMethods, typeobject::PyTypeMethods, PyTraceback,
11 PyType,
12};
13use crate::{exceptions::PyBaseException, ffi};
14use crate::{BoundObject, Py, PyAny, Python};
15use std::ffi::CStr;
16
17mod cast_error;
18mod downcast_error;
19mod err_state;
20mod impls;
21
22use crate::conversion::IntoPyObject;
23use err_state::{PyErrState, PyErrStateLazyFnOutput, PyErrStateNormalized};
24use std::convert::Infallible;
25use std::ptr;
26
27pub use cast_error::{CastError, CastIntoError};
28#[allow(deprecated)]
29pub use downcast_error::{DowncastError, DowncastIntoError};
30
31/// Represents a Python exception.
32///
33/// To avoid needing access to [`Python`] in `Into` conversions to create `PyErr` (thus improving
34/// compatibility with `?` and other Rust errors) this type supports creating exceptions instances
35/// in a lazy fashion, where the full Python object for the exception is created only when needed.
36///
37/// Accessing the contained exception in any way, such as with [`value`](PyErr::value),
38/// [`get_type`](PyErr::get_type), or [`is_instance`](PyErr::is_instance)
39/// will create the full exception object if it was not already created.
40pub struct PyErr {
41 state: PyErrState,
42}
43
44// The inner value is only accessed through ways that require proving the gil is held
45#[cfg(feature = "nightly")]
46unsafe impl crate::marker::Ungil for PyErr {}
47
48/// Represents the result of a Python call.
49pub type PyResult<T> = Result<T, PyErr>;
50
51/// Helper conversion trait that allows to use custom arguments for lazy exception construction.
52pub trait PyErrArguments: Send + Sync {
53 /// Arguments for exception
54 fn arguments(self, py: Python<'_>) -> Py<PyAny>;
55}
56
57impl<T> PyErrArguments for T
58where
59 T: for<'py> IntoPyObject<'py> + Send + Sync,
60{
61 fn arguments(self, py: Python<'_>) -> Py<PyAny> {
62 // FIXME: `arguments` should become fallible
63 match self.into_pyobject(py) {
64 Ok(obj) => obj.into_any().unbind(),
65 Err(e) => panic!("Converting PyErr arguments failed: {}", e.into()),
66 }
67 }
68}
69
70impl PyErr {
71 /// Creates a new PyErr of type `T`.
72 ///
73 /// `args` can be:
74 /// * a tuple: the exception instance will be created using the equivalent to the Python
75 /// expression `T(*tuple)`
76 /// * any other value: the exception instance will be created using the equivalent to the Python
77 /// expression `T(value)`
78 ///
79 /// This exception instance will be initialized lazily. This avoids the need for the Python GIL
80 /// to be held, but requires `args` to be `Send` and `Sync`. If `args` is not `Send` or `Sync`,
81 /// consider using [`PyErr::from_value`] instead.
82 ///
83 /// If `T` does not inherit from `BaseException`, then a `TypeError` will be returned.
84 ///
85 /// If calling T's constructor with `args` raises an exception, that exception will be returned.
86 ///
87 /// # Examples
88 ///
89 /// ```
90 /// use pyo3::prelude::*;
91 /// use pyo3::exceptions::PyTypeError;
92 ///
93 /// #[pyfunction]
94 /// fn always_throws() -> PyResult<()> {
95 /// Err(PyErr::new::<PyTypeError, _>("Error message"))
96 /// }
97 /// #
98 /// # Python::attach(|py| {
99 /// # let fun = pyo3::wrap_pyfunction!(always_throws, py).unwrap();
100 /// # let err = fun.call0().expect_err("called a function that should always return an error but the return value was Ok");
101 /// # assert!(err.is_instance_of::<PyTypeError>(py))
102 /// # });
103 /// ```
104 ///
105 /// In most cases, you can use a concrete exception's constructor instead:
106 ///
107 /// ```
108 /// use pyo3::prelude::*;
109 /// use pyo3::exceptions::PyTypeError;
110 ///
111 /// #[pyfunction]
112 /// fn always_throws() -> PyResult<()> {
113 /// Err(PyTypeError::new_err("Error message"))
114 /// }
115 /// #
116 /// # Python::attach(|py| {
117 /// # let fun = pyo3::wrap_pyfunction!(always_throws, py).unwrap();
118 /// # let err = fun.call0().expect_err("called a function that should always return an error but the return value was Ok");
119 /// # assert!(err.is_instance_of::<PyTypeError>(py))
120 /// # });
121 /// ```
122 #[inline]
123 pub fn new<T, A>(args: A) -> PyErr
124 where
125 T: PyTypeInfo,
126 A: PyErrArguments + Send + Sync + 'static,
127 {
128 PyErr::from_state(PyErrState::lazy(Box::new(move |py| {
129 PyErrStateLazyFnOutput {
130 ptype: T::type_object(py).into(),
131 pvalue: args.arguments(py),
132 }
133 })))
134 }
135
136 /// Constructs a new PyErr from the given Python type and arguments.
137 ///
138 /// `ty` is the exception type; usually one of the standard exceptions
139 /// like `exceptions::PyRuntimeError`.
140 ///
141 /// `args` is either a tuple or a single value, with the same meaning as in [`PyErr::new`].
142 ///
143 /// If `ty` does not inherit from `BaseException`, then a `TypeError` will be returned.
144 ///
145 /// If calling `ty` with `args` raises an exception, that exception will be returned.
146 pub fn from_type<A>(ty: Bound<'_, PyType>, args: A) -> PyErr
147 where
148 A: PyErrArguments + Send + Sync + 'static,
149 {
150 PyErr::from_state(PyErrState::lazy_arguments(ty.unbind().into_any(), args))
151 }
152
153 /// Creates a new PyErr.
154 ///
155 /// If `obj` is a Python exception object, the PyErr will contain that object.
156 ///
157 /// If `obj` is a Python exception type object, this is equivalent to `PyErr::from_type(obj, ())`.
158 ///
159 /// Otherwise, a `TypeError` is created.
160 ///
161 /// # Examples
162 /// ```rust
163 /// use pyo3::prelude::*;
164 /// use pyo3::PyTypeInfo;
165 /// use pyo3::exceptions::PyTypeError;
166 /// use pyo3::types::PyString;
167 ///
168 /// Python::attach(|py| {
169 /// // Case #1: Exception object
170 /// let err = PyErr::from_value(PyTypeError::new_err("some type error")
171 /// .value(py).clone().into_any());
172 /// assert_eq!(err.to_string(), "TypeError: some type error");
173 ///
174 /// // Case #2: Exception type
175 /// let err = PyErr::from_value(PyTypeError::type_object(py).into_any());
176 /// assert_eq!(err.to_string(), "TypeError: ");
177 ///
178 /// // Case #3: Invalid exception value
179 /// let err = PyErr::from_value(PyString::new(py, "foo").into_any());
180 /// assert_eq!(
181 /// err.to_string(),
182 /// "TypeError: exceptions must derive from BaseException"
183 /// );
184 /// });
185 /// ```
186 pub fn from_value(obj: Bound<'_, PyAny>) -> PyErr {
187 let state = match obj.cast_into::<PyBaseException>() {
188 Ok(obj) => PyErrState::normalized(PyErrStateNormalized::new(obj)),
189 Err(err) => {
190 // Assume obj is Type[Exception]; let later normalization handle if this
191 // is not the case
192 let obj = err.into_inner();
193 let py = obj.py();
194 PyErrState::lazy_arguments(obj.unbind(), py.None())
195 }
196 };
197
198 PyErr::from_state(state)
199 }
200
201 /// Returns the type of this exception.
202 ///
203 /// # Examples
204 /// ```rust
205 /// use pyo3::{prelude::*, exceptions::PyTypeError, types::PyType};
206 ///
207 /// Python::attach(|py| {
208 /// let err: PyErr = PyTypeError::new_err(("some type error",));
209 /// assert!(err.get_type(py).is(&PyType::new::<PyTypeError>(py)));
210 /// });
211 /// ```
212 pub fn get_type<'py>(&self, py: Python<'py>) -> Bound<'py, PyType> {
213 self.normalized(py).ptype(py)
214 }
215
216 /// Returns the value of this exception.
217 ///
218 /// # Examples
219 ///
220 /// ```rust
221 /// use pyo3::{exceptions::PyTypeError, PyErr, Python};
222 ///
223 /// Python::attach(|py| {
224 /// let err: PyErr = PyTypeError::new_err(("some type error",));
225 /// assert!(err.is_instance_of::<PyTypeError>(py));
226 /// assert_eq!(err.value(py).to_string(), "some type error");
227 /// });
228 /// ```
229 pub fn value<'py>(&self, py: Python<'py>) -> &Bound<'py, PyBaseException> {
230 self.normalized(py).pvalue.bind(py)
231 }
232
233 /// Consumes self to take ownership of the exception value contained in this error.
234 pub fn into_value(self, py: Python<'_>) -> Py<PyBaseException> {
235 // NB technically this causes one reference count increase and decrease in quick succession
236 // on pvalue, but it's probably not worth optimizing this right now for the additional code
237 // complexity.
238 let normalized = self.normalized(py);
239 let exc = normalized.pvalue.clone_ref(py);
240 if let Some(tb) = normalized.ptraceback(py) {
241 unsafe {
242 ffi::PyException_SetTraceback(exc.as_ptr(), tb.as_ptr());
243 }
244 }
245 exc
246 }
247
248 /// Returns the traceback of this exception object.
249 ///
250 /// # Examples
251 /// ```rust
252 /// use pyo3::{exceptions::PyTypeError, Python};
253 ///
254 /// Python::attach(|py| {
255 /// let err = PyTypeError::new_err(("some type error",));
256 /// assert!(err.traceback(py).is_none());
257 /// });
258 /// ```
259 pub fn traceback<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTraceback>> {
260 self.normalized(py).ptraceback(py)
261 }
262
263 /// Gets whether an error is present in the Python interpreter's global state.
264 #[inline]
265 pub fn occurred(_: Python<'_>) -> bool {
266 unsafe { !ffi::PyErr_Occurred().is_null() }
267 }
268
269 /// Takes the current error from the Python interpreter's global state and clears the global
270 /// state. If no error is set, returns `None`.
271 ///
272 /// If the error is a `PanicException` (which would have originated from a panic in a pyo3
273 /// callback) then this function will resume the panic.
274 ///
275 /// Use this function when it is not known if an error should be present. If the error is
276 /// expected to have been set, for example from [`PyErr::occurred`] or by an error return value
277 /// from a C FFI function, use [`PyErr::fetch`].
278 pub fn take(py: Python<'_>) -> Option<PyErr> {
279 let state = PyErrStateNormalized::take(py)?;
280 let pvalue = state.pvalue.bind(py);
281 if ptr::eq(
282 pvalue.get_type().as_ptr(),
283 PanicException::type_object_raw(py).cast(),
284 ) {
285 let msg: String = pvalue
286 .str()
287 .map(|py_str| py_str.to_string_lossy().into())
288 .unwrap_or_else(|_| String::from("Unwrapped panic from Python code"));
289 Self::print_panic_and_unwind(py, PyErrState::normalized(state), msg)
290 }
291
292 Some(PyErr::from_state(PyErrState::normalized(state)))
293 }
294
295 fn print_panic_and_unwind(py: Python<'_>, state: PyErrState, msg: String) -> ! {
296 eprintln!("--- PyO3 is resuming a panic after fetching a PanicException from Python. ---");
297 eprintln!("Python stack trace below:");
298
299 state.restore(py);
300
301 unsafe {
302 ffi::PyErr_PrintEx(0);
303 }
304
305 std::panic::resume_unwind(Box::new(msg))
306 }
307
308 /// Equivalent to [PyErr::take], but when no error is set:
309 /// - Panics in debug mode.
310 /// - Returns a `SystemError` in release mode.
311 ///
312 /// This behavior is consistent with Python's internal handling of what happens when a C return
313 /// value indicates an error occurred but the global error state is empty. (A lack of exception
314 /// should be treated as a bug in the code which returned an error code but did not set an
315 /// exception.)
316 ///
317 /// Use this function when the error is expected to have been set, for example from
318 /// [PyErr::occurred] or by an error return value from a C FFI function.
319 #[cfg_attr(debug_assertions, track_caller)]
320 #[inline]
321 pub fn fetch(py: Python<'_>) -> PyErr {
322 const FAILED_TO_FETCH: &str = "attempted to fetch exception but none was set";
323 match PyErr::take(py) {
324 Some(err) => err,
325 #[cfg(debug_assertions)]
326 None => panic!("{}", FAILED_TO_FETCH),
327 #[cfg(not(debug_assertions))]
328 None => crate::exceptions::PySystemError::new_err(FAILED_TO_FETCH),
329 }
330 }
331
332 /// Creates a new exception type with the given name and docstring.
333 ///
334 /// - `base` can be an existing exception type to subclass, or a tuple of classes.
335 /// - `dict` specifies an optional dictionary of class variables and methods.
336 /// - `doc` will be the docstring seen by python users.
337 ///
338 ///
339 /// # Errors
340 ///
341 /// This function returns an error if `name` is not of the form `<module>.<ExceptionName>`.
342 pub fn new_type<'py>(
343 py: Python<'py>,
344 name: &CStr,
345 doc: Option<&CStr>,
346 base: Option<&Bound<'py, PyType>>,
347 dict: Option<Py<PyAny>>,
348 ) -> PyResult<Py<PyType>> {
349 let base: *mut ffi::PyObject = match base {
350 None => std::ptr::null_mut(),
351 Some(obj) => obj.as_ptr(),
352 };
353
354 let dict: *mut ffi::PyObject = match dict {
355 None => std::ptr::null_mut(),
356 Some(obj) => obj.as_ptr(),
357 };
358
359 let doc_ptr = match doc.as_ref() {
360 Some(c) => c.as_ptr(),
361 None => std::ptr::null(),
362 };
363
364 let ptr = unsafe { ffi::PyErr_NewExceptionWithDoc(name.as_ptr(), doc_ptr, base, dict) };
365
366 unsafe { Py::from_owned_ptr_or_err(py, ptr) }
367 }
368
369 /// Prints a standard traceback to `sys.stderr`.
370 pub fn display(&self, py: Python<'_>) {
371 #[cfg(Py_3_12)]
372 unsafe {
373 ffi::PyErr_DisplayException(self.value(py).as_ptr())
374 }
375
376 #[cfg(not(Py_3_12))]
377 unsafe {
378 // keep the bound `traceback` alive for entire duration of
379 // PyErr_Display. if we inline this, the `Bound` will be dropped
380 // after the argument got evaluated, leading to call with a dangling
381 // pointer.
382 let traceback = self.traceback(py);
383 let type_bound = self.get_type(py);
384 ffi::PyErr_Display(
385 type_bound.as_ptr(),
386 self.value(py).as_ptr(),
387 traceback
388 .as_ref()
389 .map_or(std::ptr::null_mut(), |traceback| traceback.as_ptr()),
390 )
391 }
392 }
393
394 /// Calls `sys.excepthook` and then prints a standard traceback to `sys.stderr`.
395 pub fn print(&self, py: Python<'_>) {
396 self.clone_ref(py).restore(py);
397 unsafe { ffi::PyErr_PrintEx(0) }
398 }
399
400 /// Calls `sys.excepthook` and then prints a standard traceback to `sys.stderr`.
401 ///
402 /// Additionally sets `sys.last_{type,value,traceback,exc}` attributes to this exception.
403 pub fn print_and_set_sys_last_vars(&self, py: Python<'_>) {
404 self.clone_ref(py).restore(py);
405 unsafe { ffi::PyErr_PrintEx(1) }
406 }
407
408 /// Returns true if the current exception matches the exception in `exc`.
409 ///
410 /// If `exc` is a class object, this also returns `true` when `self` is an instance of a subclass.
411 /// If `exc` is a tuple, all exceptions in the tuple (and recursively in subtuples) are searched for a match.
412 pub fn matches<'py, T>(&self, py: Python<'py>, exc: T) -> Result<bool, T::Error>
413 where
414 T: IntoPyObject<'py>,
415 {
416 Ok(self.is_instance(py, &exc.into_pyobject(py)?.into_any().as_borrowed()))
417 }
418
419 /// Returns true if the current exception is instance of `T`.
420 #[inline]
421 pub fn is_instance(&self, py: Python<'_>, ty: &Bound<'_, PyAny>) -> bool {
422 let type_bound = self.get_type(py);
423 (unsafe { ffi::PyErr_GivenExceptionMatches(type_bound.as_ptr(), ty.as_ptr()) }) != 0
424 }
425
426 /// Returns true if the current exception is instance of `T`.
427 #[inline]
428 pub fn is_instance_of<T>(&self, py: Python<'_>) -> bool
429 where
430 T: PyTypeInfo,
431 {
432 self.is_instance(py, &T::type_object(py))
433 }
434
435 /// Writes the error back to the Python interpreter's global state.
436 /// This is the opposite of `PyErr::fetch()`.
437 #[inline]
438 pub fn restore(self, py: Python<'_>) {
439 self.state.restore(py)
440 }
441
442 /// Reports the error as unraisable.
443 ///
444 /// This calls `sys.unraisablehook()` using the current exception and obj argument.
445 ///
446 /// This method is useful to report errors in situations where there is no good mechanism
447 /// to report back to the Python land. In Python this is used to indicate errors in
448 /// background threads or destructors which are protected. In Rust code this is commonly
449 /// useful when you are calling into a Python callback which might fail, but there is no
450 /// obvious way to handle this error other than logging it.
451 ///
452 /// Calling this method has the benefit that the error goes back into a standardized callback
453 /// in Python which for instance allows unittests to ensure that no unraisable error
454 /// actually happened by hooking `sys.unraisablehook`.
455 ///
456 /// Example:
457 /// ```rust
458 /// # use pyo3::prelude::*;
459 /// # use pyo3::exceptions::PyRuntimeError;
460 /// # fn failing_function() -> PyResult<()> { Err(PyRuntimeError::new_err("foo")) }
461 /// # fn main() -> PyResult<()> {
462 /// Python::attach(|py| {
463 /// match failing_function() {
464 /// Err(pyerr) => pyerr.write_unraisable(py, None),
465 /// Ok(..) => { /* do something here */ }
466 /// }
467 /// Ok(())
468 /// })
469 /// # }
470 #[inline]
471 pub fn write_unraisable(self, py: Python<'_>, obj: Option<&Bound<'_, PyAny>>) {
472 self.restore(py);
473 unsafe { ffi::PyErr_WriteUnraisable(obj.map_or(std::ptr::null_mut(), Bound::as_ptr)) }
474 }
475
476 /// Issues a warning message.
477 ///
478 /// May return an `Err(PyErr)` if warnings-as-errors is enabled.
479 ///
480 /// Equivalent to `warnings.warn()` in Python.
481 ///
482 /// The `category` should be one of the `Warning` classes available in
483 /// [`pyo3::exceptions`](crate::exceptions), or a subclass. The Python
484 /// object can be retrieved using [`Python::get_type()`].
485 ///
486 /// Example:
487 /// ```rust
488 /// # use pyo3::prelude::*;
489 /// # use pyo3::ffi::c_str;
490 /// # fn main() -> PyResult<()> {
491 /// Python::attach(|py| {
492 /// let user_warning = py.get_type::<pyo3::exceptions::PyUserWarning>();
493 /// PyErr::warn(py, &user_warning, c_str!("I am warning you"), 0)?;
494 /// Ok(())
495 /// })
496 /// # }
497 /// ```
498 pub fn warn<'py>(
499 py: Python<'py>,
500 category: &Bound<'py, PyAny>,
501 message: &CStr,
502 stacklevel: i32,
503 ) -> PyResult<()> {
504 error_on_minusone(py, unsafe {
505 ffi::PyErr_WarnEx(
506 category.as_ptr(),
507 message.as_ptr(),
508 stacklevel as ffi::Py_ssize_t,
509 )
510 })
511 }
512
513 /// Issues a warning message, with more control over the warning attributes.
514 ///
515 /// May return a `PyErr` if warnings-as-errors is enabled.
516 ///
517 /// Equivalent to `warnings.warn_explicit()` in Python.
518 ///
519 /// The `category` should be one of the `Warning` classes available in
520 /// [`pyo3::exceptions`](crate::exceptions), or a subclass.
521 pub fn warn_explicit<'py>(
522 py: Python<'py>,
523 category: &Bound<'py, PyAny>,
524 message: &CStr,
525 filename: &CStr,
526 lineno: i32,
527 module: Option<&CStr>,
528 registry: Option<&Bound<'py, PyAny>>,
529 ) -> PyResult<()> {
530 let module_ptr = match module {
531 None => std::ptr::null_mut(),
532 Some(s) => s.as_ptr(),
533 };
534 let registry: *mut ffi::PyObject = match registry {
535 None => std::ptr::null_mut(),
536 Some(obj) => obj.as_ptr(),
537 };
538 error_on_minusone(py, unsafe {
539 ffi::PyErr_WarnExplicit(
540 category.as_ptr(),
541 message.as_ptr(),
542 filename.as_ptr(),
543 lineno,
544 module_ptr,
545 registry,
546 )
547 })
548 }
549
550 /// Clone the PyErr. This requires the GIL, which is why PyErr does not implement Clone.
551 ///
552 /// # Examples
553 /// ```rust
554 /// use pyo3::{exceptions::PyTypeError, PyErr, Python, prelude::PyAnyMethods};
555 /// Python::attach(|py| {
556 /// let err: PyErr = PyTypeError::new_err(("some type error",));
557 /// let err_clone = err.clone_ref(py);
558 /// assert!(err.get_type(py).is(&err_clone.get_type(py)));
559 /// assert!(err.value(py).is(err_clone.value(py)));
560 /// match err.traceback(py) {
561 /// None => assert!(err_clone.traceback(py).is_none()),
562 /// Some(tb) => assert!(err_clone.traceback(py).unwrap().is(&tb)),
563 /// }
564 /// });
565 /// ```
566 #[inline]
567 pub fn clone_ref(&self, py: Python<'_>) -> PyErr {
568 PyErr::from_state(PyErrState::normalized(self.normalized(py).clone_ref(py)))
569 }
570
571 /// Return the cause (either an exception instance, or None, set by `raise ... from ...`)
572 /// associated with the exception, as accessible from Python through `__cause__`.
573 pub fn cause(&self, py: Python<'_>) -> Option<PyErr> {
574 use crate::ffi_ptr_ext::FfiPtrExt;
575 let obj =
576 unsafe { ffi::PyException_GetCause(self.value(py).as_ptr()).assume_owned_or_opt(py) };
577 // PyException_GetCause is documented as potentially returning PyNone, but only GraalPy seems to actually do that
578 #[cfg(GraalPy)]
579 if let Some(cause) = &obj {
580 if cause.is_none() {
581 return None;
582 }
583 }
584 obj.map(Self::from_value)
585 }
586
587 /// Set the cause associated with the exception, pass `None` to clear it.
588 pub fn set_cause(&self, py: Python<'_>, cause: Option<Self>) {
589 let value = self.value(py);
590 let cause = cause.map(|err| err.into_value(py));
591 unsafe {
592 // PyException_SetCause _steals_ a reference to cause, so must use .into_ptr()
593 ffi::PyException_SetCause(
594 value.as_ptr(),
595 cause.map_or(std::ptr::null_mut(), Py::into_ptr),
596 );
597 }
598 }
599
600 /// Equivalent to calling `add_note` on the exception in Python.
601 #[cfg(Py_3_11)]
602 pub fn add_note<N: for<'py> IntoPyObject<'py, Target = PyString>>(
603 &self,
604 py: Python<'_>,
605 note: N,
606 ) -> PyResult<()> {
607 self.value(py)
608 .call_method1(intern!(py, "add_note"), (note,))?;
609 Ok(())
610 }
611
612 #[inline]
613 fn from_state(state: PyErrState) -> PyErr {
614 PyErr { state }
615 }
616
617 #[inline]
618 fn normalized(&self, py: Python<'_>) -> &PyErrStateNormalized {
619 self.state.as_normalized(py)
620 }
621}
622
623impl std::fmt::Debug for PyErr {
624 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
625 Python::attach(|py| {
626 f.debug_struct("PyErr")
627 .field("type", &self.get_type(py))
628 .field("value", self.value(py))
629 .field(
630 "traceback",
631 &self.traceback(py).map(|tb| match tb.format() {
632 Ok(s) => s,
633 Err(err) => {
634 err.write_unraisable(py, Some(&tb));
635 // It would be nice to format what we can of the
636 // error, but we can't guarantee that the error
637 // won't have another unformattable traceback inside
638 // it and we want to avoid an infinite recursion.
639 format!("<unformattable {tb:?}>")
640 }
641 }),
642 )
643 .finish()
644 })
645 }
646}
647
648impl std::fmt::Display for PyErr {
649 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
650 Python::attach(|py| {
651 let value = self.value(py);
652 let type_name = value.get_type().qualname().map_err(|_| std::fmt::Error)?;
653 write!(f, "{type_name}")?;
654 if let Ok(s) = value.str() {
655 write!(f, ": {}", &s.to_string_lossy())
656 } else {
657 write!(f, ": <exception str() failed>")
658 }
659 })
660 }
661}
662
663impl std::error::Error for PyErr {}
664
665impl<'py> IntoPyObject<'py> for PyErr {
666 type Target = PyBaseException;
667 type Output = Bound<'py, Self::Target>;
668 type Error = Infallible;
669
670 #[inline]
671 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
672 Ok(self.into_value(py).into_bound(py))
673 }
674}
675
676impl<'py> IntoPyObject<'py> for &PyErr {
677 type Target = PyBaseException;
678 type Output = Bound<'py, Self::Target>;
679 type Error = Infallible;
680
681 #[inline]
682 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
683 self.clone_ref(py).into_pyobject(py)
684 }
685}
686
687/// Python exceptions that can be converted to [`PyErr`].
688///
689/// This is used to implement [`From<Bound<'_, T>> for PyErr`].
690///
691/// Users should not need to implement this trait directly. It is implemented automatically in the
692/// [`crate::import_exception!`] and [`crate::create_exception!`] macros.
693pub trait ToPyErr {}
694
695impl<'py, T> std::convert::From<Bound<'py, T>> for PyErr
696where
697 T: ToPyErr,
698{
699 #[inline]
700 fn from(err: Bound<'py, T>) -> PyErr {
701 PyErr::from_value(err.into_any())
702 }
703}
704
705#[track_caller]
706pub fn panic_after_error(_py: Python<'_>) -> ! {
707 unsafe {
708 ffi::PyErr_Print();
709 }
710 panic!("Python API call failed");
711}
712
713/// Returns Ok if the error code is not -1.
714#[inline]
715pub(crate) fn error_on_minusone<T: SignedInteger>(py: Python<'_>, result: T) -> PyResult<()> {
716 if result != T::MINUS_ONE {
717 Ok(())
718 } else {
719 Err(PyErr::fetch(py))
720 }
721}
722
723pub(crate) trait SignedInteger: Eq {
724 const MINUS_ONE: Self;
725}
726
727macro_rules! impl_signed_integer {
728 ($t:ty) => {
729 impl SignedInteger for $t {
730 const MINUS_ONE: Self = -1;
731 }
732 };
733}
734
735impl_signed_integer!(i8);
736impl_signed_integer!(i16);
737impl_signed_integer!(i32);
738impl_signed_integer!(i64);
739impl_signed_integer!(i128);
740impl_signed_integer!(isize);
741
742#[cfg(test)]
743mod tests {
744 use super::PyErrState;
745 use crate::exceptions::{self, PyTypeError, PyValueError};
746 use crate::impl_::pyclass::{value_of, IsSend, IsSync};
747 use crate::test_utils::assert_warnings;
748 use crate::{ffi, PyErr, PyTypeInfo, Python};
749
750 #[test]
751 fn no_error() {
752 assert!(Python::attach(PyErr::take).is_none());
753 }
754
755 #[test]
756 fn set_valueerror() {
757 Python::attach(|py| {
758 let err: PyErr = exceptions::PyValueError::new_err("some exception message");
759 assert!(err.is_instance_of::<exceptions::PyValueError>(py));
760 err.restore(py);
761 assert!(PyErr::occurred(py));
762 let err = PyErr::fetch(py);
763 assert!(err.is_instance_of::<exceptions::PyValueError>(py));
764 assert_eq!(err.to_string(), "ValueError: some exception message");
765 })
766 }
767
768 #[test]
769 fn invalid_error_type() {
770 Python::attach(|py| {
771 let err: PyErr = PyErr::new::<crate::types::PyString, _>(());
772 assert!(err.is_instance_of::<exceptions::PyTypeError>(py));
773 err.restore(py);
774 let err = PyErr::fetch(py);
775
776 assert!(err.is_instance_of::<exceptions::PyTypeError>(py));
777 assert_eq!(
778 err.to_string(),
779 "TypeError: exceptions must derive from BaseException"
780 );
781 })
782 }
783
784 #[test]
785 fn set_typeerror() {
786 Python::attach(|py| {
787 let err: PyErr = exceptions::PyTypeError::new_err(());
788 err.restore(py);
789 assert!(PyErr::occurred(py));
790 drop(PyErr::fetch(py));
791 });
792 }
793
794 #[test]
795 #[should_panic(expected = "new panic")]
796 fn fetching_panic_exception_resumes_unwind() {
797 use crate::panic::PanicException;
798
799 Python::attach(|py| {
800 let err: PyErr = PanicException::new_err("new panic");
801 err.restore(py);
802 assert!(PyErr::occurred(py));
803
804 // should resume unwind
805 let _ = PyErr::fetch(py);
806 });
807 }
808
809 #[test]
810 #[should_panic(expected = "new panic")]
811 #[cfg(not(Py_3_12))]
812 fn fetching_normalized_panic_exception_resumes_unwind() {
813 use crate::panic::PanicException;
814
815 Python::attach(|py| {
816 let err: PyErr = PanicException::new_err("new panic");
817 // Restoring an error doesn't normalize it before Python 3.12,
818 // so we have to explicitly test this case.
819 let _ = err.normalized(py);
820 err.restore(py);
821 assert!(PyErr::occurred(py));
822
823 // should resume unwind
824 let _ = PyErr::fetch(py);
825 });
826 }
827
828 #[test]
829 fn err_debug() {
830 // Debug representation should be like the following (without the newlines):
831 // PyErr {
832 // type: <class 'Exception'>,
833 // value: Exception('banana'),
834 // traceback: Some(\"Traceback (most recent call last):\\n File \\\"<string>\\\", line 1, in <module>\\n\")
835 // }
836
837 Python::attach(|py| {
838 let err = py
839 .run(ffi::c_str!("raise Exception('banana')"), None, None)
840 .expect_err("raising should have given us an error");
841
842 let debug_str = format!("{err:?}");
843 assert!(debug_str.starts_with("PyErr { "));
844 assert!(debug_str.ends_with(" }"));
845
846 // Strip "PyErr { " and " }". Split into 3 substrings to separate type,
847 // value, and traceback while not splitting the string within traceback.
848 let mut fields = debug_str["PyErr { ".len()..debug_str.len() - 2].splitn(3, ", ");
849
850 assert_eq!(fields.next().unwrap(), "type: <class 'Exception'>");
851 assert_eq!(fields.next().unwrap(), "value: Exception('banana')");
852 assert_eq!(
853 fields.next().unwrap(),
854 "traceback: Some(\"Traceback (most recent call last):\\n File \\\"<string>\\\", line 1, in <module>\\n\")"
855 );
856
857 assert!(fields.next().is_none());
858 });
859 }
860
861 #[test]
862 fn err_display() {
863 Python::attach(|py| {
864 let err = py
865 .run(ffi::c_str!("raise Exception('banana')"), None, None)
866 .expect_err("raising should have given us an error");
867 assert_eq!(err.to_string(), "Exception: banana");
868 });
869 }
870
871 #[test]
872 fn test_pyerr_send_sync() {
873 assert!(value_of!(IsSend, PyErr));
874 assert!(value_of!(IsSync, PyErr));
875
876 assert!(value_of!(IsSend, PyErrState));
877 assert!(value_of!(IsSync, PyErrState));
878 }
879
880 #[test]
881 fn test_pyerr_matches() {
882 Python::attach(|py| {
883 let err = PyErr::new::<PyValueError, _>("foo");
884 assert!(err.matches(py, PyValueError::type_object(py)).unwrap());
885
886 assert!(err
887 .matches(
888 py,
889 (PyValueError::type_object(py), PyTypeError::type_object(py))
890 )
891 .unwrap());
892
893 assert!(!err.matches(py, PyTypeError::type_object(py)).unwrap());
894
895 // String is not a valid exception class, so we should get a TypeError
896 let err: PyErr = PyErr::from_type(crate::types::PyString::type_object(py), "foo");
897 assert!(err.matches(py, PyTypeError::type_object(py)).unwrap());
898 })
899 }
900
901 #[test]
902 fn test_pyerr_cause() {
903 Python::attach(|py| {
904 let err = py
905 .run(ffi::c_str!("raise Exception('banana')"), None, None)
906 .expect_err("raising should have given us an error");
907 assert!(err.cause(py).is_none());
908
909 let err = py
910 .run(
911 ffi::c_str!("raise Exception('banana') from Exception('apple')"),
912 None,
913 None,
914 )
915 .expect_err("raising should have given us an error");
916 let cause = err
917 .cause(py)
918 .expect("raising from should have given us a cause");
919 assert_eq!(cause.to_string(), "Exception: apple");
920
921 err.set_cause(py, None);
922 assert!(err.cause(py).is_none());
923
924 let new_cause = exceptions::PyValueError::new_err("orange");
925 err.set_cause(py, Some(new_cause));
926 let cause = err
927 .cause(py)
928 .expect("set_cause should have given us a cause");
929 assert_eq!(cause.to_string(), "ValueError: orange");
930 });
931 }
932
933 #[test]
934 fn warnings() {
935 use crate::types::any::PyAnyMethods;
936 // Note: although the warning filter is interpreter global, keeping the
937 // GIL locked should prevent effects to be visible to other testing
938 // threads.
939 Python::attach(|py| {
940 let cls = py.get_type::<exceptions::PyUserWarning>();
941
942 // Reset warning filter to default state
943 let warnings = py.import("warnings").unwrap();
944 warnings.call_method0("resetwarnings").unwrap();
945
946 // First, test the warning is emitted
947 assert_warnings!(
948 py,
949 { PyErr::warn(py, &cls, ffi::c_str!("I am warning you"), 0).unwrap() },
950 [(exceptions::PyUserWarning, "I am warning you")]
951 );
952
953 // Test with raising
954 warnings
955 .call_method1("simplefilter", ("error", &cls))
956 .unwrap();
957 PyErr::warn(py, &cls, ffi::c_str!("I am warning you"), 0).unwrap_err();
958
959 // Test with error for an explicit module
960 warnings.call_method0("resetwarnings").unwrap();
961 warnings
962 .call_method1("filterwarnings", ("error", "", &cls, "pyo3test"))
963 .unwrap();
964
965 // This has the wrong module and will not raise, just be emitted
966 assert_warnings!(
967 py,
968 { PyErr::warn(py, &cls, ffi::c_str!("I am warning you"), 0).unwrap() },
969 [(exceptions::PyUserWarning, "I am warning you")]
970 );
971
972 let err = PyErr::warn_explicit(
973 py,
974 &cls,
975 ffi::c_str!("I am warning you"),
976 ffi::c_str!("pyo3test.py"),
977 427,
978 None,
979 None,
980 )
981 .unwrap_err();
982 assert!(err
983 .value(py)
984 .getattr("args")
985 .unwrap()
986 .get_item(0)
987 .unwrap()
988 .eq("I am warning you")
989 .unwrap());
990
991 // Finally, reset filter again
992 warnings.call_method0("resetwarnings").unwrap();
993 });
994 }
995
996 #[test]
997 #[cfg(Py_3_11)]
998 fn test_add_note() {
999 use crate::types::any::PyAnyMethods;
1000 Python::attach(|py| {
1001 let err = PyErr::new::<exceptions::PyValueError, _>("original error");
1002 err.add_note(py, "additional context").unwrap();
1003
1004 let notes = err.value(py).getattr("__notes__").unwrap();
1005 assert_eq!(notes.len().unwrap(), 1);
1006 assert_eq!(
1007 notes.get_item(0).unwrap().extract::<String>().unwrap(),
1008 "additional context"
1009 );
1010 });
1011 }
1012}