diff --git a/pyo3-macros-backend/src/method.rs b/pyo3-macros-backend/src/method.rs index 877a00f0..9af8b31d 100644 --- a/pyo3-macros-backend/src/method.rs +++ b/pyo3-macros-backend/src/method.rs @@ -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, - pub krate: syn::Path, pub unsafety: Option, } @@ -266,7 +265,6 @@ impl<'a> FnSpec<'a> { ) -> Result> { 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(); diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 19ff3d84..56659673 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -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 { +) -> syn::Result { 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, -) -> Vec { +) -> Vec { 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, -) -> syn::Result> { +) -> syn::Result> { 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, - default_slots: Vec, + default_methods: Vec, + default_slots: Vec, doc: Option, } @@ -676,8 +677,8 @@ impl<'a> PyClassImplsBuilder<'a> { cls: &'a syn::Ident, attr: &'a PyClassArgs, methods_type: PyClassMethodsType, - default_methods: Vec, - default_slots: Vec, + default_methods: Vec, + default_slots: Vec, ) -> 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::::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 } } diff --git a/pyo3-macros-backend/src/pyfunction.rs b/pyo3-macros-backend/src/pyfunction.rs index c4403a40..f3138e94 100644 --- a/pyo3-macros-backend/src/pyfunction.rs +++ b/pyo3-macros-backend/src/pyfunction.rs @@ -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 ` // 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) diff --git a/pyo3-macros-backend/src/pyimpl.rs b/pyo3-macros-backend/src/pyimpl.rs index 1d796970..d0a1b615 100644 --- a/pyo3-macros-backend/src/pyimpl.rs +++ b/pyo3-macros-backend/src/pyimpl.rs @@ -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, } } diff --git a/pyo3-macros-backend/src/pymethod.rs b/pyo3-macros-backend/src/pymethod.rs index 1a31c95a..dc4af8e0 100644 --- a/pyo3-macros-backend/src/pymethod.rs +++ b/pyo3-macros-backend/src/pymethod.rs @@ -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, -) -> Result { +) -> Result { 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 { +fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result { 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 { +fn impl_call_slot(cls: &syn::Type, mut spec: FnSpec<'_>) -> Result { // 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 { +fn impl_py_class_attribute(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result { 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) -> _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 { @@ -415,7 +435,10 @@ fn impl_call_setter(cls: &syn::Type, spec: &FnSpec<'_>) -> syn::Result) -> Result { +pub fn impl_py_setter_def( + cls: &syn::Type, + property_type: PropertyType<'_>, +) -> Result { 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) -> Result { +pub fn impl_py_getter_def( + cls: &syn::Type, + property_type: PropertyType<'_>, +) -> Result { 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 { + ) -> Result { 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, + }) } } diff --git a/pyo3-macros-backend/src/pyproto.rs b/pyo3-macros-backend/src/pyproto.rs index cf9a60fe..c9965a24 100644 --- a/pyo3-macros-backend/src/pyproto.rs +++ b/pyo3-macros-backend/src/pyproto.rs @@ -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 { 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)* + } }; }) } diff --git a/tests/ui/invalid_pymethods.stderr b/tests/ui/invalid_pymethods.stderr index 590d0295..f64bdcdc 100644 --- a/tests/ui/invalid_pymethods.stderr +++ b/tests/ui/invalid_pymethods.stderr @@ -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)