macros: emit pymethod associated methods as a single block

This commit is contained in:
David Hewitt 2022-06-16 22:06:17 +01:00
parent 197871245c
commit f81a01b604
7 changed files with 274 additions and 199 deletions

View File

@ -7,7 +7,7 @@ use crate::deprecations::Deprecation;
use crate::params::{accept_args_kwargs, impl_arg_params};
use crate::pyfunction::PyFunctionOptions;
use crate::pyfunction::{PyFunctionArgPyO3Attributes, PyFunctionSignature};
use crate::utils::{self, get_pyo3_crate, PythonDoc};
use crate::utils::{self, PythonDoc};
use crate::{deprecations::Deprecations, pyfunction::Argument};
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
@ -224,7 +224,6 @@ pub struct FnSpec<'a> {
pub deprecations: Deprecations,
pub convention: CallingConvention,
pub text_signature: Option<TextSignatureAttribute>,
pub krate: syn::Path,
pub unsafety: Option<syn::Token![unsafe]>,
}
@ -266,7 +265,6 @@ impl<'a> FnSpec<'a> {
) -> Result<FnSpec<'a>> {
let PyFunctionOptions {
text_signature,
krate,
name,
mut deprecations,
..
@ -285,7 +283,6 @@ impl<'a> FnSpec<'a> {
let name = &sig.ident;
let ty = get_return_info(&sig.output);
let python_name = python_name.as_ref().unwrap_or(name).unraw();
let krate = get_pyo3_crate(&krate);
let doc = utils::get_doc(
meth_attrs,
@ -321,7 +318,6 @@ impl<'a> FnSpec<'a> {
doc,
deprecations,
text_signature,
krate,
unsafety: sig.unsafety,
})
}
@ -489,16 +485,14 @@ impl<'a> FnSpec<'a> {
_pyo3::callback::convert(#py, ret)
};
let krate = &self.krate;
Ok(match self.convention {
CallingConvention::Noargs => {
quote! {
unsafe extern "C" fn #ident (
_slf: *mut #krate::ffi::PyObject,
_args: *mut #krate::ffi::PyObject,
) -> *mut #krate::ffi::PyObject
_slf: *mut _pyo3::ffi::PyObject,
_args: *mut _pyo3::ffi::PyObject,
) -> *mut _pyo3::ffi::PyObject
{
use #krate as _pyo3;
#deprecations
let gil = _pyo3::GILPool::new();
let #py = gil.python();
@ -513,12 +507,11 @@ impl<'a> FnSpec<'a> {
let arg_convert = impl_arg_params(self, cls, &py, true)?;
quote! {
unsafe extern "C" fn #ident (
_slf: *mut #krate::ffi::PyObject,
_args: *const *mut #krate::ffi::PyObject,
_nargs: #krate::ffi::Py_ssize_t,
_kwnames: *mut #krate::ffi::PyObject) -> *mut #krate::ffi::PyObject
_slf: *mut _pyo3::ffi::PyObject,
_args: *const *mut _pyo3::ffi::PyObject,
_nargs: _pyo3::ffi::Py_ssize_t,
_kwnames: *mut _pyo3::ffi::PyObject) -> *mut _pyo3::ffi::PyObject
{
use #krate as _pyo3;
#deprecations
let gil = _pyo3::GILPool::new();
let #py = gil.python();
@ -534,11 +527,10 @@ impl<'a> FnSpec<'a> {
let arg_convert = impl_arg_params(self, cls, &py, false)?;
quote! {
unsafe extern "C" fn #ident (
_slf: *mut #krate::ffi::PyObject,
_args: *mut #krate::ffi::PyObject,
_kwargs: *mut #krate::ffi::PyObject) -> *mut #krate::ffi::PyObject
_slf: *mut _pyo3::ffi::PyObject,
_args: *mut _pyo3::ffi::PyObject,
_kwargs: *mut _pyo3::ffi::PyObject) -> *mut _pyo3::ffi::PyObject
{
use #krate as _pyo3;
#deprecations
let gil = _pyo3::GILPool::new();
let #py = gil.python();
@ -555,11 +547,10 @@ impl<'a> FnSpec<'a> {
let arg_convert = impl_arg_params(self, cls, &py, false)?;
quote! {
unsafe extern "C" fn #ident (
subtype: *mut #krate::ffi::PyTypeObject,
_args: *mut #krate::ffi::PyObject,
_kwargs: *mut #krate::ffi::PyObject) -> *mut #krate::ffi::PyObject
subtype: *mut _pyo3::ffi::PyTypeObject,
_args: *mut _pyo3::ffi::PyObject,
_kwargs: *mut _pyo3::ffi::PyObject) -> *mut _pyo3::ffi::PyObject
{
use #krate as _pyo3;
#deprecations
use _pyo3::callback::IntoPyCallbackOutput;
let gil = _pyo3::GILPool::new();

View File

@ -11,7 +11,8 @@ use crate::konst::{ConstAttributes, ConstSpec};
use crate::method::FnSpec;
use crate::pyimpl::{gen_py_const, PyClassMethodsType};
use crate::pymethod::{
impl_py_getter_def, impl_py_setter_def, PropertyType, SlotDef, __INT__, __REPR__, __RICHCMP__,
impl_py_getter_def, impl_py_setter_def, MethodAndMethodDef, MethodAndSlotDef, PropertyType,
SlotDef, __INT__, __REPR__, __RICHCMP__,
};
use crate::utils::{self, get_pyo3_crate, PythonDoc};
use crate::PyFunctionOptions;
@ -539,7 +540,7 @@ fn generate_default_protocol_slot(
cls: &syn::Type,
method: &mut syn::ImplItemMethod,
slot: &SlotDef,
) -> syn::Result<TokenStream> {
) -> syn::Result<MethodAndSlotDef> {
let spec = FnSpec::parse(
&mut method.sig,
&mut Vec::new(),
@ -557,7 +558,7 @@ fn generate_default_protocol_slot(
fn enum_default_methods<'a>(
cls: &'a syn::Ident,
unit_variant_names: impl IntoIterator<Item = &'a syn::Ident>,
) -> Vec<TokenStream> {
) -> Vec<MethodAndMethodDef> {
let cls_type = syn::parse_quote!(#cls);
let variant_to_attribute = |ident: &syn::Ident| ConstSpec {
rust_ident: ident.clone(),
@ -588,7 +589,7 @@ fn extract_variant_data(variant: &syn::Variant) -> syn::Result<PyClassEnumVarian
fn descriptors_to_items(
cls: &syn::Ident,
field_options: Vec<(&syn::Field, FieldPyO3Options)>,
) -> syn::Result<Vec<TokenStream>> {
) -> syn::Result<Vec<MethodAndMethodDef>> {
let ty = syn::parse_quote!(#cls);
field_options
.into_iter()
@ -666,8 +667,8 @@ struct PyClassImplsBuilder<'a> {
cls: &'a syn::Ident,
attr: &'a PyClassArgs,
methods_type: PyClassMethodsType,
default_methods: Vec<TokenStream>,
default_slots: Vec<TokenStream>,
default_methods: Vec<MethodAndMethodDef>,
default_slots: Vec<MethodAndSlotDef>,
doc: Option<PythonDoc>,
}
@ -676,8 +677,8 @@ impl<'a> PyClassImplsBuilder<'a> {
cls: &'a syn::Ident,
attr: &'a PyClassArgs,
methods_type: PyClassMethodsType,
default_methods: Vec<TokenStream>,
default_slots: Vec<TokenStream>,
default_methods: Vec<MethodAndMethodDef>,
default_slots: Vec<MethodAndSlotDef>,
) -> Self {
Self {
cls,
@ -838,8 +839,18 @@ impl<'a> PyClassImplsBuilder<'a> {
None
};
let default_methods = &self.default_methods;
let default_slots = &self.default_slots;
let default_methods = self
.default_methods
.iter()
.map(|meth| &meth.associated_method)
.chain(
self.default_slots
.iter()
.map(|meth| &meth.associated_method),
);
let default_method_defs = self.default_methods.iter().map(|meth| &meth.method_def);
let default_slot_defs = self.default_slots.iter().map(|slot| &slot.slot_def);
let freelist_slots = self.freelist_slots();
let deprecations = &self.attr.deprecations;
@ -907,8 +918,8 @@ impl<'a> PyClassImplsBuilder<'a> {
let collector = PyClassImplCollector::<Self>::new();
#deprecations;
static INTRINSIC_ITEMS: PyClassItems = PyClassItems {
methods: &[#(#default_methods),*],
slots: &[#(#default_slots),* #(#freelist_slots),*],
methods: &[#(#default_method_defs),*],
slots: &[#(#default_slot_defs),* #(#freelist_slots),*],
};
visitor(&INTRINSIC_ITEMS);
#pymethods_items
@ -920,6 +931,12 @@ impl<'a> PyClassImplsBuilder<'a> {
#weaklist_offset
}
#[doc(hidden)]
#[allow(non_snake_case)]
impl #cls {
#(#default_methods)*
}
#inventory_class
}
}

View File

@ -430,7 +430,6 @@ pub fn impl_wrap_pyfunction(
doc,
deprecations: options.deprecations,
text_signature: options.text_signature,
krate: krate.clone(),
unsafety: func.sig.unsafety,
};
@ -442,7 +441,6 @@ pub fn impl_wrap_pyfunction(
let methoddef = spec.get_methoddef(wrapper_ident);
let wrapped_pyfunction = quote! {
#wrapper
// Create a module with the same name as the `#[pyfunction]` - this way `use <the function>`
// will actually bring both the module and the function into scope.
@ -461,6 +459,8 @@ pub fn impl_wrap_pyfunction(
impl #name::MakeDef {
const DEF: #krate::impl_::pyfunction::PyMethodDef = #methoddef;
}
#wrapper
};
};
Ok(wrapped_pyfunction)

View File

@ -6,7 +6,7 @@ use crate::{
attributes::{take_pyo3_options, CrateAttribute},
konst::{ConstAttributes, ConstSpec},
pyfunction::PyFunctionOptions,
pymethod::{self, is_proto_method},
pymethod::{self, is_proto_method, MethodAndMethodDef, MethodAndSlotDef},
utils::get_pyo3_crate,
};
use proc_macro2::TokenStream;
@ -95,6 +95,7 @@ pub fn impl_methods(
let mut trait_impls = Vec::new();
let mut proto_impls = Vec::new();
let mut methods = Vec::new();
let mut associated_methods = Vec::new();
let mut implemented_proto_fragments = HashSet::new();
@ -104,18 +105,26 @@ pub fn impl_methods(
let mut fun_options = PyFunctionOptions::from_attrs(&mut meth.attrs)?;
fun_options.krate = fun_options.krate.or_else(|| options.krate.clone());
match pymethod::gen_py_method(ty, &mut meth.sig, &mut meth.attrs, fun_options)? {
GeneratedPyMethod::Method(token_stream) => {
GeneratedPyMethod::Method(MethodAndMethodDef {
associated_method,
method_def,
}) => {
let attrs = get_cfg_attributes(&meth.attrs);
methods.push(quote!(#(#attrs)* #token_stream));
associated_methods.push(quote!(#(#attrs)* #associated_method));
methods.push(quote!(#(#attrs)* #method_def));
}
GeneratedPyMethod::SlotTraitImpl(method_name, token_stream) => {
implemented_proto_fragments.insert(method_name);
let attrs = get_cfg_attributes(&meth.attrs);
trait_impls.push(quote!(#(#attrs)* #token_stream));
}
GeneratedPyMethod::Proto(token_stream) => {
GeneratedPyMethod::Proto(MethodAndSlotDef {
associated_method,
slot_def,
}) => {
let attrs = get_cfg_attributes(&meth.attrs);
proto_impls.push(quote!(#(#attrs)* #token_stream))
proto_impls.push(quote!(#(#attrs)* #slot_def));
associated_methods.push(quote!(#(#attrs)* #associated_method));
}
}
}
@ -127,8 +136,12 @@ pub fn impl_methods(
attributes,
};
let attrs = get_cfg_attributes(&konst.attrs);
let meth = gen_py_const(ty, &spec);
methods.push(quote!(#(#attrs)* #meth));
let MethodAndMethodDef {
associated_method,
method_def,
} = gen_py_const(ty, &spec);
methods.push(quote!(#(#attrs)* #method_def));
associated_methods.push(quote!(#(#attrs)* #associated_method));
if is_proto_method(&spec.python_name().to_string()) {
// If this is a known protocol method e.g. __contains__, then allow this
// symbol even though it's not an uppercase constant.
@ -158,32 +171,41 @@ pub fn impl_methods(
#(#trait_impls)*
#items
#[doc(hidden)]
#[allow(non_snake_case)]
impl #ty {
#(#associated_methods)*
}
};
})
}
pub fn gen_py_const(cls: &syn::Type, spec: &ConstSpec) -> TokenStream {
pub fn gen_py_const(cls: &syn::Type, spec: &ConstSpec) -> MethodAndMethodDef {
let member = &spec.rust_ident;
let wrapper_ident = format_ident!("__pymethod_{}__", member);
let deprecations = &spec.attributes.deprecations;
let python_name = &spec.null_terminated_python_name();
quote! {
let associated_method = quote! {
fn #wrapper_ident(py: _pyo3::Python<'_>) -> _pyo3::PyResult<_pyo3::PyObject> {
#deprecations
::std::result::Result::Ok(_pyo3::IntoPy::into_py(#cls::#member, py))
}
};
let method_def = quote! {
_pyo3::class::PyMethodDefType::ClassAttribute({
_pyo3::class::PyClassAttributeDef::new(
#python_name,
_pyo3::impl_::pymethods::PyClassAttributeFactory({
impl #cls {
#[doc(hidden)]
#[allow(non_snake_case)]
fn #wrapper_ident(py: _pyo3::Python<'_>) -> _pyo3::PyResult<_pyo3::PyObject> {
#deprecations
::std::result::Result::Ok(_pyo3::IntoPy::into_py(#cls::#member, py))
}
}
#cls::#wrapper_ident
})
_pyo3::impl_::pymethods::PyClassAttributeFactory(#cls::#wrapper_ident)
)
})
};
MethodAndMethodDef {
associated_method,
method_def,
}
}

View File

@ -15,9 +15,25 @@ use quote::{format_ident, quote, ToTokens};
use syn::Ident;
use syn::{ext::IdentExt, spanned::Spanned, Result};
/// Generated code for a single pymethod item.
pub struct MethodAndMethodDef {
/// The implementation of the Python wrapper for the pymethod
pub associated_method: TokenStream,
/// The method def which will be used to register this pymethod
pub method_def: TokenStream,
}
/// Generated code for a single pymethod item which is registered by a slot.
pub struct MethodAndSlotDef {
/// The implementation of the Python wrapper for the pymethod
pub associated_method: TokenStream,
/// The slot def which will be used to register this pymethod
pub slot_def: TokenStream,
}
pub enum GeneratedPyMethod {
Method(TokenStream),
Proto(TokenStream),
Method(MethodAndMethodDef),
Proto(MethodAndSlotDef),
SlotTraitImpl(String, TokenStream),
}
@ -262,93 +278,96 @@ pub fn impl_py_method_def(
cls: &syn::Type,
spec: &FnSpec<'_>,
flags: Option<TokenStream>,
) -> Result<TokenStream> {
) -> Result<MethodAndMethodDef> {
let wrapper_ident = format_ident!("__pymethod_{}__", spec.python_name);
let wrapper_def = spec.get_wrapper_function(&wrapper_ident, Some(cls))?;
let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls))?;
let add_flags = flags.map(|flags| quote!(.flags(#flags)));
let methoddef_type = match spec.tp {
FnType::FnStatic => quote!(Static),
FnType::FnClass => quote!(Class),
_ => quote!(Method),
};
let methoddef = spec.get_methoddef(quote! {{
impl #cls { #[doc(hidden)]#[allow(non_snake_case)] #wrapper_def }
#cls::#wrapper_ident
}});
Ok(quote! {
let methoddef = spec.get_methoddef(quote! { #cls::#wrapper_ident });
let method_def = quote! {
_pyo3::class::PyMethodDefType::#methoddef_type(#methoddef #add_flags)
};
Ok(MethodAndMethodDef {
associated_method,
method_def,
})
}
fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<TokenStream> {
fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<MethodAndSlotDef> {
let wrapper_ident = syn::Ident::new("__pymethod___new____", Span::call_site());
let wrapper = spec.get_wrapper_function(&wrapper_ident, Some(cls))?;
Ok(quote! {{
impl #cls {
#[doc(hidden)]
#[allow(non_snake_case)]
#wrapper
}
let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls))?;
let slot_def = quote! {
_pyo3::ffi::PyType_Slot {
slot: _pyo3::ffi::Py_tp_new,
pfunc: #cls::#wrapper_ident as _pyo3::ffi::newfunc as _
}
}})
};
Ok(MethodAndSlotDef {
associated_method,
slot_def,
})
}
fn impl_call_slot(cls: &syn::Type, mut spec: FnSpec<'_>) -> Result<TokenStream> {
fn impl_call_slot(cls: &syn::Type, mut spec: FnSpec<'_>) -> Result<MethodAndSlotDef> {
// HACK: __call__ proto slot must always use varargs calling convention, so change the spec.
// Probably indicates there's a refactoring opportunity somewhere.
spec.convention = CallingConvention::Varargs;
let wrapper_ident = syn::Ident::new("__pymethod___call____", Span::call_site());
let wrapper = spec.get_wrapper_function(&wrapper_ident, Some(cls))?;
Ok(quote! {{
impl #cls {
#[doc(hidden)]
#[allow(non_snake_case)]
#wrapper
}
let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls))?;
let slot_def = quote! {
_pyo3::ffi::PyType_Slot {
slot: _pyo3::ffi::Py_tp_call,
pfunc: #cls::#wrapper_ident as _pyo3::ffi::ternaryfunc as _
}
}})
};
Ok(MethodAndSlotDef {
associated_method,
slot_def,
})
}
fn impl_traverse_slot(cls: &syn::Type, spec: FnSpec<'_>) -> TokenStream {
fn impl_traverse_slot(cls: &syn::Type, spec: FnSpec<'_>) -> MethodAndSlotDef {
let ident = spec.name;
quote! {{
impl #cls {
pub unsafe extern "C" fn __pymethod_traverse__(
slf: *mut _pyo3::ffi::PyObject,
visit: _pyo3::ffi::visitproc,
arg: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int
{
let pool = _pyo3::GILPool::new();
let py = pool.python();
_pyo3::callback::abort_on_traverse_panic(::std::panic::catch_unwind(move || {
let slf = py.from_borrowed_ptr::<_pyo3::PyCell<#cls>>(slf);
let associated_method = quote! {
pub unsafe extern "C" fn __pymethod_traverse__(
slf: *mut _pyo3::ffi::PyObject,
visit: _pyo3::ffi::visitproc,
arg: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int
{
let pool = _pyo3::GILPool::new();
let py = pool.python();
_pyo3::callback::abort_on_traverse_panic(::std::panic::catch_unwind(move || {
let slf = py.from_borrowed_ptr::<_pyo3::PyCell<#cls>>(slf);
let visit = _pyo3::class::gc::PyVisit::from_raw(visit, arg, py);
let borrow = slf.try_borrow();
if let ::std::result::Result::Ok(borrow) = borrow {
_pyo3::impl_::pymethods::unwrap_traverse_result(borrow.#ident(visit))
} else {
0
}
}))
}
let visit = _pyo3::class::gc::PyVisit::from_raw(visit, arg, py);
let borrow = slf.try_borrow();
if let ::std::result::Result::Ok(borrow) = borrow {
_pyo3::impl_::pymethods::unwrap_traverse_result(borrow.#ident(visit))
} else {
0
}
}))
}
};
let slot_def = quote! {
_pyo3::ffi::PyType_Slot {
slot: _pyo3::ffi::Py_tp_traverse,
pfunc: #cls::__pymethod_traverse__ as _pyo3::ffi::traverseproc as _
}
}}
};
MethodAndSlotDef {
associated_method,
slot_def,
}
}
fn impl_py_class_attribute(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<TokenStream> {
fn impl_py_class_attribute(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<MethodAndMethodDef> {
let (py_arg, args) = split_off_python_arg(&spec.args);
ensure_spanned!(
args.is_empty(),
@ -366,30 +385,31 @@ fn impl_py_class_attribute(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<To
let deprecations = &spec.deprecations;
let python_name = spec.null_terminated_python_name();
let classattr = quote! {
let associated_method = quote! {
fn #wrapper_ident(py: _pyo3::Python<'_>) -> _pyo3::PyResult<_pyo3::PyObject> {
#deprecations
let mut ret = #fncall;
if false {
use _pyo3::impl_::ghost::IntoPyResult;
ret.assert_into_py_result();
}
_pyo3::callback::convert(py, ret)
}
};
let method_def = quote! {
_pyo3::class::PyMethodDefType::ClassAttribute({
_pyo3::class::PyClassAttributeDef::new(
#python_name,
_pyo3::impl_::pymethods::PyClassAttributeFactory({
impl #cls {
#[doc(hidden)]
#[allow(non_snake_case)]
fn #wrapper_ident(py: _pyo3::Python<'_>) -> _pyo3::PyResult<_pyo3::PyObject> {
#deprecations
let mut ret = #fncall;
if false {
use _pyo3::impl_::ghost::IntoPyResult;
ret.assert_into_py_result();
}
_pyo3::callback::convert(py, ret)
}
}
#cls::#wrapper_ident
})
_pyo3::impl_::pymethods::PyClassAttributeFactory(#cls::#wrapper_ident)
)
})
};
Ok(classattr)
Ok(MethodAndMethodDef {
associated_method,
method_def,
})
}
fn impl_call_setter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<TokenStream> {
@ -415,7 +435,10 @@ fn impl_call_setter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<TokenStre
}
// Used here for PropertyType::Function, used in pyclass for descriptors.
pub fn impl_py_setter_def(cls: &syn::Type, property_type: PropertyType<'_>) -> Result<TokenStream> {
pub fn impl_py_setter_def(
cls: &syn::Type,
property_type: PropertyType<'_>,
) -> Result<MethodAndMethodDef> {
let python_name = property_type.null_terminated_python_name()?;
let deprecations = property_type.deprecations();
let doc = property_type.doc();
@ -462,40 +485,43 @@ pub fn impl_py_setter_def(cls: &syn::Type, property_type: PropertyType<'_>) -> R
format_ident!("__pymethod_set_{}__", spec.name)
}
};
Ok(quote! {
let associated_method = quote! {
unsafe extern "C" fn #wrapper_ident(
_slf: *mut _pyo3::ffi::PyObject,
_value: *mut _pyo3::ffi::PyObject,
_: *mut ::std::os::raw::c_void
) -> ::std::os::raw::c_int {
let gil = _pyo3::GILPool::new();
let _py = gil.python();
_pyo3::callback::panic_result_into_callback_output(_py, ::std::panic::catch_unwind(move || -> _pyo3::PyResult<_> {
#slf
let _value = _py
.from_borrowed_ptr_or_opt(_value)
.ok_or_else(|| {
_pyo3::exceptions::PyAttributeError::new_err("can't delete attribute")
})?;
let _val = _pyo3::FromPyObject::extract(_value)?;
_pyo3::callback::convert(_py, #setter_impl)
}))
}
};
let method_def = quote! {
_pyo3::class::PyMethodDefType::Setter({
#deprecations
_pyo3::class::PySetterDef::new(
#python_name,
_pyo3::impl_::pymethods::PySetter({
impl #cls {
#[doc(hidden)]
#[allow(non_snake_case)]
unsafe extern "C" fn #wrapper_ident(
_slf: *mut _pyo3::ffi::PyObject,
_value: *mut _pyo3::ffi::PyObject,
_: *mut ::std::os::raw::c_void
) -> ::std::os::raw::c_int {
let gil = _pyo3::GILPool::new();
let _py = gil.python();
_pyo3::callback::panic_result_into_callback_output(_py, ::std::panic::catch_unwind(move || -> _pyo3::PyResult<_> {
#slf
let _value = _py
.from_borrowed_ptr_or_opt(_value)
.ok_or_else(|| {
_pyo3::exceptions::PyAttributeError::new_err("can't delete attribute")
})?;
let _val = _pyo3::FromPyObject::extract(_value)?;
_pyo3::callback::convert(_py, #setter_impl)
}))
}
}
#cls::#wrapper_ident
}),
_pyo3::impl_::pymethods::PySetter(#cls::#wrapper_ident),
#doc
)
})
};
Ok(MethodAndMethodDef {
associated_method,
method_def,
})
}
@ -517,7 +543,10 @@ fn impl_call_getter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result<TokenStre
}
// Used here for PropertyType::Function, used in pyclass for descriptors.
pub fn impl_py_getter_def(cls: &syn::Type, property_type: PropertyType<'_>) -> Result<TokenStream> {
pub fn impl_py_getter_def(
cls: &syn::Type,
property_type: PropertyType<'_>,
) -> Result<MethodAndMethodDef> {
let python_name = property_type.null_terminated_python_name()?;
let deprecations = property_type.deprecations();
let doc = property_type.doc();
@ -581,33 +610,35 @@ pub fn impl_py_getter_def(cls: &syn::Type, property_type: PropertyType<'_>) -> R
}
};
Ok(quote! {
let associated_method = quote! {
unsafe extern "C" fn #wrapper_ident(
_slf: *mut _pyo3::ffi::PyObject,
_: *mut ::std::os::raw::c_void
) -> *mut _pyo3::ffi::PyObject {
let gil = _pyo3::GILPool::new();
let _py = gil.python();
_pyo3::callback::panic_result_into_callback_output(_py, ::std::panic::catch_unwind(move || -> _pyo3::PyResult<_> {
#slf
let item = #getter_impl;
#conversion
}))
}
};
let method_def = quote! {
_pyo3::class::PyMethodDefType::Getter({
#deprecations
_pyo3::class::PyGetterDef::new(
#python_name,
_pyo3::impl_::pymethods::PyGetter({
impl #cls {
#[doc(hidden)]
#[allow(non_snake_case)]
unsafe extern "C" fn #wrapper_ident(
_slf: *mut _pyo3::ffi::PyObject,
_: *mut ::std::os::raw::c_void
) -> *mut _pyo3::ffi::PyObject {
let gil = _pyo3::GILPool::new();
let _py = gil.python();
_pyo3::callback::panic_result_into_callback_output(_py, ::std::panic::catch_unwind(move || -> _pyo3::PyResult<_> {
#slf
let item = #getter_impl;
#conversion
}))
}
}
#cls::#wrapper_ident
}),
_pyo3::impl_::pymethods::PyGetter(#cls::#wrapper_ident),
#doc
)
})
};
Ok(MethodAndMethodDef {
associated_method,
method_def,
})
}
@ -1000,7 +1031,7 @@ impl SlotDef {
cls: &syn::Type,
spec: &FnSpec<'_>,
method_name: &str,
) -> Result<TokenStream> {
) -> Result<MethodAndSlotDef> {
let SlotDef {
slot,
func_ty,
@ -1032,24 +1063,26 @@ impl SlotDef {
*extract_error_mode,
return_mode.as_ref(),
)?;
Ok(quote!({
impl #cls {
#[doc(hidden)]
#[allow(non_snake_case)]
unsafe extern "C" fn #wrapper_ident(_raw_slf: *mut _pyo3::ffi::PyObject, #(#arg_idents: #arg_types),*) -> #ret_ty {
let _slf = _raw_slf;
let gil = _pyo3::GILPool::new();
let #py = gil.python();
_pyo3::callback::panic_result_into_callback_output(#py, ::std::panic::catch_unwind(move || -> _pyo3::PyResult<_> {
#body
}))
}
let associated_method = quote! {
unsafe extern "C" fn #wrapper_ident(_raw_slf: *mut _pyo3::ffi::PyObject, #(#arg_idents: #arg_types),*) -> #ret_ty {
let _slf = _raw_slf;
let gil = _pyo3::GILPool::new();
let #py = gil.python();
_pyo3::callback::panic_result_into_callback_output(#py, ::std::panic::catch_unwind(move || -> _pyo3::PyResult<_> {
#body
}))
}
};
let slot_def = quote! {
_pyo3::ffi::PyType_Slot {
slot: _pyo3::ffi::#slot,
pfunc: #cls::#wrapper_ident as _pyo3::ffi::#func_ty as _
}
}))
};
Ok(MethodAndSlotDef {
associated_method,
slot_def,
})
}
}

View File

@ -5,6 +5,7 @@ use crate::method::{FnSpec, FnType};
use crate::proto_method::impl_method_proto;
use crate::pyfunction::PyFunctionOptions;
use crate::pymethod;
use crate::pymethod::MethodAndMethodDef;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use quote::ToTokens;
@ -48,7 +49,8 @@ fn impl_proto_impl(
proto: &defs::Proto,
) -> syn::Result<TokenStream> {
let mut trait_impls = TokenStream::new();
let mut py_methods = Vec::new();
let mut associated_methods = Vec::new();
let mut method_defs = Vec::new();
let mut method_names = HashSet::new();
let module = proto.module();
@ -72,7 +74,10 @@ fn impl_proto_impl(
None
};
let method = if let FnType::Fn(_) = &fn_spec.tp {
let MethodAndMethodDef {
associated_method,
method_def,
} = if let FnType::Fn(_) = &fn_spec.tp {
pymethod::impl_py_method_def(ty, &fn_spec, flags)?
} else {
bail_spanned!(
@ -80,17 +85,24 @@ fn impl_proto_impl(
);
};
py_methods.push(method);
associated_methods.push(associated_method);
method_defs.push(method_def);
}
}
}
let items = impl_proto_items(method_names, py_methods, ty, proto);
let items = impl_proto_items(method_names, method_defs, ty, proto);
Ok(quote! {
const _: () = {
use ::pyo3 as _pyo3; // pyproto doesn't support specifying #[pyo3(crate)]
#trait_impls
#items
#[doc(hidden)]
#[allow(non_snake_case)]
impl #ty {
#(#associated_methods)*
}
};
})
}

View File

@ -109,24 +109,24 @@ error: Python objects are shared, so 'self' cannot be moved out of the Python in
124 | fn method_self_by_value(self){}
| ^^^^
error[E0592]: duplicate definitions with name `__pymethod___new____`
error[E0201]: duplicate definitions with name `__pymethod___new____`:
--> tests/ui/invalid_pymethods.rs:129:1
|
129 | #[pymethods]
| ^^^^^^^^^^^^
| |
| duplicate definitions for `__pymethod___new____`
| other definition for `__pymethod___new____`
| previous definition of `__pymethod___new____` here
| duplicate definition
|
= note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0592]: duplicate definitions with name `__pymethod_func__`
error[E0201]: duplicate definitions with name `__pymethod_func__`:
--> tests/ui/invalid_pymethods.rs:140:1
|
140 | #[pymethods]
| ^^^^^^^^^^^^
| |
| duplicate definitions for `__pymethod_func__`
| other definition for `__pymethod_func__`
| previous definition of `__pymethod_func__` here
| duplicate definition
|
= note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info)