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