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