Merge pull request #3785 from davidhewitt/bound-as-any

add `Bound::as_any` and `Bound::into_any` (and same for `Py`)
This commit is contained in:
David Hewitt 2024-02-01 10:52:25 +00:00 committed by GitHub
commit d35a6a1fd6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 93 additions and 60 deletions

View File

@ -0,0 +1 @@
Add `Py::as_any` and `Py::into_any`.

View File

@ -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<T> have the same memory layout, and all Bound<T> are valid Bound<PyAny>
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<Bound<'py, PyAny>> for Bound<'py, T>
where
T: AsRef<PyAny>,
{
#[inline]
fn as_ref(&self) -> &Bound<'py, PyAny> {
self.as_any()
}
}
impl<T> Clone for Bound<'_, T> {
#[inline]
fn clone(&self) -> Self {
Self(self.0, ManuallyDrop::new(self.1.clone_ref(self.0)))
}
}
impl<T> 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<T> have the same memory layout, and all Bound<T> are valid
// Bound<PyAny>, so pointer casting is valid.
unsafe { &*(self as *const Self).cast::<Bound<'py, PyAny>>() }
}
/// Helper to cast to `Bound<'py, PyAny>`, transferring ownership.
#[inline]
pub fn into_any(self) -> Bound<'py, PyAny> {
// Safety: all Bound<T> are valid Bound<PyAny>
Bound(self.0, ManuallyDrop::new(self.unbind().into_any()))
}
/// Casts this `Bound<T>` to a `Borrowed<T>` 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<T>` from the GIL, allowing
/// it to cross thread boundaries.
#[inline]
pub fn unbind(self) -> Py<T> {
// Safety: the type T is known to be correct and the ownership of the
// pointer is transferred to the new Py<T> 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<T>` 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<ffi::PyObject> {
// wrap in ManuallyDrop to avoid running Drop for self and decreasing
// the reference count
ManuallyDrop::new(self).1 .0
}
}
unsafe impl<T> AsPyPointer for Bound<'_, T> {
@ -262,11 +275,7 @@ pub struct Borrowed<'a, 'py, T>(NonNull<ffi::PyObject>, PhantomData<&'a Py<T>>,
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<T> Clone for Borrowed<'_, '_, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
@ -357,6 +367,7 @@ impl<T> Copy for Borrowed<'_, '_, T> {}
impl<T> 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<T> ToPyObject for Borrowed<'_, '_, T> {
impl<T> IntoPy<PyObject> 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<T> 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 {
let ptr = self.0.as_ptr();
std::mem::forget(self);
ptr
ManuallyDrop::new(self).0.as_ptr()
}
/// Helper to cast to `Py<PyAny>`.
#[inline]
pub fn as_any(&self) -> &Py<PyAny> {
// Safety: all Py<T> have the same memory layout, and all Py<T> are valid
// Py<PyAny>, so pointer casting is valid.
unsafe { &*(self as *const Self).cast::<Py<PyAny>>() }
}
/// Helper to cast to `Py<PyAny>`, transferring ownership.
#[inline]
pub fn into_any(self) -> Py<PyAny> {
// Safety: all Py<T> are valid Py<PyAny>
unsafe { Py::from_non_null(ManuallyDrop::new(self).0) }
}
}
@ -868,17 +893,20 @@ where
impl<T> Py<T> {
/// 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<T>).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<T>` instead of a `Bound<T>`.
#[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<T> Py<T> {
///
/// # Safety
/// `ptr` must point to a Python object of type T.
#[inline]
pub(crate) unsafe fn from_non_null(ptr: NonNull<ffi::PyObject>) -> Self {
unsafe fn from_non_null(ptr: NonNull<ffi::PyObject>) -> Self {
Self(ptr, PhantomData)
}
/// Returns the inner pointer without decreasing the refcount.
#[inline]
fn into_non_null(self) -> NonNull<ffi::PyObject> {
let pointer = self.0;
mem::forget(self);
pointer
}
}
impl<T> ToPyObject for Py<T> {
/// 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<T> IntoPy<PyObject> for Py<T> {
/// 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<T> IntoPy<PyObject> for &'_ Py<T> {
}
impl<T> 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<T> IntoPy<PyObject> 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<T> IntoPy<PyObject> 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<T> crate::AsPyPointer for Py<T> {
}
}
impl std::convert::From<&'_ PyAny> for PyObject {
fn from(obj: &PyAny) -> Self {
unsafe { Py::from_borrowed_ptr(obj.py(), obj.as_ptr()) }
}
}
impl<T> std::convert::From<&'_ T> for PyObject
where
T: PyNativeType + AsRef<PyAny>,
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<T>) -> Self {
unsafe { Self::from_non_null(other.into_non_null()) }
other.into_any()
}
}
@ -1362,7 +1376,7 @@ where
T: PyClass,
{
fn from(cell: &PyCell<T>) -> 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;