allow borrowed extracts with `gil-refs` disabled (#3959)

This commit is contained in:
Icxolu 2024-03-15 08:53:52 +01:00 committed by GitHub
parent 989d2c53ab
commit 5c86dc35c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 54 additions and 28 deletions

View File

@ -6,7 +6,9 @@ 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::{ffi, gil, Bound, Py, PyAny, PyClass, PyNativeType, PyObject, PyRef, PyRefMut, Python}; use crate::{
ffi, gil, Borrowed, Bound, Py, PyAny, 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.
@ -288,7 +290,7 @@ pub trait FromPyObjectBound<'a, 'py>: Sized + from_py_object_bound_sealed::Seale
/// ///
/// Users are advised against calling this method directly: instead, use this via /// Users are advised against calling this method directly: instead, use this via
/// [`Bound<'_, PyAny>::extract`] or [`Py::extract`]. /// [`Bound<'_, PyAny>::extract`] or [`Py::extract`].
fn from_py_object_bound(ob: &'a Bound<'py, PyAny>) -> PyResult<Self>; fn from_py_object_bound(ob: Borrowed<'a, 'py, PyAny>) -> PyResult<Self>;
/// Extracts the type hint information for this type when it appears as an argument. /// Extracts the type hint information for this type when it appears as an argument.
/// ///
@ -307,8 +309,8 @@ impl<'py, T> FromPyObjectBound<'_, 'py> for T
where where
T: FromPyObject<'py>, T: FromPyObject<'py>,
{ {
fn from_py_object_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> { fn from_py_object_bound(ob: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
Self::extract_bound(ob) Self::extract_bound(&ob)
} }
#[cfg(feature = "experimental-inspect")] #[cfg(feature = "experimental-inspect")]

View File

@ -2,11 +2,9 @@ use std::borrow::Cow;
#[cfg(feature = "experimental-inspect")] #[cfg(feature = "experimental-inspect")]
use crate::inspect::types::TypeInfo; use crate::inspect::types::TypeInfo;
#[cfg(not(feature = "gil-refs"))]
use crate::types::PyBytesMethods;
use crate::{ use crate::{
types::{PyAnyMethods, PyByteArray, PyByteArrayMethods, PyBytes}, types::{PyByteArray, PyByteArrayMethods, PyBytes},
Bound, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
}; };
impl<'a> IntoPy<PyObject> for &'a [u8] { impl<'a> IntoPy<PyObject> for &'a [u8] {
@ -34,7 +32,7 @@ impl<'py> crate::FromPyObject<'py> for &'py [u8] {
#[cfg(not(feature = "gil-refs"))] #[cfg(not(feature = "gil-refs"))]
impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a [u8] { impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a [u8] {
fn from_py_object_bound(obj: &'a Bound<'_, PyAny>) -> PyResult<Self> { fn from_py_object_bound(obj: crate::Borrowed<'a, '_, PyAny>) -> PyResult<Self> {
Ok(obj.downcast::<PyBytes>()?.as_bytes()) Ok(obj.downcast::<PyBytes>()?.as_bytes())
} }
@ -51,7 +49,8 @@ impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a [u8] {
/// If it is a `bytearray`, its contents will be copied to an owned `Cow`. /// If it is a `bytearray`, its contents will be copied to an owned `Cow`.
#[cfg(feature = "gil-refs")] #[cfg(feature = "gil-refs")]
impl<'py> crate::FromPyObject<'py> for Cow<'py, [u8]> { impl<'py> crate::FromPyObject<'py> for Cow<'py, [u8]> {
fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> { fn extract_bound(ob: &crate::Bound<'py, PyAny>) -> PyResult<Self> {
use crate::types::PyAnyMethods;
if let Ok(bytes) = ob.downcast::<PyBytes>() { if let Ok(bytes) = ob.downcast::<PyBytes>() {
return Ok(Cow::Borrowed(bytes.clone().into_gil_ref().as_bytes())); return Ok(Cow::Borrowed(bytes.clone().into_gil_ref().as_bytes()));
} }
@ -63,7 +62,7 @@ impl<'py> crate::FromPyObject<'py> for Cow<'py, [u8]> {
#[cfg(not(feature = "gil-refs"))] #[cfg(not(feature = "gil-refs"))]
impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, [u8]> { impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, [u8]> {
fn from_py_object_bound(ob: &'a Bound<'_, PyAny>) -> PyResult<Self> { fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult<Self> {
if let Ok(bytes) = ob.downcast::<PyBytes>() { if let Ok(bytes) = ob.downcast::<PyBytes>() {
return Ok(Cow::Borrowed(bytes.as_bytes())); return Ok(Cow::Borrowed(bytes.as_bytes()));
} }

View File

@ -128,7 +128,7 @@ impl<'py> FromPyObject<'py> for &'py str {
#[cfg(all(not(feature = "gil-refs"), any(Py_3_10, not(Py_LIMITED_API))))] #[cfg(all(not(feature = "gil-refs"), any(Py_3_10, not(Py_LIMITED_API))))]
impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a str { impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a str {
fn from_py_object_bound(ob: &'a Bound<'_, PyAny>) -> PyResult<Self> { fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult<Self> {
ob.downcast::<PyString>()?.to_str() ob.downcast::<PyString>()?.to_str()
} }
@ -152,7 +152,7 @@ impl<'py> FromPyObject<'py> for Cow<'py, str> {
#[cfg(not(feature = "gil-refs"))] #[cfg(not(feature = "gil-refs"))]
impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, str> { impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, str> {
fn from_py_object_bound(ob: &'a Bound<'_, PyAny>) -> PyResult<Self> { fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult<Self> {
ob.downcast::<PyString>()?.to_cow() ob.downcast::<PyString>()?.to_cow()
} }

View File

@ -7,7 +7,7 @@ use crate::{
exceptions::{self, PyBaseException}, exceptions::{self, PyBaseException},
ffi, ffi,
}; };
use crate::{IntoPy, Py, PyAny, PyNativeType, PyObject, Python, ToPyObject}; use crate::{Borrowed, IntoPy, Py, PyAny, PyNativeType, PyObject, Python, ToPyObject};
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use std::ffi::CString; use std::ffi::CString;
@ -65,17 +65,16 @@ impl<'a> PyDowncastError<'a> {
/// Compatibility API to convert the Bound variant `DowncastError` into the /// Compatibility API to convert the Bound variant `DowncastError` into the
/// gil-ref variant /// gil-ref variant
pub(crate) fn from_downcast_err(DowncastError { from, to }: DowncastError<'a, 'a>) -> Self { pub(crate) fn from_downcast_err(DowncastError { from, to }: DowncastError<'a, 'a>) -> Self {
Self { #[allow(deprecated)]
from: from.as_gil_ref(), let from = unsafe { from.py().from_borrowed_ptr(from.as_ptr()) };
to, Self { from, to }
}
} }
} }
/// Error that indicates a failure to convert a PyAny to a more specific Python type. /// Error that indicates a failure to convert a PyAny to a more specific Python type.
#[derive(Debug)] #[derive(Debug)]
pub struct DowncastError<'a, 'py> { pub struct DowncastError<'a, 'py> {
from: &'a Bound<'py, PyAny>, from: Borrowed<'a, 'py, PyAny>,
to: Cow<'static, str>, to: Cow<'static, str>,
} }
@ -83,6 +82,16 @@ impl<'a, 'py> DowncastError<'a, 'py> {
/// Create a new `PyDowncastError` representing a failure to convert the object /// Create a new `PyDowncastError` representing a failure to convert the object
/// `from` into the type named in `to`. /// `from` into the type named in `to`.
pub fn new(from: &'a Bound<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self { pub fn new(from: &'a Bound<'py, PyAny>, to: impl Into<Cow<'static, str>>) -> Self {
DowncastError {
from: from.as_borrowed(),
to: to.into(),
}
}
#[cfg(not(feature = "gil-refs"))]
pub(crate) fn new_from_borrowed(
from: Borrowed<'a, 'py, PyAny>,
to: impl Into<Cow<'static, str>>,
) -> Self {
DowncastError { DowncastError {
from, from,
to: to.into(), to: to.into(),
@ -1036,7 +1045,7 @@ impl std::error::Error for DowncastError<'_, '_> {}
impl std::fmt::Display for DowncastError<'_, '_> { impl std::fmt::Display for DowncastError<'_, '_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
display_downcast_error(f, self.from, &self.to) display_downcast_error(f, &self.from, &self.to)
} }
} }

View File

@ -579,6 +579,20 @@ impl<'a, 'py> Borrowed<'a, 'py, PyAny> {
Self(NonNull::new_unchecked(ptr), PhantomData, py) Self(NonNull::new_unchecked(ptr), PhantomData, py)
} }
#[inline]
#[cfg(not(feature = "gil-refs"))]
pub(crate) fn downcast<T>(self) -> Result<Borrowed<'a, 'py, T>, DowncastError<'a, 'py>>
where
T: PyTypeCheck,
{
if T::type_check(&self) {
// Safety: type_check is responsible for ensuring that the type is correct
Ok(unsafe { self.downcast_unchecked() })
} else {
Err(DowncastError::new_from_borrowed(self, T::NAME))
}
}
/// Converts this `PyAny` to a concrete Python type without checking validity. /// Converts this `PyAny` to a concrete Python type without checking validity.
/// ///
/// # Safety /// # Safety

View File

@ -1,5 +1,5 @@
use crate::class::basic::CompareOp; use crate::class::basic::CompareOp;
use crate::conversion::{AsPyPointer, FromPyObject, FromPyObjectBound, IntoPy, ToPyObject}; use crate::conversion::{AsPyPointer, FromPyObjectBound, IntoPy, ToPyObject};
use crate::err::{DowncastError, DowncastIntoError, PyDowncastError, PyErr, PyResult}; use crate::err::{DowncastError, DowncastIntoError, PyDowncastError, PyErr, PyResult};
use crate::exceptions::{PyAttributeError, PyTypeError}; use crate::exceptions::{PyAttributeError, PyTypeError};
use crate::ffi_ptr_ext::FfiPtrExt; use crate::ffi_ptr_ext::FfiPtrExt;
@ -799,13 +799,14 @@ impl PyAny {
/// Extracts some type from the Python object. /// Extracts some type from the Python object.
/// ///
/// This is a wrapper function around [`FromPyObject::extract()`]. /// This is a wrapper function around
/// [`FromPyObject::extract()`](crate::FromPyObject::extract).
#[inline] #[inline]
pub fn extract<'py, D>(&'py self) -> PyResult<D> pub fn extract<'py, D>(&'py self) -> PyResult<D>
where where
D: FromPyObject<'py>, D: FromPyObjectBound<'py, 'py>,
{ {
FromPyObject::extract(self) FromPyObjectBound::from_py_object_bound(self.as_borrowed())
} }
/// Returns the reference count for the Python object. /// Returns the reference count for the Python object.
@ -1641,7 +1642,8 @@ pub trait PyAnyMethods<'py>: crate::sealed::Sealed {
/// Extracts some type from the Python object. /// Extracts some type from the Python object.
/// ///
/// This is a wrapper function around [`FromPyObject::extract()`]. /// This is a wrapper function around
/// [`FromPyObject::extract()`](crate::FromPyObject::extract).
fn extract<'a, T>(&'a self) -> PyResult<T> fn extract<'a, T>(&'a self) -> PyResult<T>
where where
T: FromPyObjectBound<'a, 'py>; T: FromPyObjectBound<'a, 'py>;
@ -2182,7 +2184,7 @@ impl<'py> PyAnyMethods<'py> for Bound<'py, PyAny> {
where where
T: FromPyObjectBound<'a, 'py>, T: FromPyObjectBound<'a, 'py>,
{ {
FromPyObjectBound::from_py_object_bound(self) FromPyObjectBound::from_py_object_bound(self.as_borrowed())
} }
fn get_refcnt(&self) -> isize { fn get_refcnt(&self) -> isize {

View File

@ -158,7 +158,7 @@ impl<'py> PyBytesMethods<'py> for Bound<'py, PyBytes> {
impl<'a> Borrowed<'a, '_, PyBytes> { impl<'a> Borrowed<'a, '_, PyBytes> {
/// Gets the Python string as a byte slice. /// Gets the Python string as a byte slice.
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
fn as_bytes(self) -> &'a [u8] { pub(crate) fn as_bytes(self) -> &'a [u8] {
unsafe { unsafe {
let buffer = ffi::PyBytes_AsString(self.as_ptr()) as *const u8; let buffer = ffi::PyBytes_AsString(self.as_ptr()) as *const u8;
let length = ffi::PyBytes_Size(self.as_ptr()) as usize; let length = ffi::PyBytes_Size(self.as_ptr()) as usize;

View File

@ -369,7 +369,7 @@ impl<'a> Borrowed<'a, '_, PyString> {
} }
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
fn to_cow(self) -> PyResult<Cow<'a, str>> { pub(crate) fn to_cow(self) -> PyResult<Cow<'a, str>> {
// TODO: this method can probably be deprecated once Python 3.9 support is dropped, // TODO: this method can probably be deprecated once Python 3.9 support is dropped,
// because all versions then support the more efficient `to_str`. // because all versions then support the more efficient `to_str`.
#[cfg(any(Py_3_10, not(Py_LIMITED_API)))] #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]