refactor: inline pyclass_default_items trait

This commit is contained in:
David Hewitt 2022-02-08 19:39:37 +00:00
parent ada301773e
commit 4d471077b4
3 changed files with 31 additions and 52 deletions

View File

@ -497,23 +497,15 @@ fn impl_enum_class(
let cls = enum_.ident;
let variants = enum_.variants;
let pytypeinfo = impl_pytypeinfo(cls, args, None);
let pyclass_impls = PyClassImplsBuilder::new(
cls,
args,
methods_type,
unit_variants_as_items(cls, variants.iter().map(|v| v.ident)),
)
.doc(doc)
.impl_all();
let default_repr_impl = {
let default_repr_impl: syn::ImplItemMethod = {
let variants_repr = variants.iter().map(|variant| {
let variant_name = variant.ident;
// Assuming all variants are unit variants because they are the only type we support.
let repr = format!("{}.{}", cls, variant_name);
quote! { #cls::#variant_name => #repr, }
});
quote! {
syn::parse_quote! {
#[doc(hidden)]
#[allow(non_snake_case)]
#[pyo3(name = "__repr__")]
@ -533,7 +525,7 @@ fn impl_enum_class(
let variant_name = variant.ident;
quote! { #cls::#variant_name => #cls::#variant_name as #repr_type, }
});
quote! {
syn::parse_quote! {
#[doc(hidden)]
#[allow(non_snake_case)]
#[pyo3(name = "__int__")]
@ -553,7 +545,7 @@ fn impl_enum_class(
Ok(true.to_object(py)),
}
});
quote! {
syn::parse_quote! {
#[doc(hidden)]
#[allow(non_snake_case)]
#[pyo3(name = "__richcmp__")]
@ -584,8 +576,16 @@ fn impl_enum_class(
}
};
let default_items =
gen_default_items(cls, vec![default_repr_impl, default_richcmp, default_int]);
let mut default_methods = vec![default_repr_impl, default_richcmp, default_int];
let pyclass_impls = PyClassImplsBuilder::new(
cls,
args,
methods_type,
enum_default_items(cls, variants.iter().map(|v| v.ident), &mut default_methods),
)
.doc(doc)
.impl_all();
Ok(quote! {
const _: () = {
@ -595,14 +595,17 @@ fn impl_enum_class(
#pyclass_impls
#default_items
impl #cls {
#(#default_methods)*
}
};
})
}
fn unit_variants_as_items<'a>(
fn enum_default_items<'a>(
cls: &'a syn::Ident,
variant_names: impl IntoIterator<Item = &'a syn::Ident>,
unit_variant_names: impl IntoIterator<Item = &'a syn::Ident>,
default_items: &mut [syn::ImplItemMethod],
) -> TokenStream {
let cls_type = syn::parse_quote!(#cls);
let variant_to_attribute = |ident: &syn::Ident| ConstSpec {
@ -613,14 +616,16 @@ fn unit_variants_as_items<'a>(
deprecations: Default::default(),
},
};
let py_methods = variant_names
let py_methods = unit_variant_names
.into_iter()
.map(|var| gen_py_const(&cls_type, &variant_to_attribute(var)));
let slots = gen_default_items(cls, default_items);
quote! {
_pyo3::impl_::pyclass::PyClassItems {
methods: &[#(#py_methods),*],
slots: &[]
slots: &[#(#slots),*]
}
}
}
@ -918,9 +923,6 @@ impl<'a> PyClassImplsBuilder<'a> {
let collector = PyClassImplCollector::<Self>::new();
static INTRINSIC_ITEMS: PyClassItems = #default_items;
visitor(&INTRINSIC_ITEMS);
// This depends on Python implementation detail;
// an old slot entry will be overriden by newer ones.
visitor(collector.pyclass_default_items());
#pymethods_items
#pyproto_items
}

View File

@ -186,24 +186,20 @@ pub fn gen_py_const(cls: &syn::Type, spec: &ConstSpec) -> TokenStream {
}
}
pub fn gen_default_items(cls: &syn::Ident, method_defs: Vec<TokenStream>) -> TokenStream {
pub fn gen_default_items<'a>(
cls: &syn::Ident,
method_defs: &'a mut [syn::ImplItemMethod],
) -> impl Iterator<Item = TokenStream> + 'a {
// This function uses a lot of `unwrap()`; since method_defs are provided by us, they should
// all succeed.
let ty: syn::Type = syn::parse_quote!(#cls);
let mut method_defs: Vec<_> = method_defs
.into_iter()
.map(|token| syn::parse2::<syn::ImplItemMethod>(token).unwrap())
.collect();
let mut proto_impls = Vec::new();
for meth in &mut method_defs {
method_defs.into_iter().map(move |meth| {
let options = PyFunctionOptions::from_attrs(&mut meth.attrs).unwrap();
match pymethod::gen_py_method(&ty, &mut meth.sig, &mut meth.attrs, options).unwrap() {
GeneratedPyMethod::Proto(token_stream) => {
let attrs = get_cfg_attributes(&meth.attrs);
proto_impls.push(quote!(#(#attrs)* #token_stream))
quote!(#(#attrs)* #token_stream)
}
GeneratedPyMethod::SlotTraitImpl(..) => {
panic!("SlotFragment methods cannot have default implementation!")
@ -212,23 +208,7 @@ pub fn gen_default_items(cls: &syn::Ident, method_defs: Vec<TokenStream>) -> Tok
panic!("Only protocol methods can have default implementation!")
}
}
}
quote! {
impl #cls {
#(#method_defs)*
}
impl _pyo3::impl_::pyclass::PyClassDefaultItems<#cls>
for _pyo3::impl_::pyclass::PyClassImplCollector<#cls> {
fn pyclass_default_items(self) -> &'static _pyo3::impl_::pyclass::PyClassItems {
static ITEMS: _pyo3::impl_::pyclass::PyClassItems = _pyo3::impl_::pyclass::PyClassItems {
methods: &[],
slots: &[#(#proto_impls),*]
};
&ITEMS
}
}
}
})
}
fn impl_py_methods(

View File

@ -757,9 +757,6 @@ mod pyproto_traits {
#[cfg(feature = "pyproto")]
pub use pyproto_traits::*;
// items that PyO3 implements by default, but can be overidden by the users.
items_trait!(PyClassDefaultItems, pyclass_default_items);
// Protocol slots from #[pymethods] if not using inventory.
#[cfg(not(feature = "multiple-pymethods"))]
items_trait!(PyMethodsProtocolItems, methods_protocol_items);