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 {} impl pyo3::types::DerefToPyAny for MyClass {}
# #[allow(deprecated)]
unsafe impl pyo3::type_object::HasPyGilRef for MyClass { unsafe impl pyo3::type_object::HasPyGilRef for MyClass {
type AsRefTarget = pyo3::PyCell<Self>; 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); let obj: &PyAny = Py::new(py, MyClass {})?.into_ref(py);
// To &PyCell<MyClass> with PyAny::downcast // To &PyCell<MyClass> with PyAny::downcast
# #[allow(deprecated)]
let _: &PyCell<MyClass> = obj.downcast()?; let _: &PyCell<MyClass> = obj.downcast()?;
// To Py<PyAny> (aka PyObject) with .into() // To Py<PyAny> (aka PyObject) with .into()

View File

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

View File

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

View File

@ -240,7 +240,7 @@ mod tests {
let array: [Foo; 8] = [Foo, Foo, Foo, Foo, Foo, Foo, Foo, Foo]; let array: [Foo; 8] = [Foo, Foo, Foo, Foo, Foo, Foo, Foo, Foo];
let pyobject = array.into_py(py); let pyobject = array.into_py(py);
let list = pyobject.downcast_bound::<PyList>(py).unwrap(); 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::pyclass::boolean_struct::False;
use crate::types::{any::PyAnyMethods, PyModule, PyType}; use crate::types::{any::PyAnyMethods, PyModule, PyType};
use crate::{ use crate::{
ffi, Borrowed, Bound, DowncastError, Py, PyAny, PyCell, PyClass, PyClassInitializer, PyErr, ffi, Borrowed, Bound, DowncastError, Py, PyAny, PyClass, PyClassInitializer, PyErr, PyObject,
PyObject, PyRef, PyRefMut, PyResult, PyTraverseError, PyTypeCheck, PyVisit, Python, PyRef, PyRefMut, PyResult, PyTraverseError, PyTypeCheck, PyVisit, Python,
}; };
use std::borrow::Cow; use std::borrow::Cow;
use std::ffi::CStr; 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] #[inline]
fn from(bound: BoundRef<'a, 'py, T>) -> Self { fn from(bound: BoundRef<'a, 'py, T>) -> Self {
bound.0.as_gil_ref() bound.0.as_gil_ref()

View File

@ -1,6 +1,6 @@
use crate::err::{self, PyDowncastError, PyErr, PyResult}; use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::impl_::pycell::PyClassObject; 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::pyclass::boolean_struct::{False, True};
use crate::type_object::HasPyGilRef; use crate::type_object::HasPyGilRef;
use crate::types::{any::PyAnyMethods, string::PyStringMethods, typeobject::PyTypeMethods}; 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>` // `&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 where
T: PyClass, T: PyClass,
{ {
fn from(cell: &PyCell<T>) -> Self { fn from(cell: &crate::PyCell<T>) -> Self {
cell.as_borrowed().to_owned().unbind() 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::gil::{prepare_freethreaded_python, with_embedded_python_interpreter};
pub use crate::instance::{Borrowed, Bound, Py, PyNativeType, PyObject}; pub use crate::instance::{Borrowed, Bound, Py, PyNativeType, PyObject};
pub use crate::marker::Python; 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::PyClass;
pub use crate::pyclass_init::PyClassInitializer; pub use crate::pyclass_init::PyClassInitializer;
pub use crate::type_object::{PyTypeCheck, PyTypeInfo}; 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::err::{PyErr, PyResult};
pub use crate::instance::{Borrowed, Bound, Py, PyObject}; pub use crate::instance::{Borrowed, Bound, Py, PyObject};
pub use crate::marker::Python; 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::pyclass_init::PyClassInitializer;
pub use crate::types::{PyAny, PyModule}; pub use crate::types::{PyAny, PyModule};
pub use crate::PyNativeType; pub use crate::PyNativeType;

View File

@ -154,6 +154,7 @@
//! # pub struct Number { //! # pub struct Number {
//! # inner: u32, //! # inner: u32,
//! # } //! # }
//! # #[allow(deprecated)]
//! #[pyfunction] //! #[pyfunction]
//! fn swap_numbers(a: &PyCell<Number>, b: &PyCell<Number>) { //! fn swap_numbers(a: &PyCell<Number>, b: &PyCell<Number>) {
//! // Check that the pointers are unequal //! // 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 /// For more information on how, when and why (not) to use `PyCell` please see the
/// [module-level documentation](self). /// [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)] #[repr(transparent)]
pub struct PyCell<T: PyClassImpl>(PyClassObject<T>); pub struct PyCell<T: PyClassImpl>(PyClassObject<T>);
#[allow(deprecated)]
unsafe impl<T: PyClass> PyNativeType for PyCell<T> { unsafe impl<T: PyClass> PyNativeType for PyCell<T> {
type AsRefSource = T; type AsRefSource = T;
} }
#[allow(deprecated)]
impl<T: PyClass> PyCell<T> { impl<T: PyClass> PyCell<T> {
/// Makes a new `PyCell` on the Python heap and return the reference to it. /// 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> {} unsafe impl<T: PyClassImpl> PyLayout<T> for PyCell<T> {}
#[allow(deprecated)]
impl<T: PyClass> PySizedLayout<T> for PyCell<T> {} impl<T: PyClass> PySizedLayout<T> for PyCell<T> {}
#[allow(deprecated)]
impl<T> PyTypeCheck for PyCell<T> impl<T> PyTypeCheck for PyCell<T>
where where
T: PyClass, T: PyClass,
@ -492,18 +505,21 @@ where
} }
} }
#[allow(deprecated)]
unsafe impl<T: PyClass> AsPyPointer for PyCell<T> { unsafe impl<T: PyClass> AsPyPointer for PyCell<T> {
fn as_ptr(&self) -> *mut ffi::PyObject { fn as_ptr(&self) -> *mut ffi::PyObject {
(self as *const _) as *mut _ (self as *const _) as *mut _
} }
} }
#[allow(deprecated)]
impl<T: PyClass> ToPyObject for &PyCell<T> { impl<T: PyClass> ToPyObject for &PyCell<T> {
fn to_object(&self, py: Python<'_>) -> PyObject { fn to_object(&self, py: Python<'_>) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) } unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
} }
} }
#[allow(deprecated)]
impl<T: PyClass> AsRef<PyAny> for PyCell<T> { impl<T: PyClass> AsRef<PyAny> for PyCell<T> {
fn as_ref(&self) -> &PyAny { fn as_ref(&self) -> &PyAny {
#[allow(deprecated)] #[allow(deprecated)]
@ -513,6 +529,7 @@ impl<T: PyClass> AsRef<PyAny> for PyCell<T> {
} }
} }
#[allow(deprecated)]
impl<T: PyClass> Deref for PyCell<T> { impl<T: PyClass> Deref for PyCell<T> {
type Target = PyAny; 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> { impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.try_borrow() { 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> { impl<'a, T: PyClass> std::convert::TryFrom<&'a PyCell<T>> for crate::PyRef<'a, T> {
type Error = PyBorrowError; type Error = PyBorrowError;
fn try_from(cell: &'a crate::PyCell<T>) -> Result<Self, Self::Error> { 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>> impl<'a, T: PyClass<Frozen = False>> std::convert::TryFrom<&'a PyCell<T>>
for crate::PyRefMut<'a, T> for crate::PyRefMut<'a, T>
{ {

View File

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

View File

@ -62,7 +62,7 @@ impl PySuper {
/// (SubClass {}, BaseClass::new()) /// (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()?; /// let super_ = self_.py_super()?;
/// super_.call_method("method", (), None) /// super_.call_method("method", (), None)
/// } /// }

View File

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

View File

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

View File

@ -18,15 +18,18 @@ struct Reader {
#[pymethods] #[pymethods]
impl Reader { 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 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 slf
} }
fn get_iter(slf: &PyCell<Self>, keys: Py<PyBytes>) -> Iter { fn get_iter(slf: &Bound<'_, Self>, keys: Py<PyBytes>) -> Iter {
Iter { Iter {
reader: slf.into(), reader: slf.clone().unbind(),
keys, keys,
idx: 0, idx: 0,
} }

View File

@ -29,14 +29,14 @@ impl SubClass {
(SubClass {}, BaseClass::new()) (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()?; let super_ = self_.py_super()?;
super_.call_method("method", (), None) 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))] #[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) super_.call_method("method", (), None)
} }
} }

View File

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

View File

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

View File

@ -67,11 +67,11 @@ error[E0271]: type mismatch resolving `<MutableBase as PyClass>::Frozen == True`
33 | class.get(); 33 | class.get();
| ^^^ expected `True`, found `False` | ^^^ expected `True`, found `False`
| |
note: required by a bound in `pyo3::PyCell::<T>::get` note: required by a bound in `pyo3::Bound::<'py, T>::get`
--> src/pycell.rs --> src/instance.rs
| |
| pub fn get(&self) -> &T | pub fn get(&self) -> &T
| --- required by a bound in this associated function | --- required by a bound in this associated function
| where | where
| T: PyClass<Frozen = True> + Sync, | 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() { fn main() {
let obj = Python::with_gil(|py| { let obj = Python::with_gil(|py| {
PyCell::new(py, NotThreadSafe { data: Rc::new(5) }) Bound::new(py, NotThreadSafe { data: Rc::new(5) })
.unwrap() .unwrap()
.to_object(py) .unbind(py)
}); });
std::thread::spawn(move || { std::thread::spawn(move || {
Python::with_gil(|py| { Python::with_gil(|py| {
// Uh oh, moved Rc to a new thread! // 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); assert_eq!(*c.borrow().data, 5);
}) })