diff --git a/src/conversions/hashbrown.rs b/src/conversions/hashbrown.rs index 7e57d332..c9b6c61c 100644 --- a/src/conversions/hashbrown.rs +++ b/src/conversions/hashbrown.rs @@ -121,7 +121,7 @@ mod tests { map.insert(1, 1); let m = map.to_object(py); - let py_map: &PyDict = m.downcast(py).unwrap(); + let py_map = m.downcast_bound::(py).unwrap(); assert!(py_map.len() == 1); assert!( @@ -143,7 +143,7 @@ mod tests { map.insert(1, 1); let m: PyObject = map.into_py(py); - let py_map: &PyDict = m.downcast(py).unwrap(); + let py_map = m.downcast_bound::(py).unwrap(); assert!(py_map.len() == 1); assert!( diff --git a/src/conversions/indexmap.rs b/src/conversions/indexmap.rs index ec7ed5de..5a8cd3d6 100644 --- a/src/conversions/indexmap.rs +++ b/src/conversions/indexmap.rs @@ -149,7 +149,7 @@ mod test_indexmap { map.insert(1, 1); let m = map.to_object(py); - let py_map: &PyDict = m.downcast(py).unwrap(); + let py_map = m.downcast_bound::(py).unwrap(); assert!(py_map.len() == 1); assert!( @@ -175,7 +175,7 @@ mod test_indexmap { map.insert(1, 1); let m: PyObject = map.into_py(py); - let py_map: &PyDict = m.downcast(py).unwrap(); + let py_map = m.downcast_bound::(py).unwrap(); assert!(py_map.len() == 1); assert!( diff --git a/src/conversions/std/array.rs b/src/conversions/std/array.rs index 016ab0a6..d901ff4e 100644 --- a/src/conversions/std/array.rs +++ b/src/conversions/std/array.rs @@ -239,7 +239,7 @@ mod tests { Python::with_gil(|py| { let array: [Foo; 8] = [Foo, Foo, Foo, Foo, Foo, Foo, Foo, Foo]; let pyobject = array.into_py(py); - let list: &PyList = pyobject.downcast(py).unwrap(); + let list = pyobject.downcast_bound::(py).unwrap(); let _cell: &crate::PyCell = list.get_item(4).unwrap().extract().unwrap(); }); } diff --git a/src/conversions/std/map.rs b/src/conversions/std/map.rs index 700617ec..8c6835d7 100644 --- a/src/conversions/std/map.rs +++ b/src/conversions/std/map.rs @@ -121,7 +121,7 @@ mod tests { map.insert(1, 1); let m = map.to_object(py); - let py_map: &PyDict = m.downcast(py).unwrap(); + let py_map = m.downcast_bound::(py).unwrap(); assert!(py_map.len() == 1); assert!( @@ -144,7 +144,7 @@ mod tests { map.insert(1, 1); let m = map.to_object(py); - let py_map: &PyDict = m.downcast(py).unwrap(); + let py_map = m.downcast_bound::(py).unwrap(); assert!(py_map.len() == 1); assert!( @@ -167,7 +167,7 @@ mod tests { map.insert(1, 1); let m: PyObject = map.into_py(py); - let py_map: &PyDict = m.downcast(py).unwrap(); + let py_map = m.downcast_bound::(py).unwrap(); assert!(py_map.len() == 1); assert!( @@ -189,7 +189,7 @@ mod tests { map.insert(1, 1); let m: PyObject = map.into_py(py); - let py_map: &PyDict = m.downcast(py).unwrap(); + let py_map = m.downcast_bound::(py).unwrap(); assert!(py_map.len() == 1); assert!( diff --git a/src/err/mod.rs b/src/err/mod.rs index 0c2637a7..cf74739d 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -62,6 +62,15 @@ impl<'a> PyDowncastError<'a> { to: to.into(), } } + + /// Compatibility API to convert the Bound variant `DowncastError` into the + /// gil-ref variant + pub(crate) fn from_downcast_err(DowncastError { from, to }: DowncastError<'a, 'a>) -> Self { + Self { + from: from.as_gil_ref(), + to, + } + } } /// Error that indicates a failure to convert a PyAny to a more specific Python type. diff --git a/src/instance.rs b/src/instance.rs index cb803cc6..821aad81 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -7,8 +7,8 @@ use crate::types::any::PyAnyMethods; use crate::types::string::PyStringMethods; use crate::types::{PyDict, PyString, PyTuple}; use crate::{ - ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyClass, PyClassInitializer, PyRef, PyRefMut, - PyTypeInfo, Python, ToPyObject, + ffi, AsPyPointer, DowncastError, FromPyObject, IntoPy, PyAny, PyClass, PyClassInitializer, + PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject, }; use crate::{gil, PyTypeCheck}; use std::marker::PhantomData; @@ -1686,6 +1686,23 @@ impl std::fmt::Debug for Py { pub type PyObject = Py; impl PyObject { + /// Deprecated form of [`PyObject::downcast_bound`] + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyObject::downcast` will be replaced by `PyObject::downcast_bound` in a future PyO3 version" + ) + )] + #[inline] + pub fn downcast<'py, T>(&'py self, py: Python<'py>) -> Result<&'py T, PyDowncastError<'py>> + where + T: PyTypeCheck, + { + self.downcast_bound::(py) + .map(Bound::as_gil_ref) + .map_err(PyDowncastError::from_downcast_err) + } /// Downcast this `PyObject` to a concrete Python type or pyclass. /// /// Note that you can often avoid downcasting yourself by just specifying @@ -1703,8 +1720,8 @@ impl PyObject { /// Python::with_gil(|py| { /// let any: PyObject = PyDict::new_bound(py).into(); /// - /// assert!(any.downcast::(py).is_ok()); - /// assert!(any.downcast::(py).is_err()); + /// assert!(any.downcast_bound::(py).is_ok()); + /// assert!(any.downcast_bound::(py).is_err()); /// }); /// ``` /// @@ -1725,9 +1742,9 @@ impl PyObject { /// Python::with_gil(|py| { /// let class: PyObject = Py::new(py, Class { i: 0 }).unwrap().into_py(py); /// - /// let class_cell: &PyCell = class.downcast(py)?; + /// let class_bound = class.downcast_bound::(py)?; /// - /// class_cell.borrow_mut().i += 1; + /// class_bound.borrow_mut().i += 1; /// /// // Alternatively you can get a `PyRefMut` directly /// let class_ref: PyRefMut<'_, Class> = class.extract(py)?; @@ -1737,11 +1754,34 @@ impl PyObject { /// # } /// ``` #[inline] - pub fn downcast<'py, T>(&'py self, py: Python<'py>) -> Result<&'py T, PyDowncastError<'py>> + pub fn downcast_bound<'py, T>( + &self, + py: Python<'py>, + ) -> Result<&Bound<'py, T>, DowncastError<'_, 'py>> where - T: PyTypeCheck, + T: PyTypeCheck, { - self.as_ref(py).downcast() + self.bind(py).downcast() + } + + /// Deprecated form of [`PyObject::downcast_bound_unchecked`] + /// + /// # Safety + /// + /// Callers must ensure that the type is valid or risk type confusion. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyObject::downcast_unchecked` will be replaced by `PyObject::downcast_bound_unchecked` in a future PyO3 version" + ) + )] + #[inline] + pub unsafe fn downcast_unchecked<'py, T>(&'py self, py: Python<'py>) -> &T + where + T: HasPyGilRef, + { + self.downcast_bound_unchecked::(py).as_gil_ref() } /// Casts the PyObject to a concrete Python object type without checking validity. @@ -1750,11 +1790,8 @@ impl PyObject { /// /// Callers must ensure that the type is valid or risk type confusion. #[inline] - pub unsafe fn downcast_unchecked<'p, T>(&'p self, py: Python<'p>) -> &T - where - T: HasPyGilRef, - { - self.as_ref(py).downcast_unchecked() + pub unsafe fn downcast_bound_unchecked<'py, T>(&self, py: Python<'py>) -> &Bound<'py, T> { + self.bind(py).downcast_unchecked() } } diff --git a/src/marker.rs b/src/marker.rs index fc84ca54..5321eddb 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -826,6 +826,13 @@ impl<'py> Python<'py> { } /// Registers the object in the release pool, and tries to downcast to specific type. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "part of the deprecated GIL Ref API; to migrate use `obj.downcast_bound::(py)` instead of `py.checked_cast_as::(obj)`" + ) + )] pub fn checked_cast_as(self, obj: PyObject) -> Result<&'py T, PyDowncastError<'py>> where T: PyTypeCheck, @@ -839,6 +846,13 @@ impl<'py> Python<'py> { /// # Safety /// /// Callers must ensure that ensure that the cast is valid. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "part of the deprecated GIL Ref API; to migrate use `obj.downcast_bound_unchecked::(py)` instead of `py.cast_as::(obj)`" + ) + )] pub unsafe fn cast_as(self, obj: PyObject) -> &'py T where T: HasPyGilRef, diff --git a/src/types/mapping.rs b/src/types/mapping.rs index 81bb2e63..86c605f8 100644 --- a/src/types/mapping.rs +++ b/src/types/mapping.rs @@ -299,13 +299,13 @@ mod tests { Python::with_gil(|py| { let mut v = HashMap::new(); let ob = v.to_object(py); - let mapping: &PyMapping = ob.downcast(py).unwrap(); + let mapping = ob.downcast_bound::(py).unwrap(); assert_eq!(0, mapping.len().unwrap()); assert!(mapping.is_empty().unwrap()); v.insert(7, 32); let ob = v.to_object(py); - let mapping2: &PyMapping = ob.downcast(py).unwrap(); + let mapping2 = ob.downcast_bound::(py).unwrap(); assert_eq!(1, mapping2.len().unwrap()); assert!(!mapping2.is_empty().unwrap()); }); @@ -317,7 +317,7 @@ mod tests { let mut v = HashMap::new(); v.insert("key0", 1234); let ob = v.to_object(py); - let mapping: &PyMapping = ob.downcast(py).unwrap(); + let mapping = ob.downcast_bound::(py).unwrap(); mapping.set_item("key1", "foo").unwrap(); assert!(mapping.contains("key0").unwrap()); @@ -332,7 +332,7 @@ mod tests { let mut v = HashMap::new(); v.insert(7, 32); let ob = v.to_object(py); - let mapping: &PyMapping = ob.downcast(py).unwrap(); + let mapping = ob.downcast_bound::(py).unwrap(); assert_eq!( 32, mapping.get_item(7i32).unwrap().extract::().unwrap() @@ -350,7 +350,7 @@ mod tests { let mut v = HashMap::new(); v.insert(7, 32); let ob = v.to_object(py); - let mapping: &PyMapping = ob.downcast(py).unwrap(); + let mapping = ob.downcast_bound::(py).unwrap(); assert!(mapping.set_item(7i32, 42i32).is_ok()); // change assert!(mapping.set_item(8i32, 123i32).is_ok()); // insert assert_eq!( @@ -370,7 +370,7 @@ mod tests { let mut v = HashMap::new(); v.insert(7, 32); let ob = v.to_object(py); - let mapping: &PyMapping = ob.downcast(py).unwrap(); + let mapping = ob.downcast_bound::(py).unwrap(); assert!(mapping.del_item(7i32).is_ok()); assert_eq!(0, mapping.len().unwrap()); assert!(mapping @@ -388,12 +388,12 @@ mod tests { v.insert(8, 42); v.insert(9, 123); let ob = v.to_object(py); - let mapping: &PyMapping = ob.downcast(py).unwrap(); + let mapping = ob.downcast_bound::(py).unwrap(); // Can't just compare against a vector of tuples since we don't have a guaranteed ordering. let mut key_sum = 0; let mut value_sum = 0; for el in mapping.items().unwrap().iter().unwrap() { - let tuple = el.unwrap().downcast::().unwrap(); + let tuple = el.unwrap().downcast_into::().unwrap(); key_sum += tuple.get_item(0).unwrap().extract::().unwrap(); value_sum += tuple.get_item(1).unwrap().extract::().unwrap(); } @@ -410,7 +410,7 @@ mod tests { v.insert(8, 42); v.insert(9, 123); let ob = v.to_object(py); - let mapping: &PyMapping = ob.downcast(py).unwrap(); + let mapping = ob.downcast_bound::(py).unwrap(); // Can't just compare against a vector of tuples since we don't have a guaranteed ordering. let mut key_sum = 0; for el in mapping.keys().unwrap().iter().unwrap() { @@ -428,7 +428,7 @@ mod tests { v.insert(8, 42); v.insert(9, 123); let ob = v.to_object(py); - let mapping: &PyMapping = ob.downcast(py).unwrap(); + let mapping = ob.downcast_bound::(py).unwrap(); // Can't just compare against a vector of tuples since we don't have a guaranteed ordering. let mut values_sum = 0; for el in mapping.values().unwrap().iter().unwrap() { diff --git a/src/types/none.rs b/src/types/none.rs index 6460482e..a14af044 100644 --- a/src/types/none.rs +++ b/src/types/none.rs @@ -103,7 +103,7 @@ mod tests { #[test] fn test_unit_to_object_is_none() { Python::with_gil(|py| { - assert!(().to_object(py).downcast::(py).is_ok()); + assert!(().to_object(py).downcast_bound::(py).is_ok()); }) } @@ -111,7 +111,7 @@ mod tests { fn test_unit_into_py_is_none() { Python::with_gil(|py| { let obj: PyObject = ().into_py(py); - assert!(obj.downcast::(py).is_ok()); + assert!(obj.downcast_bound::(py).is_ok()); }) } diff --git a/src/types/tuple.rs b/src/types/tuple.rs index a8737c25..e41fcbf0 100644 --- a/src/types/tuple.rs +++ b/src/types/tuple.rs @@ -169,7 +169,7 @@ impl PyTuple { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| -> PyResult<()> { /// let ob = (1, 2, 3).to_object(py); - /// let tuple: &PyTuple = ob.downcast(py).unwrap(); + /// let tuple = ob.downcast_bound::(py).unwrap(); /// let obj = tuple.get_item(0); /// assert_eq!(obj.unwrap().extract::().unwrap(), 1); /// Ok(()) @@ -273,7 +273,7 @@ pub trait PyTupleMethods<'py> { /// # fn main() -> PyResult<()> { /// Python::with_gil(|py| -> PyResult<()> { /// let ob = (1, 2, 3).to_object(py); - /// let tuple: &PyTuple = ob.downcast(py).unwrap(); + /// let tuple = ob.downcast_bound::(py).unwrap(); /// let obj = tuple.get_item(0); /// assert_eq!(obj.unwrap().extract::().unwrap(), 1); /// Ok(())