feature gate deprecated APIs for `PyErr` and exceptions (#4136)

This commit is contained in:
Icxolu 2024-04-30 18:58:53 +02:00 committed by GitHub
parent d5452bcd8d
commit 22c5cff039
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 69 additions and 86 deletions

View File

@ -24,9 +24,9 @@ use err_state::{PyErrState, PyErrStateLazyFnOutput, PyErrStateNormalized};
/// compatibility with `?` and other Rust errors) this type supports creating exceptions instances
/// in a lazy fashion, where the full Python object for the exception is created only when needed.
///
/// Accessing the contained exception in any way, such as with [`value`](PyErr::value),
/// [`get_type`](PyErr::get_type), or [`is_instance`](PyErr::is_instance) will create the full
/// exception object if it was not already created.
/// Accessing the contained exception in any way, such as with [`value_bound`](PyErr::value_bound),
/// [`get_type_bound`](PyErr::get_type_bound), or [`is_instance_bound`](PyErr::is_instance_bound)
/// will create the full exception object if it was not already created.
pub struct PyErr {
// Safety: can only hand out references when in the "normalized" state. Will never change
// after normalization.
@ -136,7 +136,7 @@ impl PyErr {
///
/// This exception instance will be initialized lazily. This avoids the need for the Python GIL
/// to be held, but requires `args` to be `Send` and `Sync`. If `args` is not `Send` or `Sync`,
/// consider using [`PyErr::from_value`] instead.
/// consider using [`PyErr::from_value_bound`] instead.
///
/// If `T` does not inherit from `BaseException`, then a `TypeError` will be returned.
///
@ -192,12 +192,10 @@ impl PyErr {
}
/// Deprecated form of [`PyErr::from_type_bound`]
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::from_type` will be replaced by `PyErr::from_type_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyErr::from_type` will be replaced by `PyErr::from_type_bound` in a future PyO3 version"
)]
pub fn from_type<A>(ty: &PyType, args: A) -> PyErr
where
@ -224,12 +222,10 @@ impl PyErr {
}
/// Deprecated form of [`PyErr::from_value_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::from_value` will be replaced by `PyErr::from_value_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyErr::from_value` will be replaced by `PyErr::from_value_bound` in a future PyO3 version"
)]
pub fn from_value(obj: &PyAny) -> PyErr {
PyErr::from_value_bound(obj.as_borrowed().to_owned())
@ -284,12 +280,10 @@ impl PyErr {
}
/// Deprecated form of [`PyErr::get_type_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::get_type` will be replaced by `PyErr::get_type_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyErr::get_type` will be replaced by `PyErr::get_type_bound` in a future PyO3 version"
)]
pub fn get_type<'py>(&'py self, py: Python<'py>) -> &'py PyType {
self.get_type_bound(py).into_gil_ref()
@ -311,12 +305,10 @@ impl PyErr {
}
/// Deprecated form of [`PyErr::value_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::value` will be replaced by `PyErr::value_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyErr::value` will be replaced by `PyErr::value_bound` in a future PyO3 version"
)]
pub fn value<'py>(&'py self, py: Python<'py>) -> &'py PyBaseException {
self.value_bound(py).as_gil_ref()
@ -355,12 +347,10 @@ impl PyErr {
}
/// Deprecated form of [`PyErr::traceback_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::traceback` will be replaced by `PyErr::traceback_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyErr::traceback` will be replaced by `PyErr::traceback_bound` in a future PyO3 version"
)]
pub fn traceback<'py>(&'py self, py: Python<'py>) -> Option<&'py PyTraceback> {
self.normalized(py).ptraceback(py).map(|b| b.into_gil_ref())
@ -508,12 +498,10 @@ impl PyErr {
}
/// Deprecated form of [`PyErr::new_type_bound`]
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::new_type` will be replaced by `PyErr::new_type_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyErr::new_type` will be replaced by `PyErr::new_type_bound` in a future PyO3 version"
)]
pub fn new_type(
py: Python<'_>,
@ -636,12 +624,10 @@ impl PyErr {
}
/// Deprecated form of `PyErr::is_instance_bound`.
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::is_instance` will be replaced by `PyErr::is_instance_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyErr::is_instance` will be replaced by `PyErr::is_instance_bound` in a future PyO3 version"
)]
#[inline]
pub fn is_instance(&self, py: Python<'_>, ty: &PyAny) -> bool {
@ -675,12 +661,10 @@ impl PyErr {
}
/// Deprecated form of `PyErr::write_unraisable_bound`.
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::write_unraisable` will be replaced by `PyErr::write_unraisable_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyErr::write_unraisable` will be replaced by `PyErr::write_unraisable_bound` in a future PyO3 version"
)]
#[inline]
pub fn write_unraisable(self, py: Python<'_>, obj: Option<&PyAny>) {
@ -722,12 +706,10 @@ impl PyErr {
}
/// Deprecated form of [`PyErr::warn_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::warn` will be replaced by `PyErr::warn_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyErr::warn` will be replaced by `PyErr::warn_bound` in a future PyO3 version"
)]
pub fn warn(py: Python<'_>, category: &PyAny, message: &str, stacklevel: i32) -> PyResult<()> {
Self::warn_bound(py, &category.as_borrowed(), message, stacklevel)
@ -771,12 +753,10 @@ impl PyErr {
}
/// Deprecated form of [`PyErr::warn_explicit_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::warn_explicit` will be replaced by `PyErr::warn_explicit_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyErr::warn_explicit` will be replaced by `PyErr::warn_explicit_bound` in a future PyO3 version"
)]
pub fn warn_explicit(
py: Python<'_>,

View File

@ -21,6 +21,7 @@ macro_rules! impl_exception_boilerplate {
($name: ident) => {
// FIXME https://github.com/PyO3/pyo3/issues/3903
#[allow(unknown_lints, non_local_definitions)]
#[cfg(feature = "gil-refs")]
impl ::std::convert::From<&$name> for $crate::PyErr {
#[inline]
fn from(err: &$name) -> $crate::PyErr {
@ -650,12 +651,10 @@ impl_windows_native_exception!(
impl PyUnicodeDecodeError {
/// Deprecated form of [`PyUnicodeDecodeError::new_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyUnicodeDecodeError::new` will be replaced by `PyUnicodeDecodeError::new_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyUnicodeDecodeError::new` will be replaced by `PyUnicodeDecodeError::new_bound` in a future PyO3 version"
)]
pub fn new<'p>(
py: Python<'p>,
@ -692,12 +691,10 @@ impl PyUnicodeDecodeError {
}
/// Deprecated form of [`PyUnicodeDecodeError::new_utf8_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyUnicodeDecodeError::new_utf8` will be replaced by `PyUnicodeDecodeError::new_utf8_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyUnicodeDecodeError::new_utf8` will be replaced by `PyUnicodeDecodeError::new_utf8_bound` in a future PyO3 version"
)]
pub fn new_utf8<'p>(
py: Python<'p>,
@ -808,7 +805,7 @@ macro_rules! test_exception {
use super::$exc_ty;
$crate::Python::with_gil(|py| {
use std::error::Error;
use $crate::types::PyAnyMethods;
let err: $crate::PyErr = {
None
$(
@ -819,13 +816,19 @@ macro_rules! test_exception {
assert!(err.is_instance_of::<$exc_ty>(py));
let value: &$exc_ty = err.value_bound(py).clone().into_gil_ref().downcast().unwrap();
assert!(value.source().is_none());
let value = err.value_bound(py).as_any().downcast::<$exc_ty>().unwrap();
err.set_cause(py, Some($crate::exceptions::PyValueError::new_err("a cause")));
assert!(value.source().is_some());
#[cfg(feature = "gil-refs")]
{
use std::error::Error;
let value = value.as_gil_ref();
assert!(value.source().is_none());
assert!($crate::PyErr::from(value).is_instance_of::<$exc_ty>(py));
err.set_cause(py, Some($crate::exceptions::PyValueError::new_err("a cause")));
assert!(value.source().is_some());
}
assert!($crate::PyErr::from(value.clone()).is_instance_of::<$exc_ty>(py));
})
}
};
@ -1068,6 +1071,7 @@ mod tests {
}
#[test]
#[cfg(feature = "gil-refs")]
fn native_exception_chain() {
use std::error::Error;

View File

@ -100,10 +100,9 @@ fn test_exception_nosegfault() {
#[test]
#[cfg(Py_3_8)]
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
fn test_write_unraisable() {
use common::UnraisableCapture;
use pyo3::{exceptions::PyRuntimeError, ffi};
use pyo3::{exceptions::PyRuntimeError, ffi, types::PyNotImplemented};
Python::with_gil(|py| {
let capture = UnraisableCapture::install(py);
@ -111,14 +110,14 @@ fn test_write_unraisable() {
assert!(capture.borrow(py).capture.is_none());
let err = PyRuntimeError::new_err("foo");
err.write_unraisable(py, None);
err.write_unraisable_bound(py, None);
let (err, object) = capture.borrow_mut(py).capture.take().unwrap();
assert_eq!(err.to_string(), "RuntimeError: foo");
assert!(object.is_none(py));
let err = PyRuntimeError::new_err("bar");
err.write_unraisable(py, Some(py.NotImplemented().as_ref(py)));
err.write_unraisable_bound(py, Some(&PyNotImplemented::get_bound(py)));
let (err, object) = capture.borrow_mut(py).capture.take().unwrap();
assert_eq!(err.to_string(), "RuntimeError: bar");