diff --git a/newsfragments/3785.added.md b/newsfragments/3785.added.md new file mode 100644 index 00000000000..6af3bb999f8 --- /dev/null +++ b/newsfragments/3785.added.md @@ -0,0 +1 @@ +Add `Py::as_any` and `Py::into_any`. diff --git a/src/instance.rs b/src/instance.rs index 4dffe67f9b2..627c5e678f2 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -11,7 +11,7 @@ use crate::{ }; use crate::{gil, PyTypeCheck}; use std::marker::PhantomData; -use std::mem::{self, ManuallyDrop}; +use std::mem::ManuallyDrop; use std::ops::Deref; use std::ptr::NonNull; @@ -35,10 +35,15 @@ pub unsafe trait PyNativeType: Sized { /// /// This is available as a migration tool to adjust code from the deprecated "GIL Refs" /// API to the `Bound` smart pointer API. + #[inline] fn as_borrowed(&self) -> Borrowed<'_, '_, Self::AsRefSource> { // Safety: &'py Self is expected to be a Python pointer, // so has the same layout as Borrowed<'py, 'py, T> - unsafe { std::mem::transmute(self) } + Borrowed( + unsafe { NonNull::new_unchecked(self as *const Self as *mut _) }, + PhantomData, + self.py(), + ) } /// Returns a GIL marker constrained to the lifetime of this type. @@ -96,14 +101,6 @@ impl<'py> Bound<'py, PyAny> { } } -impl<'py, T> Bound<'py, T> { - /// Helper to cast to Bound<'py, PyAny> - pub(crate) fn as_any(&self) -> &Bound<'py, PyAny> { - // Safety: all Bound have the same memory layout, and all Bound are valid Bound - unsafe { std::mem::transmute(self) } - } -} - impl<'py, T> std::fmt::Debug for Bound<'py, T> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { let any = self.as_any(); @@ -150,25 +147,29 @@ impl<'py, T> AsRef> for Bound<'py, T> where T: AsRef, { + #[inline] fn as_ref(&self) -> &Bound<'py, PyAny> { self.as_any() } } impl Clone for Bound<'_, T> { + #[inline] fn clone(&self) -> Self { Self(self.0, ManuallyDrop::new(self.1.clone_ref(self.0))) } } impl Drop for Bound<'_, T> { + #[inline] fn drop(&mut self) { - unsafe { ffi::Py_DECREF(self.1.as_ptr()) } + unsafe { ffi::Py_DECREF(self.as_ptr()) } } } impl<'py, T> Bound<'py, T> { /// Returns the GIL token associated with this object. + #[inline] pub fn py(&self) -> Python<'py> { self.0 } @@ -194,10 +195,26 @@ impl<'py, T> Bound<'py, T> { /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)). #[inline] pub fn into_ptr(self) -> *mut ffi::PyObject { - self.into_non_null().as_ptr() + ManuallyDrop::new(self).as_ptr() + } + + /// Helper to cast to `Bound<'py, PyAny>`. + #[inline] + pub fn as_any(&self) -> &Bound<'py, PyAny> { + // Safety: all Bound have the same memory layout, and all Bound are valid + // Bound, so pointer casting is valid. + unsafe { &*(self as *const Self).cast::>() } + } + + /// Helper to cast to `Bound<'py, PyAny>`, transferring ownership. + #[inline] + pub fn into_any(self) -> Bound<'py, PyAny> { + // Safety: all Bound are valid Bound + Bound(self.0, ManuallyDrop::new(self.unbind().into_any())) } /// Casts this `Bound` to a `Borrowed` smart pointer. + #[inline] pub fn as_borrowed<'a>(&'a self) -> Borrowed<'a, 'py, T> { Borrowed( unsafe { NonNull::new_unchecked(self.as_ptr()) }, @@ -208,15 +225,18 @@ impl<'py, T> Bound<'py, T> { /// Removes the connection for this `Bound` from the GIL, allowing /// it to cross thread boundaries. + #[inline] pub fn unbind(self) -> Py { // Safety: the type T is known to be correct and the ownership of the // pointer is transferred to the new Py instance. - unsafe { Py::from_non_null(self.into_non_null()) } + let non_null = (ManuallyDrop::new(self).1).0; + unsafe { Py::from_non_null(non_null) } } /// Casts this `Bound` as the corresponding "GIL Ref" type. /// /// This is a helper to be used for migration from the deprecated "GIL Refs" API. + #[inline] pub fn as_gil_ref(&'py self) -> &'py T::AsRefTarget where T: HasPyGilRef, @@ -228,20 +248,13 @@ impl<'py, T> Bound<'py, T> { /// [release pool](Python::from_owned_ptr). /// /// This is a helper to be used for migration from the deprecated "GIL Refs" API. + #[inline] pub fn into_gil_ref(self) -> &'py T::AsRefTarget where T: HasPyGilRef, { unsafe { self.py().from_owned_ptr(self.into_ptr()) } } - - // Internal helper to convert `self` into a `NonNull` which owns the - // Python reference. - pub(crate) fn into_non_null(self) -> NonNull { - // wrap in ManuallyDrop to avoid running Drop for self and decreasing - // the reference count - ManuallyDrop::new(self).1 .0 - } } unsafe impl AsPyPointer for Bound<'_, T> { @@ -262,11 +275,7 @@ pub struct Borrowed<'a, 'py, T>(NonNull, PhantomData<&'a Py>, impl<'py, T> Borrowed<'_, 'py, T> { /// Creates a new owned `Bound` from this borrowed reference by increasing the reference count. pub(crate) fn to_owned(self) -> Bound<'py, T> { - unsafe { ffi::Py_INCREF(self.as_ptr()) }; - Bound( - self.py(), - ManuallyDrop::new(unsafe { Py::from_non_null(self.0) }), - ) + (*self).clone() } } @@ -348,6 +357,7 @@ impl<'py, T> Deref for Borrowed<'_, 'py, T> { } impl Clone for Borrowed<'_, '_, T> { + #[inline] fn clone(&self) -> Self { *self } @@ -357,6 +367,7 @@ impl Copy for Borrowed<'_, '_, T> {} impl ToPyObject for Borrowed<'_, '_, T> { /// Converts `Py` instance -> PyObject. + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { (*self).into_py(py) } @@ -364,6 +375,7 @@ impl ToPyObject for Borrowed<'_, '_, T> { impl IntoPy for Borrowed<'_, '_, T> { /// Converts `Py` instance -> PyObject. + #[inline] fn into_py(self, py: Python<'_>) -> PyObject { self.to_owned().into_py(py) } @@ -709,9 +721,22 @@ impl Py { /// of the pointer or decrease the reference count (e.g. with [`pyo3::ffi::Py_DecRef`](crate::ffi::Py_DecRef)). #[inline] pub fn into_ptr(self) -> *mut ffi::PyObject { - let ptr = self.0.as_ptr(); - std::mem::forget(self); - ptr + ManuallyDrop::new(self).0.as_ptr() + } + + /// Helper to cast to `Py`. + #[inline] + pub fn as_any(&self) -> &Py { + // Safety: all Py have the same memory layout, and all Py are valid + // Py, so pointer casting is valid. + unsafe { &*(self as *const Self).cast::>() } + } + + /// Helper to cast to `Py`, transferring ownership. + #[inline] + pub fn into_any(self) -> Py { + // Safety: all Py are valid Py + unsafe { Py::from_non_null(ManuallyDrop::new(self).0) } } } @@ -868,17 +893,20 @@ where impl Py { /// Attaches this `Py` to the given Python context, allowing access to further Python APIs. + #[inline] pub fn bind<'py>(&self, _py: Python<'py>) -> &Bound<'py, T> { // Safety: `Bound` has the same layout as `Py` unsafe { &*(self as *const Py).cast() } } /// Same as `bind` but takes ownership of `self`. + #[inline] pub fn into_bound(self, py: Python<'_>) -> Bound<'_, T> { Bound(py, ManuallyDrop::new(self)) } /// Same as `bind` but produces a `Borrowed` instead of a `Bound`. + #[inline] pub fn bind_borrowed<'a, 'py>(&'a self, py: Python<'py>) -> Borrowed<'a, 'py, T> { Borrowed(self.0, PhantomData, py) } @@ -1243,24 +1271,16 @@ impl Py { /// /// # Safety /// `ptr` must point to a Python object of type T. - #[inline] - pub(crate) unsafe fn from_non_null(ptr: NonNull) -> Self { + unsafe fn from_non_null(ptr: NonNull) -> Self { Self(ptr, PhantomData) } - - /// Returns the inner pointer without decreasing the refcount. - #[inline] - fn into_non_null(self) -> NonNull { - let pointer = self.0; - mem::forget(self); - pointer - } } impl ToPyObject for Py { /// Converts `Py` instance -> PyObject. + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) } + self.clone_ref(py).into_any() } } @@ -1269,7 +1289,7 @@ impl IntoPy for Py { /// Consumes `self` without calling `Py_DECREF()`. #[inline] fn into_py(self, _py: Python<'_>) -> PyObject { - unsafe { PyObject::from_non_null(self.into_non_null()) } + self.into_any() } } @@ -1281,27 +1301,26 @@ impl IntoPy for &'_ Py { } impl ToPyObject for Bound<'_, T> { - /// Converts `Py` instance -> PyObject. + /// Converts `&Bound` instance -> PyObject, increasing the reference count. + #[inline] fn to_object(&self, py: Python<'_>) -> PyObject { - unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) } + self.clone().into_py(py) } } impl IntoPy for Bound<'_, T> { - /// Converts a `Py` instance to `PyObject`. - /// Consumes `self` without calling `Py_DECREF()`. + /// Converts a `Bound` instance to `PyObject`. #[inline] fn into_py(self, _py: Python<'_>) -> PyObject { - unsafe { PyObject::from_non_null(self.into_non_null()) } + self.into_any().unbind() } } impl IntoPy for &Bound<'_, T> { - /// Converts a `Py` instance to `PyObject`. - /// Consumes `self` without calling `Py_DECREF()`. + /// Converts `&Bound` instance -> PyObject, increasing the reference count. #[inline] - fn into_py(self, _py: Python<'_>) -> PyObject { - unsafe { PyObject::from_non_null(self.clone().into_non_null()) } + fn into_py(self, py: Python<'_>) -> PyObject { + self.to_object(py) } } @@ -1313,18 +1332,13 @@ unsafe impl crate::AsPyPointer for Py { } } -impl std::convert::From<&'_ PyAny> for PyObject { - fn from(obj: &PyAny) -> Self { - unsafe { Py::from_borrowed_ptr(obj.py(), obj.as_ptr()) } - } -} - impl std::convert::From<&'_ T> for PyObject where - T: PyNativeType + AsRef, + T: PyNativeType, { + #[inline] fn from(obj: &T) -> Self { - unsafe { Py::from_borrowed_ptr(obj.py(), obj.as_ref().as_ptr()) } + obj.as_borrowed().to_owned().into_any().unbind() } } @@ -1334,7 +1348,7 @@ where { #[inline] fn from(other: Py) -> Self { - unsafe { Self::from_non_null(other.into_non_null()) } + other.into_any() } } @@ -1362,7 +1376,7 @@ where T: PyClass, { fn from(cell: &PyCell) -> Self { - unsafe { Py::from_borrowed_ptr(cell.py(), cell.as_ptr()) } + cell.as_borrowed().to_owned().unbind() } } @@ -1724,6 +1738,24 @@ a = A() }); } + #[test] + fn test_bound_as_any() { + Python::with_gil(|py| { + let obj = PyString::new_bound(py, "hello world"); + let any = obj.as_any(); + assert_eq!(any.as_ptr(), obj.as_ptr()); + }); + } + + #[test] + fn test_bound_into_any() { + Python::with_gil(|py| { + let obj = PyString::new_bound(py, "hello world"); + let any = obj.clone().into_any(); + assert_eq!(any.as_ptr(), obj.as_ptr()); + }); + } + #[cfg(feature = "macros")] mod using_macros { use crate::PyCell;