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