From bc87b7bac619ae7e29bfe197ce94faea8a4a2f75 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Mon, 27 Nov 2023 21:27:34 +0000 Subject: [PATCH] deprecate `PyTryFrom` and `PyTryInto` --- guide/src/migration.md | 1 + newsfragments/3601.changed.md | 1 + src/conversion.rs | 92 ++++++++++++++++++++++------------- src/instance.rs | 4 +- src/lib.rs | 1 + src/prelude.rs | 1 + src/type_object.rs | 1 - src/types/iterator.rs | 1 + src/types/mapping.rs | 5 +- src/types/sequence.rs | 5 +- 10 files changed, 75 insertions(+), 37 deletions(-) create mode 100644 newsfragments/3601.changed.md diff --git a/guide/src/migration.md b/guide/src/migration.md index 0c55c15c..82acf11e 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -16,6 +16,7 @@ To migrate, switch all type casts to use `obj.downcast()` instead of `try_from(o Before: ```rust +# #![allow(deprecated)] # use pyo3::prelude::*; # use pyo3::types::{PyInt, PyList}; # fn main() -> PyResult<()> { diff --git a/newsfragments/3601.changed.md b/newsfragments/3601.changed.md new file mode 100644 index 00000000..413765ec --- /dev/null +++ b/newsfragments/3601.changed.md @@ -0,0 +1 @@ +Deprecate `PyTryFrom` and `PyTryInto` traits in favor of `any.downcast()` via the `PyTypeCheck` and `PyTypeInfo` traits. diff --git a/src/conversion.rs b/src/conversion.rs index 665cdeca..0a842f9a 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -355,11 +355,20 @@ where /// If `T` implements `PyTryFrom`, we can convert `&PyAny` to `&T`. /// /// This trait is similar to `std::convert::TryFrom` +#[deprecated(since = "0.21.0")] pub trait PyTryFrom<'v>: Sized + PyNativeType { /// Cast from a concrete Python object type to PyObject. + #[deprecated( + since = "0.21.0", + note = "use `value.downcast::()` instead of `T::try_from(value)`" + )] fn try_from>(value: V) -> Result<&'v Self, PyDowncastError<'v>>; /// Cast from a concrete Python object type to PyObject. With exact type check. + #[deprecated( + since = "0.21.0", + note = "use `value.downcast_exact::()` instead of `T::try_from_exact(value)`" + )] fn try_from_exact>(value: V) -> Result<&'v Self, PyDowncastError<'v>>; /// Cast a PyAny to a specific type of PyObject. The caller must @@ -368,19 +377,33 @@ pub trait PyTryFrom<'v>: Sized + PyNativeType { /// # Safety /// /// Callers must ensure that the type is valid or risk type confusion. + #[deprecated( + since = "0.21.0", + note = "use `value.downcast_unchecked::()` instead of `T::try_from_unchecked(value)`" + )] unsafe fn try_from_unchecked>(value: V) -> &'v Self; } /// Trait implemented by Python object types that allow a checked downcast. /// This trait is similar to `std::convert::TryInto` +#[deprecated(since = "0.21.0")] pub trait PyTryInto: Sized { /// Cast from PyObject to a concrete Python object type. + #[deprecated( + since = "0.21.0", + note = "use `value.downcast()` instead of `value.try_into()`" + )] fn try_into(&self) -> Result<&T, PyDowncastError<'_>>; /// Cast from PyObject to a concrete Python object type. With exact type check. + #[deprecated( + since = "0.21.0", + note = "use `value.downcast()` instead of `value.try_into_exact()`" + )] fn try_into_exact(&self) -> Result<&T, PyDowncastError<'_>>; } +#[allow(deprecated)] mod implementations { use super::*; @@ -555,47 +578,50 @@ mod test_no_clone {} #[cfg(test)] mod tests { - use crate::PyObject; + use crate::{PyObject, Python}; - use super::super::PyTryFrom; - use crate::types::{IntoPyDict, PyAny, PyDict, PyList}; - use crate::{Python, ToPyObject}; + #[allow(deprecated)] + mod deprecated { + use super::super::PyTryFrom; + use crate::types::{IntoPyDict, PyAny, PyDict, PyList}; + use crate::{Python, ToPyObject}; - #[test] - fn test_try_from() { - Python::with_gil(|py| { - let list: &PyAny = vec![3, 6, 5, 4, 7].to_object(py).into_ref(py); - let dict: &PyAny = vec![("reverse", true)].into_py_dict(py).as_ref(); + #[test] + fn test_try_from() { + Python::with_gil(|py| { + let list: &PyAny = vec![3, 6, 5, 4, 7].to_object(py).into_ref(py); + let dict: &PyAny = vec![("reverse", true)].into_py_dict(py).as_ref(); - assert!(>::try_from(list).is_ok()); - assert!(>::try_from(dict).is_ok()); + assert!(>::try_from(list).is_ok()); + assert!(>::try_from(dict).is_ok()); - assert!(>::try_from(list).is_ok()); - assert!(>::try_from(dict).is_ok()); - }); - } + assert!(>::try_from(list).is_ok()); + assert!(>::try_from(dict).is_ok()); + }); + } - #[test] - fn test_try_from_exact() { - Python::with_gil(|py| { - let list: &PyAny = vec![3, 6, 5, 4, 7].to_object(py).into_ref(py); - let dict: &PyAny = vec![("reverse", true)].into_py_dict(py).as_ref(); + #[test] + fn test_try_from_exact() { + Python::with_gil(|py| { + let list: &PyAny = vec![3, 6, 5, 4, 7].to_object(py).into_ref(py); + let dict: &PyAny = vec![("reverse", true)].into_py_dict(py).as_ref(); - assert!(PyList::try_from_exact(list).is_ok()); - assert!(PyDict::try_from_exact(dict).is_ok()); + assert!(PyList::try_from_exact(list).is_ok()); + assert!(PyDict::try_from_exact(dict).is_ok()); - assert!(PyAny::try_from_exact(list).is_err()); - assert!(PyAny::try_from_exact(dict).is_err()); - }); - } + assert!(PyAny::try_from_exact(list).is_err()); + assert!(PyAny::try_from_exact(dict).is_err()); + }); + } - #[test] - fn test_try_from_unchecked() { - Python::with_gil(|py| { - let list = PyList::new(py, [1, 2, 3]); - let val = unsafe { ::try_from_unchecked(list.as_ref()) }; - assert!(list.is(val)); - }); + #[test] + fn test_try_from_unchecked() { + Python::with_gil(|py| { + let list = PyList::new(py, [1, 2, 3]); + let val = unsafe { ::try_from_unchecked(list.as_ref()) }; + assert!(list.is(val)); + }); + } } #[test] diff --git a/src/instance.rs b/src/instance.rs index 7568b28f..a25992fe 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -1614,7 +1614,7 @@ a = A() #[cfg(feature = "macros")] mod using_macros { - use crate::{PyCell, PyTryInto}; + use crate::PyCell; use super::*; @@ -1642,7 +1642,9 @@ a = A() } #[test] + #[allow(deprecated)] fn cell_tryfrom() { + use crate::PyTryInto; // More detailed tests of the underlying semantics in pycell.rs Python::with_gil(|py| { let instance: &PyAny = Py::new(py, SomeClass(0)).unwrap().into_ref(py); diff --git a/src/lib.rs b/src/lib.rs index 985ec0aa..96b753ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -295,6 +295,7 @@ //! [`Ungil`]: crate::marker::Ungil pub use crate::class::*; pub use crate::conversion::{AsPyPointer, FromPyObject, FromPyPointer, IntoPy, ToPyObject}; +#[allow(deprecated)] pub use crate::conversion::{PyTryFrom, PyTryInto}; pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyResult}; pub use crate::gil::GILPool; diff --git a/src/prelude.rs b/src/prelude.rs index 85409ffe..2a0397c1 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -9,6 +9,7 @@ //! ``` pub use crate::conversion::{FromPyObject, IntoPy, ToPyObject}; +#[allow(deprecated)] pub use crate::conversion::{PyTryFrom, PyTryInto}; pub use crate::err::{PyErr, PyResult}; pub use crate::instance::{Py, PyObject}; diff --git a/src/type_object.rs b/src/type_object.rs index 428de86a..e7d88f76 100644 --- a/src/type_object.rs +++ b/src/type_object.rs @@ -23,7 +23,6 @@ pub trait PySizedLayout: PyLayout + Sized {} /// /// This is expected to be deprecated in the near future, see /// -/// /// # Safety /// /// - `Py::as_ref` will hand out references to `Self::AsRefTarget`. diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 13c4e4ba..49a85194 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -79,6 +79,7 @@ impl PyTypeCheck for PyIterator { } } +#[allow(deprecated)] impl<'v> crate::PyTryFrom<'v> for PyIterator { fn try_from>(value: V) -> Result<&'v PyIterator, PyDowncastError<'v>> { let value = value.into(); diff --git a/src/types/mapping.rs b/src/types/mapping.rs index e837ef91..798b7268 100644 --- a/src/types/mapping.rs +++ b/src/types/mapping.rs @@ -135,6 +135,7 @@ impl PyTypeCheck for PyMapping { } } +#[allow(deprecated)] impl<'v> crate::PyTryFrom<'v> for PyMapping { /// Downcasting to `PyMapping` requires the concrete class to be a subclass (or registered /// subclass) of `collections.abc.Mapping` (from the Python standard library) - i.e. @@ -168,7 +169,7 @@ mod tests { use crate::{ exceptions::PyKeyError, types::{PyDict, PyTuple}, - PyTryFrom, Python, + Python, }; use super::*; @@ -318,7 +319,9 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_mapping_try_from() { + use crate::PyTryFrom; Python::with_gil(|py| { let dict = PyDict::new(py); let _ = ::try_from(dict).unwrap(); diff --git a/src/types/sequence.rs b/src/types/sequence.rs index e3b76c12..c4c2b21b 100644 --- a/src/types/sequence.rs +++ b/src/types/sequence.rs @@ -549,6 +549,7 @@ impl PyTypeCheck for PySequence { } } +#[allow(deprecated)] impl<'v> crate::PyTryFrom<'v> for PySequence { /// Downcasting to `PySequence` requires the concrete class to be a subclass (or registered /// subclass) of `collections.abc.Sequence` (from the Python standard library) - i.e. @@ -577,7 +578,7 @@ impl<'v> crate::PyTryFrom<'v> for PySequence { #[cfg(test)] mod tests { use crate::types::{PyList, PySequence, PyTuple}; - use crate::{PyObject, PyTryFrom, Python, ToPyObject}; + use crate::{PyObject, Python, ToPyObject}; fn get_object() -> PyObject { // Convenience function for getting a single unique object @@ -1078,7 +1079,9 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_seq_try_from() { + use crate::PyTryFrom; Python::with_gil(|py| { let list = PyList::empty(py); let _ = ::try_from(list).unwrap();