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