macros: change self_arg to be an expression

This commit is contained in:
David Hewitt 2023-07-14 13:18:57 +01:00
parent b89934475d
commit 52cd9cbc90
2 changed files with 84 additions and 108 deletions

View File

@ -8,7 +8,7 @@ use quote::ToTokens;
use quote::{quote, quote_spanned}; use quote::{quote, quote_spanned};
use syn::ext::IdentExt; use syn::ext::IdentExt;
use syn::spanned::Spanned; use syn::spanned::Spanned;
use syn::{Result, Token}; use syn::Result;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct FnArg<'a> { pub struct FnArg<'a> {
@ -100,38 +100,31 @@ pub enum FnType {
} }
impl FnType { impl FnType {
pub fn self_conversion( pub fn self_arg(&self, cls: Option<&syn::Type>, error_mode: ExtractErrorMode) -> TokenStream {
&self,
cls: Option<&syn::Type>,
error_mode: ExtractErrorMode,
) -> TokenStream {
match self { match self {
FnType::Getter(st) | FnType::Setter(st) | FnType::Fn(st) => st.receiver( FnType::Getter(st) | FnType::Setter(st) | FnType::Fn(st) => {
cls.expect("no class given for Fn with a \"self\" receiver"), let mut receiver = st.receiver(
error_mode, cls.expect("no class given for Fn with a \"self\" receiver"),
), error_mode,
);
syn::Token![,](Span::call_site()).to_tokens(&mut receiver);
receiver
}
FnType::FnNew | FnType::FnStatic | FnType::ClassAttribute => { FnType::FnNew | FnType::FnStatic | FnType::ClassAttribute => {
quote!() quote!()
} }
FnType::FnClass | FnType::FnNewClass => { FnType::FnClass | FnType::FnNewClass => {
quote! { quote! {
let _slf = _pyo3::types::PyType::from_type_ptr(_py, _slf as *mut _pyo3::ffi::PyTypeObject); _pyo3::types::PyType::from_type_ptr(_py, _slf as *mut _pyo3::ffi::PyTypeObject),
} }
} }
FnType::FnModule => { FnType::FnModule => {
quote! { quote! {
let _slf = _py.from_borrowed_ptr::<_pyo3::types::PyModule>(_slf); _py.from_borrowed_ptr::<_pyo3::types::PyModule>(_slf),
} }
} }
} }
} }
pub fn self_arg(&self) -> TokenStream {
match self {
FnType::FnNew | FnType::FnStatic | FnType::ClassAttribute => quote!(),
_ => quote!(_slf,),
}
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -166,40 +159,34 @@ impl SelfType {
let _slf = syn::Ident::new("_slf", Span::call_site()); let _slf = syn::Ident::new("_slf", Span::call_site());
match self { match self {
SelfType::Receiver { span, mutable } => { SelfType::Receiver { span, mutable } => {
let (method, mutability) = if *mutable { let method = if *mutable {
( syn::Ident::new("extract_pyclass_ref_mut", *span)
quote_spanned! { *span => extract_pyclass_ref_mut },
Some(Token![mut](*span)),
)
} else { } else {
(quote_spanned! { *span => extract_pyclass_ref }, None) syn::Ident::new("extract_pyclass_ref", *span)
}; };
let extract = error_mode.handle_error( error_mode.handle_error(
&py, &py,
quote_spanned! { *span => quote_spanned! { *span =>
_pyo3::impl_::extract_argument::#method( _pyo3::impl_::extract_argument::#method::<#cls>(
#py.from_borrowed_ptr::<_pyo3::PyAny>(#_slf), #py.from_borrowed_ptr::<_pyo3::PyAny>(#_slf),
&mut holder, &mut { _pyo3::impl_::extract_argument::FunctionArgumentHolder::INIT },
) )
}, },
); )
quote_spanned! { *span =>
let mut holder = _pyo3::impl_::extract_argument::FunctionArgumentHolder::INIT;
let #_slf: &#mutability #cls = #extract;
}
} }
SelfType::TryFromPyCell(span) => { SelfType::TryFromPyCell(span) => {
let cell = error_mode.handle_error( error_mode.handle_error(
&py, &py,
quote!{ quote_spanned! { *span =>
_py.from_borrowed_ptr::<_pyo3::PyAny>(_slf).downcast::<_pyo3::PyCell<#cls>>() #py.from_borrowed_ptr::<_pyo3::PyAny>(#_slf).downcast::<_pyo3::PyCell<#cls>>()
.map_err(::std::convert::Into::<_pyo3::PyErr>::into)
.and_then(
#[allow(clippy::useless_conversion)] // In case _slf is PyCell<Self>
|cell| ::std::convert::TryFrom::try_from(cell).map_err(::std::convert::Into::into)
)
} }
); )
quote_spanned! { *span =>
let _cell = #cell;
#[allow(clippy::useless_conversion)] // In case _slf is PyCell<Self>
let #_slf = ::std::convert::TryFrom::try_from(_cell)?;
}
} }
} }
} }
@ -421,8 +408,7 @@ impl<'a> FnSpec<'a> {
ident: &proc_macro2::Ident, ident: &proc_macro2::Ident,
cls: Option<&syn::Type>, cls: Option<&syn::Type>,
) -> Result<TokenStream> { ) -> Result<TokenStream> {
let self_conversion = self.tp.self_conversion(cls, ExtractErrorMode::Raise); let self_arg = self.tp.self_arg(cls, ExtractErrorMode::Raise);
let self_arg = self.tp.self_arg();
let py = syn::Ident::new("_py", Span::call_site()); let py = syn::Ident::new("_py", Span::call_site());
let func_name = &self.name; let func_name = &self.name;
@ -448,13 +434,13 @@ impl<'a> FnSpec<'a> {
} else { } else {
rust_call(vec![]) rust_call(vec![])
}; };
quote! { quote! {
unsafe fn #ident<'py>( unsafe fn #ident<'py>(
#py: _pyo3::Python<'py>, #py: _pyo3::Python<'py>,
_slf: *mut _pyo3::ffi::PyObject, _slf: *mut _pyo3::ffi::PyObject,
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> { ) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
let function = #rust_name; // Shadow the function name to avoid #3017 let function = #rust_name; // Shadow the function name to avoid #3017
#self_conversion
#call #call
} }
} }
@ -471,7 +457,6 @@ impl<'a> FnSpec<'a> {
_kwnames: *mut _pyo3::ffi::PyObject _kwnames: *mut _pyo3::ffi::PyObject
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> { ) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
let function = #rust_name; // Shadow the function name to avoid #3017 let function = #rust_name; // Shadow the function name to avoid #3017
#self_conversion
#arg_convert #arg_convert
#call #call
} }
@ -488,7 +473,6 @@ impl<'a> FnSpec<'a> {
_kwargs: *mut _pyo3::ffi::PyObject _kwargs: *mut _pyo3::ffi::PyObject
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> { ) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
let function = #rust_name; // Shadow the function name to avoid #3017 let function = #rust_name; // Shadow the function name to avoid #3017
#self_conversion
#arg_convert #arg_convert
#call #call
} }

View File

@ -475,8 +475,13 @@ fn impl_py_class_attribute(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<Me
}) })
} }
fn impl_call_setter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<TokenStream> { fn impl_call_setter(
cls: &syn::Type,
spec: &FnSpec<'_>,
self_type: &SelfType,
) -> syn::Result<TokenStream> {
let (py_arg, args) = split_off_python_arg(&spec.signature.arguments); let (py_arg, args) = split_off_python_arg(&spec.signature.arguments);
let slf = self_type.receiver(cls, ExtractErrorMode::Raise);
if args.is_empty() { if args.is_empty() {
bail_spanned!(spec.name.span() => "setter function expected to have one argument"); bail_spanned!(spec.name.span() => "setter function expected to have one argument");
@ -489,9 +494,9 @@ fn impl_call_setter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<TokenStre
let name = &spec.name; let name = &spec.name;
let fncall = if py_arg.is_some() { let fncall = if py_arg.is_some() {
quote!(#cls::#name(_slf, _py, _val)) quote!(#cls::#name(#slf, _py, _val))
} else { } else {
quote!(#cls::#name(_slf, _val)) quote!(#cls::#name(#slf, _val))
}; };
Ok(fncall) Ok(fncall)
@ -506,31 +511,25 @@ pub fn impl_py_setter_def(
let doc = property_type.doc(); let doc = property_type.doc();
let setter_impl = match property_type { let setter_impl = match property_type {
PropertyType::Descriptor { PropertyType::Descriptor {
field: syn::Field { field_index, field, ..
ident: Some(ident), ..
},
..
} => { } => {
// named struct field let slf = SelfType::Receiver {
quote!({ _slf.#ident = _val; }) mutable: true,
} span: Span::call_site(),
PropertyType::Descriptor { field_index, .. } => { }
// tuple struct field .receiver(cls, ExtractErrorMode::Raise);
let index = syn::Index::from(field_index); if let Some(ident) = &field.ident {
quote!({ _slf.#index = _val; }) // named struct field
} quote!({ #slf.#ident = _val; })
PropertyType::Function { spec, .. } => impl_call_setter(cls, spec)?, } else {
}; // tuple struct field
let index = syn::Index::from(field_index);
let slf = match property_type { quote!({ #slf.#index = _val; })
PropertyType::Descriptor { .. } => SelfType::Receiver { }
mutable: true,
span: Span::call_site(),
}
.receiver(cls, ExtractErrorMode::Raise),
PropertyType::Function { self_type, .. } => {
self_type.receiver(cls, ExtractErrorMode::Raise)
} }
PropertyType::Function {
spec, self_type, ..
} => impl_call_setter(cls, spec, self_type)?,
}; };
let wrapper_ident = match property_type { let wrapper_ident = match property_type {
@ -568,7 +567,6 @@ pub fn impl_py_setter_def(
_slf: *mut _pyo3::ffi::PyObject, _slf: *mut _pyo3::ffi::PyObject,
_value: *mut _pyo3::ffi::PyObject, _value: *mut _pyo3::ffi::PyObject,
) -> _pyo3::PyResult<::std::os::raw::c_int> { ) -> _pyo3::PyResult<::std::os::raw::c_int> {
#slf
let _value = _py let _value = _py
.from_borrowed_ptr_or_opt(_value) .from_borrowed_ptr_or_opt(_value)
.ok_or_else(|| { .ok_or_else(|| {
@ -597,8 +595,13 @@ pub fn impl_py_setter_def(
}) })
} }
fn impl_call_getter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<TokenStream> { fn impl_call_getter(
cls: &syn::Type,
spec: &FnSpec<'_>,
self_type: &SelfType,
) -> syn::Result<TokenStream> {
let (py_arg, args) = split_off_python_arg(&spec.signature.arguments); let (py_arg, args) = split_off_python_arg(&spec.signature.arguments);
let slf = self_type.receiver(cls, ExtractErrorMode::Raise);
ensure_spanned!( ensure_spanned!(
args.is_empty(), args.is_empty(),
args[0].ty.span() => "getter function can only have one argument (of type pyo3::Python)" args[0].ty.span() => "getter function can only have one argument (of type pyo3::Python)"
@ -606,9 +609,9 @@ fn impl_call_getter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<TokenStre
let name = &spec.name; let name = &spec.name;
let fncall = if py_arg.is_some() { let fncall = if py_arg.is_some() {
quote!(#cls::#name(_slf, _py)) quote!(#cls::#name(#slf, _py))
} else { } else {
quote!(#cls::#name(_slf)) quote!(#cls::#name(#slf))
}; };
Ok(fncall) Ok(fncall)
@ -621,33 +624,28 @@ pub fn impl_py_getter_def(
) -> Result<MethodAndMethodDef> { ) -> Result<MethodAndMethodDef> {
let python_name = property_type.null_terminated_python_name()?; let python_name = property_type.null_terminated_python_name()?;
let doc = property_type.doc(); let doc = property_type.doc();
let getter_impl = match property_type { let getter_impl = match property_type {
PropertyType::Descriptor { PropertyType::Descriptor {
field: syn::Field { field_index, field, ..
ident: Some(ident), ..
},
..
} => { } => {
// named struct field let slf = SelfType::Receiver {
quote!(::std::clone::Clone::clone(&(_slf.#ident))) mutable: false,
} span: Span::call_site(),
PropertyType::Descriptor { field_index, .. } => { }
// tuple struct field .receiver(cls, ExtractErrorMode::Raise);
let index = syn::Index::from(field_index); if let Some(ident) = &field.ident {
quote!(::std::clone::Clone::clone(&(_slf.#index))) // named struct field
} quote!(::std::clone::Clone::clone(&(#slf.#ident)))
PropertyType::Function { spec, .. } => impl_call_getter(cls, spec)?, } else {
}; // tuple struct field
let index = syn::Index::from(field_index);
let slf = match property_type { quote!(::std::clone::Clone::clone(&(#slf.#index)))
PropertyType::Descriptor { .. } => SelfType::Receiver { }
mutable: false,
span: Span::call_site(),
}
.receiver(cls, ExtractErrorMode::Raise),
PropertyType::Function { self_type, .. } => {
self_type.receiver(cls, ExtractErrorMode::Raise)
} }
PropertyType::Function {
spec, self_type, ..
} => impl_call_getter(cls, spec, self_type)?,
}; };
let conversion = match property_type { let conversion = match property_type {
@ -699,7 +697,6 @@ pub fn impl_py_getter_def(
_py: _pyo3::Python<'_>, _py: _pyo3::Python<'_>,
_slf: *mut _pyo3::ffi::PyObject _slf: *mut _pyo3::ffi::PyObject
) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> { ) -> _pyo3::PyResult<*mut _pyo3::ffi::PyObject> {
#slf
let item = #getter_impl; let item = #getter_impl;
#conversion #conversion
} }
@ -1151,19 +1148,14 @@ fn generate_method_body(
extract_error_mode: ExtractErrorMode, extract_error_mode: ExtractErrorMode,
return_mode: Option<&ReturnMode>, return_mode: Option<&ReturnMode>,
) -> Result<TokenStream> { ) -> Result<TokenStream> {
let self_conversion = spec.tp.self_conversion(Some(cls), extract_error_mode); let self_arg = spec.tp.self_arg(Some(cls), extract_error_mode);
let self_arg = spec.tp.self_arg();
let rust_name = spec.name; let rust_name = spec.name;
let args = extract_proto_arguments(py, spec, arguments, extract_error_mode)?; let args = extract_proto_arguments(py, spec, arguments, extract_error_mode)?;
let call = quote! { _pyo3::callback::convert(#py, #cls::#rust_name(#self_arg #(#args),*)) }; let call = quote! { _pyo3::callback::convert(#py, #cls::#rust_name(#self_arg #(#args),*)) };
let body = if let Some(return_mode) = return_mode { Ok(if let Some(return_mode) = return_mode {
return_mode.return_call_output(py, call) return_mode.return_call_output(py, call)
} else { } else {
call call
};
Ok(quote! {
#self_conversion
#body
}) })
} }