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