allow `Bound<'_, T>` in #[pymethods] `self` position (#3896)

* allow `Bound<'_, T>` in #[pymethods] `self` position

* rename `TryFromPyCell` -> `TryFromBoundRef`

* remove unneccessary unsafe
This commit is contained in:
Icxolu 2024-02-25 08:13:36 +01:00 committed by GitHub
parent 8f1b99e1e9
commit 7c10ff4327
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 49 additions and 14 deletions

View File

@ -151,7 +151,7 @@ impl FnType {
#[derive(Clone, Debug)]
pub enum SelfType {
Receiver { mutable: bool, span: Span },
TryFromPyCell(Span),
TryFromBoundRef(Span),
}
#[derive(Clone, Copy)]
@ -204,15 +204,14 @@ impl SelfType {
)
})
}
SelfType::TryFromPyCell(span) => {
SelfType::TryFromBoundRef(span) => {
error_mode.handle_error(
quote_spanned! { *span =>
#py.from_borrowed_ptr::<_pyo3::PyAny>(#slf).downcast::<_pyo3::PyCell<#cls>>()
_pyo3::impl_::pymethods::BoundRef::ref_from_ptr(#py, &#slf).downcast::<#cls>()
.map_err(::std::convert::Into::<_pyo3::PyErr>::into)
.and_then(
#[allow(clippy::useless_conversion)] // In case slf is PyCell<Self>
#[allow(unknown_lints, clippy::unnecessary_fallible_conversions)] // In case slf is Py<Self> (unknown_lints can be removed when MSRV is 1.75+)
|cell| ::std::convert::TryFrom::try_from(cell).map_err(::std::convert::Into::into)
|bound| ::std::convert::TryFrom::try_from(bound).map_err(::std::convert::Into::into)
)
}
@ -291,7 +290,7 @@ pub fn parse_method_receiver(arg: &syn::FnArg) -> Result<SelfType> {
if let syn::Type::ImplTrait(_) = &**ty {
bail_spanned!(ty.span() => IMPL_TRAIT_ERR);
}
Ok(SelfType::TryFromPyCell(ty.span()))
Ok(SelfType::TryFromBoundRef(ty.span()))
}
}
}

View File

@ -1188,7 +1188,7 @@ fn complex_enum_variant_field_getter<'a>(
) -> Result<MethodAndMethodDef> {
let signature = crate::pyfunction::FunctionSignature::from_arguments(vec![])?;
let self_type = crate::method::SelfType::TryFromPyCell(field_span);
let self_type = crate::method::SelfType::TryFromBoundRef(field_span);
let spec = FnSpec {
tp: crate::method::FnType::Getter(self_type.clone()),

View File

@ -3,10 +3,12 @@ use crate::exceptions::PyStopAsyncIteration;
use crate::gil::LockGIL;
use crate::impl_::panic::PanicTrap;
use crate::internal_tricks::extract_c_string;
use crate::pycell::{PyBorrowError, PyBorrowMutError};
use crate::pyclass::boolean_struct::False;
use crate::types::{any::PyAnyMethods, PyModule, PyType};
use crate::{
ffi, Bound, Py, PyAny, PyCell, PyClass, PyErr, PyObject, PyResult, PyTraverseError, PyVisit,
Python,
ffi, Bound, DowncastError, Py, PyAny, PyCell, PyClass, PyErr, PyObject, PyRef, PyRefMut,
PyResult, PyTraverseError, PyTypeCheck, PyVisit, Python,
};
use std::borrow::Cow;
use std::ffi::CStr;
@ -490,6 +492,10 @@ impl<'a, 'py> BoundRef<'a, 'py, PyAny> {
Bound::ref_from_ptr_or_opt(py, ptr).as_ref().map(BoundRef)
}
pub fn downcast<T: PyTypeCheck>(self) -> Result<BoundRef<'a, 'py, T>, DowncastError<'a, 'py>> {
self.0.downcast::<T>().map(BoundRef)
}
pub unsafe fn downcast_unchecked<T>(self) -> BoundRef<'a, 'py, T> {
BoundRef(self.0.downcast_unchecked::<T>())
}
@ -511,6 +517,36 @@ impl<'a> From<BoundRef<'a, 'a, PyModule>> for &'a PyModule {
}
}
impl<'a, 'py, T: PyClass> From<BoundRef<'a, 'py, T>> for &'a PyCell<T> {
#[inline]
fn from(bound: BoundRef<'a, 'py, T>) -> Self {
bound.0.as_gil_ref()
}
}
impl<'a, 'py, T: PyClass> TryFrom<BoundRef<'a, 'py, T>> for PyRef<'py, T> {
type Error = PyBorrowError;
#[inline]
fn try_from(value: BoundRef<'a, 'py, T>) -> Result<Self, Self::Error> {
value.0.clone().into_gil_ref().try_into()
}
}
impl<'a, 'py, T: PyClass<Frozen = False>> TryFrom<BoundRef<'a, 'py, T>> for PyRefMut<'py, T> {
type Error = PyBorrowMutError;
#[inline]
fn try_from(value: BoundRef<'a, 'py, T>) -> Result<Self, Self::Error> {
value.0.clone().into_gil_ref().try_into()
}
}
impl<'a, 'py, T> From<BoundRef<'a, 'py, T>> for Bound<'py, T> {
#[inline]
fn from(bound: BoundRef<'a, 'py, T>) -> Self {
bound.0.clone()
}
}
impl<'a, 'py, T> From<BoundRef<'a, 'py, T>> for &'a Bound<'py, T> {
#[inline]
fn from(bound: BoundRef<'a, 'py, T>) -> Self {

View File

@ -654,7 +654,7 @@ impl<T: PyClass + fmt::Debug> fmt::Debug for PyCell<T> {
/// }
/// # Python::with_gil(|py| {
/// # let sub = Py::new(py, Child::new()).unwrap();
/// # pyo3::py_run!(py, sub, "assert sub.format() == 'Caterpillar(base: Butterfly, cnt: 3)'");
/// # pyo3::py_run!(py, sub, "assert sub.format() == 'Caterpillar(base: Butterfly, cnt: 4)'");
/// # });
/// ```
///

View File

@ -1,8 +1,8 @@
error[E0277]: the trait bound `i32: From<&PyCell<MyClass>>` is not satisfied
error[E0277]: the trait bound `i32: From<BoundRef<'_, '_, MyClass>>` is not satisfied
--> tests/ui/invalid_pymethod_receiver.rs:8:43
|
8 | fn method_with_invalid_self_type(slf: i32, py: Python<'_>, index: u32) {}
| ^^^ the trait `From<&PyCell<MyClass>>` is not implemented for `i32`
| ^^^ the trait `From<BoundRef<'_, '_, MyClass>>` is not implemented for `i32`
|
= help: the following other types implement trait `From<T>`:
<i32 as From<bool>>
@ -11,5 +11,5 @@ error[E0277]: the trait bound `i32: From<&PyCell<MyClass>>` is not satisfied
<i32 as From<u8>>
<i32 as From<u16>>
<i32 as From<NonZeroI32>>
= note: required for `&PyCell<MyClass>` to implement `Into<i32>`
= note: required for `i32` to implement `TryFrom<&PyCell<MyClass>>`
= note: required for `BoundRef<'_, '_, MyClass>` to implement `Into<i32>`
= note: required for `i32` to implement `TryFrom<BoundRef<'_, '_, MyClass>>`