From 22c5cff0394f0095b204e9aa6e0f4b31808506a1 Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Tue, 30 Apr 2024 18:58:53 +0200 Subject: [PATCH] feature gate deprecated APIs for `PyErr` and exceptions (#4136) --- src/err/mod.rs | 108 ++++++++++++++++----------------------- src/exceptions.rs | 40 ++++++++------- tests/test_exceptions.rs | 7 ++- 3 files changed, 69 insertions(+), 86 deletions(-) diff --git a/src/err/mod.rs b/src/err/mod.rs index a61c8c62..4f46f360 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -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(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<'_>, diff --git a/src/exceptions.rs b/src/exceptions.rs index c650d7af..36702292 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -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; diff --git a/tests/test_exceptions.rs b/tests/test_exceptions.rs index ec2fe156..e85355fd 100644 --- a/tests/test_exceptions.rs +++ b/tests/test_exceptions.rs @@ -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");