pyclass: simplify generated code for PyClassImpl

This commit is contained in:
David Hewitt 2022-01-11 22:06:18 +00:00
parent 558549e1c2
commit ecfd2c2434
9 changed files with 157 additions and 268 deletions

View File

@ -858,16 +858,11 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
type BaseType = PyAny; type BaseType = PyAny;
type ThreadChecker = pyo3::impl_::pyclass::ThreadCheckerStub<MyClass>; type ThreadChecker = pyo3::impl_::pyclass::ThreadCheckerStub<MyClass>;
fn for_each_method_def(visitor: &mut dyn FnMut(&[pyo3::class::PyMethodDefType])) { fn for_all_items(visitor: &mut dyn FnMut(&pyo3::impl_::pyclass::PyClassItems)) {
use pyo3::impl_::pyclass::*; use pyo3::impl_::pyclass::*;
let collector = PyClassImplCollector::<MyClass>::new(); let collector = PyClassImplCollector::<MyClass>::new();
visitor(collector.py_methods()); visitor(collector.py_methods());
visitor(collector.py_class_descriptors()); visitor(collector.pyclass_intrinsic_items());
visitor(collector.object_protocol_methods());
visitor(collector.async_protocol_methods());
visitor(collector.descr_protocol_methods());
visitor(collector.mapping_protocol_methods());
visitor(collector.number_protocol_methods());
} }
fn get_new() -> Option<pyo3::ffi::newfunc> { fn get_new() -> Option<pyo3::ffi::newfunc> {
use pyo3::impl_::pyclass::*; use pyo3::impl_::pyclass::*;
@ -884,21 +879,6 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
let collector = PyClassImplCollector::<Self>::new(); let collector = PyClassImplCollector::<Self>::new();
collector.free_impl() collector.free_impl()
} }
fn for_each_proto_slot(visitor: &mut dyn FnMut(&[pyo3::ffi::PyType_Slot])) {
// Implementation which uses dtolnay specialization to load all slots.
use pyo3::impl_::pyclass::*;
let collector = PyClassImplCollector::<Self>::new();
visitor(collector.object_protocol_slots());
visitor(collector.number_protocol_slots());
visitor(collector.iter_protocol_slots());
visitor(collector.gc_protocol_slots());
visitor(collector.descr_protocol_slots());
visitor(collector.mapping_protocol_slots());
visitor(collector.sequence_protocol_slots());
visitor(collector.async_protocol_slots());
visitor(collector.buffer_protocol_slots());
visitor(collector.methods_protocol_slots());
}
} }
# Python::with_gil(|py| { # Python::with_gil(|py| {
# let cls = py.get_type::<MyClass>(); # let cls = py.get_type::<MyClass>();

View File

@ -64,27 +64,13 @@ impl Proto {
}) })
} }
pub(crate) fn slots_trait(&self) -> syn::Ident { pub(crate) fn items_trait(&self) -> syn::Ident {
syn::Ident::new(&format!("Py{}ProtocolSlots", self.name), Span::call_site()) syn::Ident::new(&format!("Py{}ProtocolItems", self.name), Span::call_site())
} }
pub(crate) fn slots_trait_slots(&self) -> syn::Ident { pub(crate) fn items_trait_items(&self) -> syn::Ident {
syn::Ident::new( syn::Ident::new(
&format!("{}_protocol_slots", self.name.to_ascii_lowercase()), &format!("{}_protocol_items", self.name.to_ascii_lowercase()),
Span::call_site(),
)
}
pub(crate) fn methods_trait(&self) -> syn::Ident {
syn::Ident::new(
&format!("Py{}ProtocolMethods", self.name),
Span::call_site(),
)
}
pub(crate) fn methods_trait_methods(&self) -> syn::Ident {
syn::Ident::new(
&format!("{}_protocol_methods", self.name.to_ascii_lowercase()),
Span::call_site(), Span::call_site(),
) )
} }

View File

@ -5,7 +5,7 @@ use crate::attributes::{
}; };
use crate::deprecations::Deprecations; use crate::deprecations::Deprecations;
use crate::konst::{ConstAttributes, ConstSpec}; use crate::konst::{ConstAttributes, ConstSpec};
use crate::pyimpl::{gen_default_slot_impls, gen_py_const, PyClassMethodsType}; use crate::pyimpl::{gen_default_items, gen_py_const, PyClassMethodsType};
use crate::pymethod::{impl_py_getter_def, impl_py_setter_def, PropertyType}; use crate::pymethod::{impl_py_getter_def, impl_py_setter_def, PropertyType};
use crate::utils::{self, get_pyo3_crate, unwrap_group, PythonDoc}; use crate::utils::{self, get_pyo3_crate, unwrap_group, PythonDoc};
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
@ -477,7 +477,7 @@ fn impl_enum_class(
} }
}; };
let default_impls = gen_default_slot_impls(cls, vec![default_repr_impl]); let default_impls = gen_default_items(cls, vec![default_repr_impl]);
Ok(quote! { Ok(quote! {
const _: () = { const _: () = {
use #krate as _pyo3; use #krate as _pyo3;
@ -511,12 +511,15 @@ fn unit_variants_as_descriptors<'a>(
.map(|var| gen_py_const(&cls_type, &variant_to_attribute(var))); .map(|var| gen_py_const(&cls_type, &variant_to_attribute(var)));
quote! { quote! {
impl _pyo3::impl_::pyclass::PyClassDescriptors<#cls> impl _pyo3::impl_::pyclass::PyClassIntrinsicItems<#cls>
for _pyo3::impl_::pyclass::PyClassImplCollector<#cls> for _pyo3::impl_::pyclass::PyClassImplCollector<#cls>
{ {
fn py_class_descriptors(self) -> &'static [_pyo3::impl_::pymethods::PyMethodDefType] { fn pyclass_intrinsic_items(self) -> &'static _pyo3::impl_::pyclass::PyClassItems {
static METHODS: &[_pyo3::impl_::pymethods::PyMethodDefType] = &[#(#py_methods),*]; static ITEMS: _pyo3::impl_::pyclass::PyClassItems = _pyo3::impl_::pyclass::PyClassItems {
METHODS methods: &[#(#py_methods),*],
slots: &[]
};
&ITEMS
} }
} }
} }
@ -574,12 +577,15 @@ fn impl_descriptors(
.collect::<syn::Result<_>>()?; .collect::<syn::Result<_>>()?;
Ok(quote! { Ok(quote! {
impl _pyo3::impl_::pyclass::PyClassDescriptors<#cls> impl _pyo3::impl_::pyclass::PyClassIntrinsicItems<#cls>
for _pyo3::impl_::pyclass::PyClassImplCollector<#cls> for _pyo3::impl_::pyclass::PyClassImplCollector<#cls>
{ {
fn py_class_descriptors(self) -> &'static [_pyo3::impl_::pymethods::PyMethodDefType] { fn pyclass_intrinsic_items(self) -> &'static _pyo3::impl_::pyclass::PyClassItems {
static METHODS: &[_pyo3::impl_::pymethods::PyMethodDefType] = &[#(#py_methods),*]; static ITEMS: _pyo3::impl_::pyclass::PyClassItems = _pyo3::impl_::pyclass::PyClassItems {
METHODS methods: &[#(#py_methods),*],
slots: &[]
};
&ITEMS
} }
} }
}) })
@ -758,15 +764,10 @@ impl<'a> PyClassImplsBuilder<'a> {
quote! { _pyo3::impl_::pyclass::ThreadCheckerStub<#cls> } quote! { _pyo3::impl_::pyclass::ThreadCheckerStub<#cls> }
}; };
let (for_each_py_method, methods_protos, inventory, inventory_class) = match self let (pymethods_items, inventory, inventory_class) = match self.methods_type {
.methods_type PyClassMethodsType::Specialization => {
{ (quote! { visitor(collector.py_methods()); }, None, None)
PyClassMethodsType::Specialization => ( }
quote! { visitor(collector.py_methods()); },
quote! { visitor(collector.methods_protocol_slots()); },
None,
None,
),
PyClassMethodsType::Inventory => { PyClassMethodsType::Inventory => {
// To allow multiple #[pymethods] block, we define inventory types. // To allow multiple #[pymethods] block, we define inventory types.
let inventory_class_name = syn::Ident::new( let inventory_class_name = syn::Ident::new(
@ -776,12 +777,7 @@ impl<'a> PyClassImplsBuilder<'a> {
( (
quote! { quote! {
for inventory in _pyo3::inventory::iter::<<Self as _pyo3::impl_::pyclass::PyClassImpl>::Inventory>() { for inventory in _pyo3::inventory::iter::<<Self as _pyo3::impl_::pyclass::PyClassImpl>::Inventory>() {
visitor(_pyo3::impl_::pyclass::PyClassInventory::methods(inventory)); visitor(_pyo3::impl_::pyclass::PyClassInventory::items(inventory));
}
},
quote! {
for inventory in _pyo3::inventory::iter::<<Self as _pyo3::impl_::pyclass::PyClassImpl>::Inventory>() {
visitor(_pyo3::impl_::pyclass::PyClassInventory::slots(inventory));
} }
}, },
Some(quote! { type Inventory = #inventory_class_name; }), Some(quote! { type Inventory = #inventory_class_name; }),
@ -790,29 +786,17 @@ impl<'a> PyClassImplsBuilder<'a> {
} }
}; };
let pyproto_method_visitors = if cfg!(feature = "pyproto") { let pyproto_items = if cfg!(feature = "pyproto") {
Some(quote! { Some(quote! {
visitor(collector.object_protocol_methods()); visitor(collector.object_protocol_items());
visitor(collector.async_protocol_methods()); visitor(collector.number_protocol_items());
visitor(collector.descr_protocol_methods()); visitor(collector.iter_protocol_items());
visitor(collector.mapping_protocol_methods()); visitor(collector.gc_protocol_items());
visitor(collector.number_protocol_methods()); visitor(collector.descr_protocol_items());
}) visitor(collector.mapping_protocol_items());
} else { visitor(collector.sequence_protocol_items());
None visitor(collector.async_protocol_items());
}; visitor(collector.buffer_protocol_items());
let pyproto_proto_visitors = if cfg!(feature = "pyproto") {
Some(quote! {
visitor(collector.object_protocol_slots());
visitor(collector.number_protocol_slots());
visitor(collector.iter_protocol_slots());
visitor(collector.gc_protocol_slots());
visitor(collector.descr_protocol_slots());
visitor(collector.mapping_protocol_slots());
visitor(collector.sequence_protocol_slots());
visitor(collector.async_protocol_slots());
visitor(collector.buffer_protocol_slots());
}) })
} else { } else {
None None
@ -830,12 +814,15 @@ impl<'a> PyClassImplsBuilder<'a> {
type ThreadChecker = #thread_checker; type ThreadChecker = #thread_checker;
#inventory #inventory
fn for_each_method_def(visitor: &mut dyn ::std::ops::FnMut(&[_pyo3::class::PyMethodDefType])) { fn for_all_items(visitor: &mut dyn ::std::ops::FnMut(& _pyo3::impl_::pyclass::PyClassItems)) {
use _pyo3::impl_::pyclass::*; use _pyo3::impl_::pyclass::*;
let collector = PyClassImplCollector::<Self>::new(); let collector = PyClassImplCollector::<Self>::new();
#for_each_py_method; visitor(collector.pyclass_intrinsic_items());
visitor(collector.py_class_descriptors()); // This depends on Python implementation detail;
#pyproto_method_visitors // an old slot entry will be overriden by newer ones.
visitor(collector.pyclass_default_items());
#pymethods_items
#pyproto_items
} }
fn get_new() -> ::std::option::Option<_pyo3::ffi::newfunc> { fn get_new() -> ::std::option::Option<_pyo3::ffi::newfunc> {
use _pyo3::impl_::pyclass::*; use _pyo3::impl_::pyclass::*;
@ -853,17 +840,6 @@ impl<'a> PyClassImplsBuilder<'a> {
collector.free_impl() collector.free_impl()
} }
fn for_each_proto_slot(visitor: &mut dyn ::std::ops::FnMut(&[_pyo3::ffi::PyType_Slot])) {
// Implementation which uses dtolnay specialization to load all slots.
use _pyo3::impl_::pyclass::*;
let collector = PyClassImplCollector::<Self>::new();
// This depends on Python implementation detail;
// an old slot entry will be overriden by newer ones.
visitor(collector.py_class_default_slots());
#pyproto_proto_visitors
#methods_protos
}
#dict_offset #dict_offset
#weaklist_offset #weaklist_offset
@ -933,31 +909,20 @@ fn define_inventory_class(inventory_class_name: &syn::Ident) -> TokenStream {
quote! { quote! {
#[doc(hidden)] #[doc(hidden)]
pub struct #inventory_class_name { pub struct #inventory_class_name {
methods: &'static [_pyo3::class::PyMethodDefType], items: _pyo3::impl_::pyclass::PyClassItems,
slots: &'static [_pyo3::ffi::PyType_Slot],
} }
impl #inventory_class_name { impl #inventory_class_name {
const fn new( const fn new(items: _pyo3::impl_::pyclass::PyClassItems) -> Self {
methods: &'static [_pyo3::class::PyMethodDefType], Self { items }
slots: &'static [_pyo3::ffi::PyType_Slot],
) -> Self {
Self { methods, slots }
} }
} }
impl _pyo3::impl_::pyclass::PyClassInventory for #inventory_class_name { impl _pyo3::impl_::pyclass::PyClassInventory for #inventory_class_name {
fn methods(&'static self) -> &'static [_pyo3::class::PyMethodDefType] { fn items(&self) -> &_pyo3::impl_::pyclass::PyClassItems {
self.methods &self.items
}
fn slots(&'static self) -> &'static [_pyo3::ffi::PyType_Slot] {
self.slots
} }
} }
// inventory requires these bounds
unsafe impl ::std::marker::Send for #inventory_class_name {}
unsafe impl ::std::marker::Sync for #inventory_class_name {}
_pyo3::inventory::collect!(#inventory_class_name); _pyo3::inventory::collect!(#inventory_class_name);
} }
} }

View File

@ -150,35 +150,19 @@ pub fn impl_methods(
let krate = get_pyo3_crate(&options.krate); let krate = get_pyo3_crate(&options.krate);
Ok(match methods_type { let items = match methods_type {
PyClassMethodsType::Specialization => { PyClassMethodsType::Specialization => impl_py_methods(ty, methods, proto_impls),
let methods_registration = impl_py_methods(ty, methods); PyClassMethodsType::Inventory => submit_methods_inventory(ty, methods, proto_impls),
let protos_registration = impl_protos(ty, proto_impls); };
quote! { Ok(quote! {
const _: () = { const _: () = {
use #krate as _pyo3; use #krate as _pyo3;
#(#trait_impls)* #(#trait_impls)*
#protos_registration #items
};
#methods_registration
};
}
}
PyClassMethodsType::Inventory => {
let inventory = submit_methods_inventory(ty, methods, proto_impls);
quote! {
const _: () = {
use #krate as _pyo3;
#(#trait_impls)*
#inventory
};
}
}
}) })
} }
@ -202,7 +186,7 @@ pub fn gen_py_const(cls: &syn::Type, spec: &ConstSpec) -> TokenStream {
} }
} }
pub fn gen_default_slot_impls(cls: &syn::Ident, method_defs: Vec<TokenStream>) -> TokenStream { pub fn gen_default_items(cls: &syn::Ident, method_defs: Vec<TokenStream>) -> TokenStream {
// This function uses a lot of `unwrap()`; since method_defs are provided by us, they should // This function uses a lot of `unwrap()`; since method_defs are provided by us, they should
// all succeed. // all succeed.
let ty: syn::Type = syn::parse_quote!(#cls); let ty: syn::Type = syn::parse_quote!(#cls);
@ -234,23 +218,34 @@ pub fn gen_default_slot_impls(cls: &syn::Ident, method_defs: Vec<TokenStream>) -
impl #cls { impl #cls {
#(#method_defs)* #(#method_defs)*
} }
impl ::pyo3::impl_::pyclass::PyClassDefaultSlots<#cls> impl ::pyo3::impl_::pyclass::PyClassDefaultItems<#cls>
for ::pyo3::impl_::pyclass::PyClassImplCollector<#cls> { for ::pyo3::impl_::pyclass::PyClassImplCollector<#cls> {
fn py_class_default_slots(self) -> &'static [::pyo3::ffi::PyType_Slot] { fn pyclass_default_items(self) -> &'static _pyo3::impl_::pyclass::PyClassItems {
&[#(#proto_impls),*] static ITEMS: _pyo3::impl_::pyclass::PyClassItems = _pyo3::impl_::pyclass::PyClassItems {
methods: &[],
slots: &[#(#proto_impls),*]
};
&ITEMS
} }
} }
} }
} }
fn impl_py_methods(ty: &syn::Type, methods: Vec<TokenStream>) -> TokenStream { fn impl_py_methods(
ty: &syn::Type,
methods: Vec<TokenStream>,
proto_impls: Vec<TokenStream>,
) -> TokenStream {
quote! { quote! {
impl _pyo3::impl_::pyclass::PyMethods<#ty> impl _pyo3::impl_::pyclass::PyMethods<#ty>
for _pyo3::impl_::pyclass::PyClassImplCollector<#ty> for _pyo3::impl_::pyclass::PyClassImplCollector<#ty>
{ {
fn py_methods(self) -> &'static [_pyo3::impl_::pymethods::PyMethodDefType] { fn py_methods(self) -> &'static _pyo3::impl_::pyclass::PyClassItems {
static METHODS: &[_pyo3::impl_::pymethods::PyMethodDefType] = &[#(#methods),*]; static ITEMS: _pyo3::impl_::pyclass::PyClassItems = _pyo3::impl_::pyclass::PyClassItems {
METHODS methods: &[#(#methods),*],
slots: &[#(#proto_impls),*]
};
&ITEMS
} }
} }
} }
@ -298,18 +293,6 @@ fn add_shared_proto_slots(
assert!(implemented_proto_fragments.is_empty()); assert!(implemented_proto_fragments.is_empty());
} }
fn impl_protos(ty: &syn::Type, proto_impls: Vec<TokenStream>) -> TokenStream {
quote! {
impl _pyo3::impl_::pyclass::PyMethodsProtocolSlots<#ty>
for _pyo3::impl_::pyclass::PyClassImplCollector<#ty>
{
fn methods_protocol_slots(self) -> &'static [_pyo3::ffi::PyType_Slot] {
&[#(#proto_impls),*]
}
}
}
}
fn submit_methods_inventory( fn submit_methods_inventory(
ty: &syn::Type, ty: &syn::Type,
methods: Vec<TokenStream>, methods: Vec<TokenStream>,
@ -318,7 +301,7 @@ fn submit_methods_inventory(
quote! { quote! {
_pyo3::inventory::submit! { _pyo3::inventory::submit! {
type Inventory = <#ty as _pyo3::impl_::pyclass::PyClassImpl>::Inventory; type Inventory = <#ty as _pyo3::impl_::pyclass::PyClassImpl>::Inventory;
Inventory::new(&[#(#methods),*], &[#(#proto_impls),*]) Inventory::new(_pyo3::impl_::pyclass::PyClassItems { methods: &[#(#methods),*], slots: &[#(#proto_impls),*] })
} }
} }
} }

View File

@ -84,55 +84,30 @@ fn impl_proto_impl(
} }
} }
} }
let normal_methods = impl_normal_methods(py_methods, ty, proto); let items = impl_proto_items(method_names, py_methods, ty, proto);
let protocol_methods = impl_proto_methods(method_names, ty, proto);
Ok(quote! { Ok(quote! {
const _: () = { const _: () = {
use ::pyo3 as _pyo3; // pyproto doesn't support specifying #[pyo3(crate)] use ::pyo3 as _pyo3; // pyproto doesn't support specifying #[pyo3(crate)]
#trait_impls #trait_impls
#normal_methods #items
#protocol_methods
}; };
}) })
} }
fn impl_normal_methods( fn impl_proto_items(
method_names: HashSet<String>,
py_methods: Vec<TokenStream>, py_methods: Vec<TokenStream>,
ty: &syn::Type, ty: &syn::Type,
proto: &defs::Proto, proto: &defs::Proto,
) -> TokenStream { ) -> TokenStream {
if py_methods.is_empty() { if method_names.is_empty() && py_methods.is_empty() {
return TokenStream::default();
}
let methods_trait = proto.methods_trait();
let methods_trait_methods = proto.methods_trait_methods();
quote! {
impl _pyo3::impl_::pyclass::#methods_trait<#ty>
for _pyo3::impl_::pyclass::PyClassImplCollector<#ty>
{
fn #methods_trait_methods(self) -> &'static [_pyo3::impl_::pymethods::PyMethodDefType] {
static METHODS: &[_pyo3::impl_::pymethods::PyMethodDefType] =
&[#(#py_methods),*];
METHODS
}
}
}
}
fn impl_proto_methods(
method_names: HashSet<String>,
ty: &syn::Type,
proto: &defs::Proto,
) -> TokenStream {
if method_names.is_empty() {
return TokenStream::default(); return TokenStream::default();
} }
let module = proto.module(); let module = proto.module();
let slots_trait = proto.slots_trait(); let items_trait = proto.items_trait();
let slots_trait_slots = proto.slots_trait_slots(); let items_trait_items = proto.items_trait_items();
let mut tokens = proto let mut tokens = proto
.slot_defs(method_names) .slot_defs(method_names)
@ -153,11 +128,15 @@ fn impl_proto_methods(
} }
quote! { quote! {
impl _pyo3::impl_::pyclass::#slots_trait<#ty> impl _pyo3::impl_::pyclass::#items_trait<#ty>
for _pyo3::impl_::pyclass::PyClassImplCollector<#ty> for _pyo3::impl_::pyclass::PyClassImplCollector<#ty>
{ {
fn #slots_trait_slots(self) -> &'static [_pyo3::ffi::PyType_Slot] { fn #items_trait_items(self) -> &'static _pyo3::impl_::pyclass::PyClassItems {
&[#(#tokens),*] static ITEMS: _pyo3::impl_::pyclass::PyClassItems = _pyo3::impl_::pyclass::PyClassItems {
methods: &[#(#py_methods),*],
slots: &[#(#tokens),*]
};
&ITEMS
} }
} }
} }

View File

@ -7,7 +7,12 @@ use crate::{
type_object::{PyLayout, PyTypeObject}, type_object::{PyLayout, PyTypeObject},
PyCell, PyClass, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python, PyCell, PyClass, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python,
}; };
use std::{marker::PhantomData, os::raw::{c_int, c_void}, ptr::NonNull, thread}; use std::{
marker::PhantomData,
os::raw::{c_int, c_void},
ptr::NonNull,
thread,
};
/// Gets the offset of the dictionary from the start of the object in bytes. /// Gets the offset of the dictionary from the start of the object in bytes.
#[inline] #[inline]
@ -128,6 +133,14 @@ impl<T> Clone for PyClassImplCollector<T> {
impl<T> Copy for PyClassImplCollector<T> {} impl<T> Copy for PyClassImplCollector<T> {}
pub struct PyClassItems {
pub methods: &'static [PyMethodDefType],
pub slots: &'static [ffi::PyType_Slot],
}
// Allow PyClassItems in statics
unsafe impl Sync for PyClassItems {}
/// Implements the underlying functionality of `#[pyclass]`, assembled by various proc macros. /// Implements the underlying functionality of `#[pyclass]`, assembled by various proc macros.
/// ///
/// Users are discouraged from implementing this trait manually; it is a PyO3 implementation detail /// Users are discouraged from implementing this trait manually; it is a PyO3 implementation detail
@ -163,8 +176,7 @@ pub trait PyClassImpl: Sized {
#[cfg(feature = "multiple-pymethods")] #[cfg(feature = "multiple-pymethods")]
type Inventory: PyClassInventory; type Inventory: PyClassInventory;
fn for_each_method_def(_visitor: &mut dyn FnMut(&[PyMethodDefType])) {} fn for_all_items(visitor: &mut dyn FnMut(&PyClassItems));
fn for_each_proto_slot(_visitor: &mut dyn FnMut(&[ffi::PyType_Slot])) {}
#[inline] #[inline]
fn get_new() -> Option<ffi::newfunc> { fn get_new() -> Option<ffi::newfunc> {
@ -697,15 +709,18 @@ unsafe fn bpo_35810_workaround(_py: Python, ty: *mut ffi::PyTypeObject) {
// General methods implementation: either dtolnay specialization trait or inventory if // General methods implementation: either dtolnay specialization trait or inventory if
// multiple-pymethods feature is enabled. // multiple-pymethods feature is enabled.
macro_rules! methods_trait { macro_rules! items_trait {
($name:ident, $function_name: ident) => { ($name:ident, $function_name: ident) => {
pub trait $name<T> { pub trait $name<T> {
fn $function_name(self) -> &'static [PyMethodDefType]; fn $function_name(self) -> &'static PyClassItems;
} }
impl<T> $name<T> for &'_ PyClassImplCollector<T> { impl<T> $name<T> for &'_ PyClassImplCollector<T> {
fn $function_name(self) -> &'static [PyMethodDefType] { fn $function_name(self) -> &'static PyClassItems {
&[] &PyClassItems {
methods: &[],
slots: &[],
}
} }
} }
}; };
@ -717,62 +732,40 @@ macro_rules! methods_trait {
/// which are eventually collected by `#[pyclass]`. /// which are eventually collected by `#[pyclass]`.
#[cfg(feature = "multiple-pymethods")] #[cfg(feature = "multiple-pymethods")]
pub trait PyClassInventory: inventory::Collect { pub trait PyClassInventory: inventory::Collect {
/// Returns the methods for a single `#[pymethods] impl` block /// Returns the items for a single `#[pymethods] impl` block
fn methods(&'static self) -> &'static [PyMethodDefType]; fn items(&'static self) -> &'static PyClassItems;
/// Returns the slots for a single `#[pymethods] impl` block
fn slots(&'static self) -> &'static [ffi::PyType_Slot];
} }
// Methods from #[pyo3(get, set)] on struct fields. // Methods from #[pyo3(get, set)] on struct fields.
methods_trait!(PyClassDescriptors, py_class_descriptors); items_trait!(PyClassIntrinsicItems, pyclass_intrinsic_items);
// Methods from #[pymethods] if not using inventory. // Items from #[pymethods] if not using inventory.
#[cfg(not(feature = "multiple-pymethods"))] #[cfg(not(feature = "multiple-pymethods"))]
methods_trait!(PyMethods, py_methods); items_trait!(PyMethods, py_methods);
// All traits describing slots, as well as the fallback implementations for unimplemented protos /// Items from `#[pyproto]` implementations
// #[cfg(feature = "pyproto")]
// Protos which are implemented use dtolnay specialization to implement for PyClassImplCollector<T>. mod pyproto_traits {
// use super::*;
// See https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md items_trait!(PyObjectProtocolItems, object_protocol_items);
items_trait!(PyDescrProtocolItems, descr_protocol_items);
macro_rules! slots_trait { items_trait!(PyGCProtocolItems, gc_protocol_items);
($name:ident, $function_name: ident) => { items_trait!(PyIterProtocolItems, iter_protocol_items);
pub trait $name<T> { items_trait!(PyMappingProtocolItems, mapping_protocol_items);
fn $function_name(self) -> &'static [ffi::PyType_Slot]; items_trait!(PyNumberProtocolItems, number_protocol_items);
} items_trait!(PyAsyncProtocolItems, async_protocol_items);
items_trait!(PySequenceProtocolItems, sequence_protocol_items);
impl<T> $name<T> for &'_ PyClassImplCollector<T> { items_trait!(PyBufferProtocolItems, buffer_protocol_items);
fn $function_name(self) -> &'static [ffi::PyType_Slot] {
&[]
}
}
};
} }
#[cfg(feature = "pyproto")]
pub use pyproto_traits::*;
slots_trait!(PyObjectProtocolSlots, object_protocol_slots); // items that PyO3 implements by default, but can be overidden by the users.
slots_trait!(PyDescrProtocolSlots, descr_protocol_slots); items_trait!(PyClassDefaultItems, pyclass_default_items);
slots_trait!(PyGCProtocolSlots, gc_protocol_slots);
slots_trait!(PyIterProtocolSlots, iter_protocol_slots);
slots_trait!(PyMappingProtocolSlots, mapping_protocol_slots);
slots_trait!(PyNumberProtocolSlots, number_protocol_slots);
slots_trait!(PyAsyncProtocolSlots, async_protocol_slots);
slots_trait!(PySequenceProtocolSlots, sequence_protocol_slots);
slots_trait!(PyBufferProtocolSlots, buffer_protocol_slots);
// slots that PyO3 implements by default, but can be overidden by the users.
slots_trait!(PyClassDefaultSlots, py_class_default_slots);
// Protocol slots from #[pymethods] if not using inventory. // Protocol slots from #[pymethods] if not using inventory.
#[cfg(not(feature = "multiple-pymethods"))] #[cfg(not(feature = "multiple-pymethods"))]
slots_trait!(PyMethodsProtocolSlots, methods_protocol_slots); items_trait!(PyMethodsProtocolItems, methods_protocol_items);
methods_trait!(PyObjectProtocolMethods, object_protocol_methods);
methods_trait!(PyAsyncProtocolMethods, async_protocol_methods);
methods_trait!(PyDescrProtocolMethods, descr_protocol_methods);
methods_trait!(PyMappingProtocolMethods, mapping_protocol_methods);
methods_trait!(PyNumberProtocolMethods, number_protocol_methods);
// Thread checkers // Thread checkers

View File

@ -2,7 +2,10 @@
use crate::{ use crate::{
callback::IntoPyCallbackOutput, callback::IntoPyCallbackOutput,
ffi, ffi,
impl_::pyclass::{assign_sequence_item_from_mapping, get_sequence_item_from_mapping, fallback_new, tp_dealloc, PyClassDict, PyClassImpl, PyClassWeakRef}, impl_::pyclass::{
assign_sequence_item_from_mapping, fallback_new, get_sequence_item_from_mapping,
tp_dealloc, PyClassDict, PyClassImpl, PyClassItems, PyClassWeakRef,
},
IntoPy, IntoPyPointer, PyCell, PyErr, PyMethodDefType, PyNativeType, PyObject, PyResult, IntoPy, IntoPyPointer, PyCell, PyErr, PyMethodDefType, PyNativeType, PyObject, PyResult,
PyTypeInfo, Python, PyTypeInfo, Python,
}; };
@ -52,8 +55,7 @@ where
T::get_free(), T::get_free(),
T::dict_offset(), T::dict_offset(),
T::weaklist_offset(), T::weaklist_offset(),
&T::for_each_method_def, &T::for_all_items,
&T::for_each_proto_slot,
T::IS_GC, T::IS_GC,
T::IS_BASETYPE, T::IS_BASETYPE,
) )
@ -77,8 +79,7 @@ unsafe fn create_type_object_impl(
tp_free: Option<ffi::freefunc>, tp_free: Option<ffi::freefunc>,
dict_offset: Option<ffi::Py_ssize_t>, dict_offset: Option<ffi::Py_ssize_t>,
weaklist_offset: Option<ffi::Py_ssize_t>, weaklist_offset: Option<ffi::Py_ssize_t>,
for_each_method_def: &dyn Fn(&mut dyn FnMut(&[PyMethodDefType])), for_all_items: &dyn Fn(&mut dyn FnMut(&PyClassItems)),
for_each_proto_slot: &dyn Fn(&mut dyn FnMut(&[ffi::PyType_Slot])),
is_gc: bool, is_gc: bool,
is_basetype: bool, is_basetype: bool,
) -> PyResult<*mut ffi::PyTypeObject> { ) -> PyResult<*mut ffi::PyTypeObject> {
@ -118,7 +119,7 @@ unsafe fn create_type_object_impl(
let PyClassInfo { let PyClassInfo {
method_defs, method_defs,
property_defs, property_defs,
} = method_defs_to_pyclass_info(for_each_method_def, dict_offset.is_none()); } = method_defs_to_pyclass_info(for_all_items, dict_offset.is_none());
// normal methods // normal methods
if !method_defs.is_empty() { if !method_defs.is_empty() {
@ -139,8 +140,8 @@ unsafe fn create_type_object_impl(
#[cfg(all(not(Py_3_9), not(Py_LIMITED_API)))] #[cfg(all(not(Py_3_9), not(Py_LIMITED_API)))]
let mut buffer_procs: ffi::PyBufferProcs = Default::default(); let mut buffer_procs: ffi::PyBufferProcs = Default::default();
for_each_proto_slot(&mut |proto_slots| { for_all_items(&mut |items| {
for slot in proto_slots { for slot in items.slots {
has_getitem |= slot.slot == ffi::Py_mp_subscript; has_getitem |= slot.slot == ffi::Py_mp_subscript;
has_setitem |= slot.slot == ffi::Py_mp_ass_subscript; has_setitem |= slot.slot == ffi::Py_mp_ass_subscript;
has_gc_methods |= slot.slot == ffi::Py_tp_clear || slot.slot == ffi::Py_tp_traverse; has_gc_methods |= slot.slot == ffi::Py_tp_clear || slot.slot == ffi::Py_tp_traverse;
@ -156,7 +157,7 @@ unsafe fn create_type_object_impl(
buffer_procs.bf_releasebuffer = Some(std::mem::transmute(slot.pfunc)); buffer_procs.bf_releasebuffer = Some(std::mem::transmute(slot.pfunc));
} }
} }
slots.extend_from_slice(proto_slots); slots.extend_from_slice(items.slots);
}); });
// If mapping methods implemented, define sequence methods get implemented too. // If mapping methods implemented, define sequence methods get implemented too.
@ -320,14 +321,14 @@ struct PyClassInfo {
} }
fn method_defs_to_pyclass_info( fn method_defs_to_pyclass_info(
for_each_method_def: &dyn Fn(&mut dyn FnMut(&[PyMethodDefType])), for_all_items: &dyn Fn(&mut dyn FnMut(&PyClassItems)),
has_dict: bool, has_dict: bool,
) -> PyClassInfo { ) -> PyClassInfo {
let mut method_defs = Vec::new(); let mut method_defs = Vec::new();
let mut property_defs_map = std::collections::HashMap::new(); let mut property_defs_map = std::collections::HashMap::new();
for_each_method_def(&mut |class_method_defs| { for_all_items(&mut |items| {
for def in class_method_defs { for def in items.methods {
match def { match def {
PyMethodDefType::Getter(getter) => { PyMethodDefType::Getter(getter) => {
getter.copy_to( getter.copy_to(

View File

@ -1,6 +1,7 @@
// Copyright (c) 2017-present PyO3 Project and Contributors // Copyright (c) 2017-present PyO3 Project and Contributors
//! Python type object information //! Python type object information
use crate::impl_::pyclass::PyClassItems;
use crate::internal_tricks::extract_cstr_or_leak_cstring; use crate::internal_tricks::extract_cstr_or_leak_cstring;
use crate::once_cell::GILOnceCell; use crate::once_cell::GILOnceCell;
use crate::pyclass::create_type_object; use crate::pyclass::create_type_object;
@ -108,7 +109,7 @@ impl LazyStaticType {
pub fn get_or_init<T: PyClass>(&self, py: Python) -> *mut ffi::PyTypeObject { pub fn get_or_init<T: PyClass>(&self, py: Python) -> *mut ffi::PyTypeObject {
let type_object = *self.value.get_or_init(py, || create_type_object::<T>(py)); let type_object = *self.value.get_or_init(py, || create_type_object::<T>(py));
self.ensure_init(py, type_object, T::NAME, &T::for_each_method_def); self.ensure_init(py, type_object, T::NAME, &T::for_all_items);
type_object type_object
} }
@ -117,7 +118,7 @@ impl LazyStaticType {
py: Python, py: Python,
type_object: *mut ffi::PyTypeObject, type_object: *mut ffi::PyTypeObject,
name: &str, name: &str,
for_each_method_def: &dyn Fn(&mut dyn FnMut(&[PyMethodDefType])), for_all_items: &dyn Fn(&mut dyn FnMut(&PyClassItems)),
) { ) {
// We might want to fill the `tp_dict` with python instances of `T` // We might want to fill the `tp_dict` with python instances of `T`
// itself. In order to do so, we must first initialize the type object // itself. In order to do so, we must first initialize the type object
@ -151,8 +152,8 @@ impl LazyStaticType {
// means that another thread can continue the initialization in the // means that another thread can continue the initialization in the
// meantime: at worst, we'll just make a useless computation. // meantime: at worst, we'll just make a useless computation.
let mut items = vec![]; let mut items = vec![];
for_each_method_def(&mut |method_defs| { for_all_items(&mut |class_items| {
items.extend(method_defs.iter().filter_map(|def| { items.extend(class_items.methods.iter().filter_map(|def| {
if let PyMethodDefType::ClassAttribute(attr) = def { if let PyMethodDefType::ClassAttribute(attr) = def {
let key = extract_cstr_or_leak_cstring( let key = extract_cstr_or_leak_cstring(
attr.name, attr.name,

View File

@ -56,6 +56,7 @@ struct Iter {
#[pymethods] #[pymethods]
impl Iter { impl Iter {
#[allow(clippy::self_named_constructors)]
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> { fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
slf slf
} }