deprecate the use of `PyCell` in favor of `Bound` and `Py` (#3916)

* deprecate the use of `PyCell` in favor of `Bound` and `Py`

* update `FromPyObject` for `T: PyClass + Clone` impl

* move `PyCell` deprecation to the `gil-refs` feature gate and add a migration note
This commit is contained in:
Icxolu 2024-03-03 15:47:25 +01:00 committed by GitHub
parent 00eb014623
commit 70a7aa808d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 77 additions and 45 deletions

View File

@ -1230,6 +1230,7 @@ struct MyClass {
impl pyo3::types::DerefToPyAny for MyClass {}
# #[allow(deprecated)]
unsafe impl pyo3::type_object::HasPyGilRef for MyClass {
type AsRefTarget = pyo3::PyCell<Self>;
}

View File

@ -96,6 +96,7 @@ For a `&PyAny` object reference `any` where the underlying object is a `#[pyclas
let obj: &PyAny = Py::new(py, MyClass {})?.into_ref(py);
// To &PyCell<MyClass> with PyAny::downcast
# #[allow(deprecated)]
let _: &PyCell<MyClass> = obj.downcast()?;
// To Py<PyAny> (aka PyObject) with .into()

View File

@ -1281,6 +1281,7 @@ fn impl_pytypeinfo(
};
quote! {
#[allow(deprecated)]
unsafe impl _pyo3::type_object::HasPyGilRef for #cls {
type AsRefTarget = _pyo3::PyCell<Self>;
}

View File

@ -6,9 +6,7 @@ use crate::pyclass::boolean_struct::False;
use crate::type_object::PyTypeInfo;
use crate::types::any::PyAnyMethods;
use crate::types::PyTuple;
use crate::{
ffi, gil, Bound, Py, PyAny, PyCell, PyClass, PyNativeType, PyObject, PyRef, PyRefMut, Python,
};
use crate::{ffi, gil, Bound, Py, PyAny, PyClass, PyNativeType, PyObject, PyRef, PyRefMut, Python};
use std::ptr::NonNull;
/// Returns a borrowed pointer to a Python object.
@ -265,7 +263,8 @@ where
}
}
impl<'py, T> FromPyObject<'py> for &'py PyCell<T>
#[allow(deprecated)]
impl<'py, T> FromPyObject<'py> for &'py crate::PyCell<T>
where
T: PyClass,
{
@ -278,9 +277,9 @@ impl<T> FromPyObject<'_> for T
where
T: PyClass + Clone,
{
fn extract(obj: &PyAny) -> PyResult<Self> {
let cell: &PyCell<Self> = obj.downcast()?;
Ok(unsafe { cell.try_borrow_unguarded()?.clone() })
fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
let bound = obj.downcast::<Self>()?;
Ok(bound.try_borrow()?.clone())
}
}
@ -389,7 +388,7 @@ mod implementations {
}
}
impl<'v, T> PyTryFrom<'v> for PyCell<T>
impl<'v, T> PyTryFrom<'v> for crate::PyCell<T>
where
T: 'v + PyClass,
{

View File

@ -240,7 +240,7 @@ mod tests {
let array: [Foo; 8] = [Foo, Foo, Foo, Foo, Foo, Foo, Foo, Foo];
let pyobject = array.into_py(py);
let list = pyobject.downcast_bound::<PyList>(py).unwrap();
let _cell: &crate::PyCell<Foo> = list.get_item(4).unwrap().extract().unwrap();
let _bound = list.get_item(4).unwrap().downcast::<Foo>().unwrap();
});
}

View File

@ -7,8 +7,8 @@ use crate::pycell::{PyBorrowError, PyBorrowMutError};
use crate::pyclass::boolean_struct::False;
use crate::types::{any::PyAnyMethods, PyModule, PyType};
use crate::{
ffi, Borrowed, Bound, DowncastError, Py, PyAny, PyCell, PyClass, PyClassInitializer, PyErr,
PyObject, PyRef, PyRefMut, PyResult, PyTraverseError, PyTypeCheck, PyVisit, Python,
ffi, Borrowed, Bound, DowncastError, Py, PyAny, PyClass, PyClassInitializer, PyErr, PyObject,
PyRef, PyRefMut, PyResult, PyTraverseError, PyTypeCheck, PyVisit, Python,
};
use std::borrow::Cow;
use std::ffi::CStr;
@ -518,7 +518,8 @@ impl<'a> From<BoundRef<'a, 'a, PyModule>> for &'a PyModule {
}
}
impl<'a, 'py, T: PyClass> From<BoundRef<'a, 'py, T>> for &'a PyCell<T> {
#[allow(deprecated)]
impl<'a, 'py, T: PyClass> From<BoundRef<'a, 'py, T>> for &'a crate::PyCell<T> {
#[inline]
fn from(bound: BoundRef<'a, 'py, T>) -> Self {
bound.0.as_gil_ref()

View File

@ -1,6 +1,6 @@
use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::impl_::pycell::PyClassObject;
use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell};
use crate::pycell::{PyBorrowError, PyBorrowMutError};
use crate::pyclass::boolean_struct::{False, True};
use crate::type_object::HasPyGilRef;
use crate::types::{any::PyAnyMethods, string::PyStringMethods, typeobject::PyTypeMethods};
@ -1698,11 +1698,12 @@ impl<T> std::convert::From<Bound<'_, T>> for Py<T> {
}
// `&PyCell<T>` can be converted to `Py<T>`
impl<T> std::convert::From<&PyCell<T>> for Py<T>
#[allow(deprecated)]
impl<T> std::convert::From<&crate::PyCell<T>> for Py<T>
where
T: PyClass,
{
fn from(cell: &PyCell<T>) -> Self {
fn from(cell: &crate::PyCell<T>) -> Self {
cell.as_borrowed().to_owned().unbind()
}
}

View File

@ -315,7 +315,9 @@ pub use crate::gil::GILPool;
pub use crate::gil::{prepare_freethreaded_python, with_embedded_python_interpreter};
pub use crate::instance::{Borrowed, Bound, Py, PyNativeType, PyObject};
pub use crate::marker::Python;
pub use crate::pycell::{PyCell, PyRef, PyRefMut};
#[allow(deprecated)]
pub use crate::pycell::PyCell;
pub use crate::pycell::{PyRef, PyRefMut};
pub use crate::pyclass::PyClass;
pub use crate::pyclass_init::PyClassInitializer;
pub use crate::type_object::{PyTypeCheck, PyTypeInfo};

View File

@ -14,7 +14,9 @@ pub use crate::conversion::{PyTryFrom, PyTryInto};
pub use crate::err::{PyErr, PyResult};
pub use crate::instance::{Borrowed, Bound, Py, PyObject};
pub use crate::marker::Python;
pub use crate::pycell::{PyCell, PyRef, PyRefMut};
#[allow(deprecated)]
pub use crate::pycell::PyCell;
pub use crate::pycell::{PyRef, PyRefMut};
pub use crate::pyclass_init::PyClassInitializer;
pub use crate::types::{PyAny, PyModule};
pub use crate::PyNativeType;

View File

@ -62,7 +62,7 @@
//! ) -> *mut pyo3::ffi::PyObject {
//! use :: pyo3 as _pyo3;
//! _pyo3::impl_::trampoline::noargs(_slf, _args, |py, _slf| {
//! # #[allow(deprecated)]
//! # #[allow(deprecated)]
//! let _cell = py
//! .from_borrowed_ptr::<_pyo3::PyAny>(_slf)
//! .downcast::<_pyo3::PyCell<Number>>()?;
@ -154,6 +154,7 @@
//! # pub struct Number {
//! # inner: u32,
//! # }
//! # #[allow(deprecated)]
//! #[pyfunction]
//! fn swap_numbers(a: &PyCell<Number>, b: &PyCell<Number>) {
//! // Check that the pointers are unequal
@ -250,13 +251,22 @@ use self::impl_::{PyClassObject, PyClassObjectLayout};
/// ```
/// For more information on how, when and why (not) to use `PyCell` please see the
/// [module-level documentation](self).
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyCell` was merged into `Bound`, use that instead; see the migration guide for more info"
)
)]
#[repr(transparent)]
pub struct PyCell<T: PyClassImpl>(PyClassObject<T>);
#[allow(deprecated)]
unsafe impl<T: PyClass> PyNativeType for PyCell<T> {
type AsRefSource = T;
}
#[allow(deprecated)]
impl<T: PyClass> PyCell<T> {
/// Makes a new `PyCell` on the Python heap and return the reference to it.
///
@ -478,9 +488,12 @@ impl<T: PyClass> PyCell<T> {
}
}
#[allow(deprecated)]
unsafe impl<T: PyClassImpl> PyLayout<T> for PyCell<T> {}
#[allow(deprecated)]
impl<T: PyClass> PySizedLayout<T> for PyCell<T> {}
#[allow(deprecated)]
impl<T> PyTypeCheck for PyCell<T>
where
T: PyClass,
@ -492,18 +505,21 @@ where
}
}
#[allow(deprecated)]
unsafe impl<T: PyClass> AsPyPointer for PyCell<T> {
fn as_ptr(&self) -> *mut ffi::PyObject {
(self as *const _) as *mut _
}
}
#[allow(deprecated)]
impl<T: PyClass> ToPyObject for &PyCell<T> {
fn to_object(&self, py: Python<'_>) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
}
}
#[allow(deprecated)]
impl<T: PyClass> AsRef<PyAny> for PyCell<T> {
fn as_ref(&self) -> &PyAny {
#[allow(deprecated)]
@ -513,6 +529,7 @@ impl<T: PyClass> AsRef<PyAny> for PyCell<T> {
}
}
#[allow(deprecated)]
impl<T: PyClass> Deref for PyCell<T> {
type Target = PyAny;
@ -524,6 +541,7 @@ impl<T: PyClass> Deref for PyCell<T> {
}
}
#[allow(deprecated)]
impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.try_borrow() {
@ -748,6 +766,7 @@ impl<T: PyClass> IntoPy<PyObject> for &'_ PyRef<'_, T> {
}
}
#[allow(deprecated)]
impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell<T>> for crate::PyRef<'a, T> {
type Error = PyBorrowError;
fn try_from(cell: &'a crate::PyCell<T>) -> Result<Self, Self::Error> {
@ -905,6 +924,7 @@ unsafe impl<'a, T: PyClass<Frozen = False>> AsPyPointer for PyRefMut<'a, T> {
}
}
#[allow(deprecated)]
impl<'a, T: PyClass<Frozen = False>> std::convert::TryFrom<&'a PyCell<T>>
for crate::PyRefMut<'a, T>
{

View File

@ -1,7 +1,7 @@
//! `PyClass` and related traits.
use crate::{
callback::IntoPyCallbackOutput, ffi, impl_::pyclass::PyClassImpl, IntoPy, PyCell, PyObject,
PyResult, PyTypeInfo, Python,
callback::IntoPyCallbackOutput, ffi, impl_::pyclass::PyClassImpl, IntoPy, PyObject, PyResult,
PyTypeInfo, Python,
};
use std::{cmp::Ordering, os::raw::c_int};
@ -15,7 +15,8 @@ pub use self::gc::{PyTraverseError, PyVisit};
///
/// The `#[pyclass]` attribute implements this trait for your Rust struct -
/// you shouldn't implement this trait directly.
pub trait PyClass: PyTypeInfo<AsRefTarget = PyCell<Self>> + PyClassImpl {
#[allow(deprecated)]
pub trait PyClass: PyTypeInfo<AsRefTarget = crate::PyCell<Self>> + PyClassImpl {
/// Whether the pyclass is frozen.
///
/// This can be enabled via `#[pyclass(frozen)]`.

View File

@ -62,7 +62,7 @@ impl PySuper {
/// (SubClass {}, BaseClass::new())
/// }
///
/// fn method(self_: &PyCell<Self>) -> PyResult<&PyAny> {
/// fn method<'py>(self_: &Bound<'py, Self>) -> PyResult<Bound<'py, PyAny>> {
/// let super_ = self_.py_super()?;
/// super_.call_method("method", (), None)
/// }

View File

@ -24,11 +24,11 @@ struct TestBufferClass {
#[pymethods]
impl TestBufferClass {
unsafe fn __getbuffer__(
slf: &PyCell<Self>,
slf: Bound<'_, Self>,
view: *mut ffi::Py_buffer,
flags: c_int,
) -> PyResult<()> {
fill_view_from_readonly_data(view, flags, &slf.borrow().vec, slf)
fill_view_from_readonly_data(view, flags, &slf.borrow().vec, slf.into_any())
}
unsafe fn __releasebuffer__(&self, view: *mut ffi::Py_buffer) {
@ -105,12 +105,12 @@ fn test_releasebuffer_unraisable_error() {
#[pymethods]
impl ReleaseBufferError {
unsafe fn __getbuffer__(
slf: &PyCell<Self>,
slf: Bound<'_, Self>,
view: *mut ffi::Py_buffer,
flags: c_int,
) -> PyResult<()> {
static BUF_BYTES: &[u8] = b"hello world";
fill_view_from_readonly_data(view, flags, BUF_BYTES, slf)
fill_view_from_readonly_data(view, flags, BUF_BYTES, slf.into_any())
}
unsafe fn __releasebuffer__(&self, _view: *mut ffi::Py_buffer) -> PyResult<()> {
@ -145,7 +145,7 @@ unsafe fn fill_view_from_readonly_data(
view: *mut ffi::Py_buffer,
flags: c_int,
data: &[u8],
owner: &PyAny,
owner: Bound<'_, PyAny>,
) -> PyResult<()> {
if view.is_null() {
return Err(PyBufferError::new_err("View is null"));

View File

@ -23,7 +23,7 @@ fn empty_class_with_new() {
assert!(typeobj
.call((), None)
.unwrap()
.downcast::<PyCell<EmptyClassWithNew>>()
.downcast::<EmptyClassWithNew>()
.is_ok());
// Calling with arbitrary args or kwargs is not ok
@ -52,7 +52,7 @@ fn unit_class_with_new() {
assert!(typeobj
.call((), None)
.unwrap()
.downcast::<PyCell<UnitClassWithNew>>()
.downcast::<UnitClassWithNew>()
.is_ok());
});
}

View File

@ -18,15 +18,18 @@ struct Reader {
#[pymethods]
impl Reader {
fn clone_ref(slf: &PyCell<Self>) -> &PyCell<Self> {
fn clone_ref<'a, 'py>(slf: &'a Bound<'py, Self>) -> &'a Bound<'py, Self> {
slf
}
fn clone_ref_with_py<'py>(slf: &'py PyCell<Self>, _py: Python<'py>) -> &'py PyCell<Self> {
fn clone_ref_with_py<'a, 'py>(
slf: &'a Bound<'py, Self>,
_py: Python<'py>,
) -> &'a Bound<'py, Self> {
slf
}
fn get_iter(slf: &PyCell<Self>, keys: Py<PyBytes>) -> Iter {
fn get_iter(slf: &Bound<'_, Self>, keys: Py<PyBytes>) -> Iter {
Iter {
reader: slf.into(),
reader: slf.clone().unbind(),
keys,
idx: 0,
}

View File

@ -29,14 +29,14 @@ impl SubClass {
(SubClass {}, BaseClass::new())
}
fn method(self_: &PyCell<Self>) -> PyResult<&PyAny> {
fn method<'py>(self_: &Bound<'py, Self>) -> PyResult<Bound<'py, PyAny>> {
let super_ = self_.py_super()?;
super_.call_method("method", (), None)
}
fn method_super_new(self_: &PyCell<Self>) -> PyResult<&PyAny> {
fn method_super_new<'py>(self_: &Bound<'py, Self>) -> PyResult<Bound<'py, PyAny>> {
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
let super_ = PySuper::new(self_.get_type(), self_)?;
let super_ = PySuper::new_bound(&self_.get_type(), self_)?;
super_.call_method("method", (), None)
}
}

View File

@ -367,7 +367,7 @@ fn test_methods() {
let _ = a;
}
#[pyo3(text_signature = "($self, b)")]
fn pyself_method(_this: &PyCell<Self>, b: i32) {
fn pyself_method(_this: &Bound<'_, Self>, b: i32) {
let _ = b;
}
#[classmethod]

View File

@ -124,7 +124,7 @@ impl PickleSupport {
}
pub fn __reduce__<'py>(
slf: &'py PyCell<Self>,
slf: &Bound<'py, Self>,
py: Python<'py>,
) -> PyResult<(PyObject, Bound<'py, PyTuple>, PyObject)> {
let cls = slf.to_object(py).getattr(py, "__class__")?;

View File

@ -29,7 +29,7 @@ fn py_get_of_mutable_class_fails(class: Py<MutableBase>) {
class.get();
}
fn pyclass_get_of_mutable_class_fails(class: &PyCell<MutableBase>) {
fn pyclass_get_of_mutable_class_fails(class: &Bound<'_, MutableBase>) {
class.get();
}

View File

@ -67,11 +67,11 @@ error[E0271]: type mismatch resolving `<MutableBase as PyClass>::Frozen == True`
33 | class.get();
| ^^^ expected `True`, found `False`
|
note: required by a bound in `pyo3::PyCell::<T>::get`
--> src/pycell.rs
note: required by a bound in `pyo3::Bound::<'py, T>::get`
--> src/instance.rs
|
| pub fn get(&self) -> &T
| --- required by a bound in this associated function
| where
| T: PyClass<Frozen = True> + Sync,
| ^^^^^^^^^^^^^ required by this bound in `PyCell::<T>::get`
| ^^^^^^^^^^^^^ required by this bound in `Bound::<'py, T>::get`

View File

@ -8,15 +8,15 @@ struct NotThreadSafe {
fn main() {
let obj = Python::with_gil(|py| {
PyCell::new(py, NotThreadSafe { data: Rc::new(5) })
Bound::new(py, NotThreadSafe { data: Rc::new(5) })
.unwrap()
.to_object(py)
.unbind(py)
});
std::thread::spawn(move || {
Python::with_gil(|py| {
// Uh oh, moved Rc to a new thread!
let c: &PyCell<NotThreadSafe> = obj.as_ref(py).downcast().unwrap();
let c = obj.bind(py).downcast::<NotThreadSafe>().unwrap();
assert_eq!(*c.borrow().data, 5);
})