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 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::*;
let collector = PyClassImplCollector::<MyClass>::new();
visitor(collector.py_methods());
visitor(collector.py_class_descriptors());
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());
visitor(collector.pyclass_intrinsic_items());
}
fn get_new() -> Option<pyo3::ffi::newfunc> {
use pyo3::impl_::pyclass::*;
@ -884,21 +879,6 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
let collector = PyClassImplCollector::<Self>::new();
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| {
# let cls = py.get_type::<MyClass>();

View File

@ -64,27 +64,13 @@ impl Proto {
})
}
pub(crate) fn slots_trait(&self) -> syn::Ident {
syn::Ident::new(&format!("Py{}ProtocolSlots", self.name), Span::call_site())
pub(crate) fn items_trait(&self) -> syn::Ident {
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(
&format!("{}_protocol_slots", 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()),
&format!("{}_protocol_items", self.name.to_ascii_lowercase()),
Span::call_site(),
)
}

View File

@ -5,7 +5,7 @@ use crate::attributes::{
};
use crate::deprecations::Deprecations;
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::utils::{self, get_pyo3_crate, unwrap_group, PythonDoc};
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! {
const _: () = {
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)));
quote! {
impl _pyo3::impl_::pyclass::PyClassDescriptors<#cls>
impl _pyo3::impl_::pyclass::PyClassIntrinsicItems<#cls>
for _pyo3::impl_::pyclass::PyClassImplCollector<#cls>
{
fn py_class_descriptors(self) -> &'static [_pyo3::impl_::pymethods::PyMethodDefType] {
static METHODS: &[_pyo3::impl_::pymethods::PyMethodDefType] = &[#(#py_methods),*];
METHODS
fn pyclass_intrinsic_items(self) -> &'static _pyo3::impl_::pyclass::PyClassItems {
static ITEMS: _pyo3::impl_::pyclass::PyClassItems = _pyo3::impl_::pyclass::PyClassItems {
methods: &[#(#py_methods),*],
slots: &[]
};
&ITEMS
}
}
}
@ -574,12 +577,15 @@ fn impl_descriptors(
.collect::<syn::Result<_>>()?;
Ok(quote! {
impl _pyo3::impl_::pyclass::PyClassDescriptors<#cls>
impl _pyo3::impl_::pyclass::PyClassIntrinsicItems<#cls>
for _pyo3::impl_::pyclass::PyClassImplCollector<#cls>
{
fn py_class_descriptors(self) -> &'static [_pyo3::impl_::pymethods::PyMethodDefType] {
static METHODS: &[_pyo3::impl_::pymethods::PyMethodDefType] = &[#(#py_methods),*];
METHODS
fn pyclass_intrinsic_items(self) -> &'static _pyo3::impl_::pyclass::PyClassItems {
static ITEMS: _pyo3::impl_::pyclass::PyClassItems = _pyo3::impl_::pyclass::PyClassItems {
methods: &[#(#py_methods),*],
slots: &[]
};
&ITEMS
}
}
})
@ -758,15 +764,10 @@ impl<'a> PyClassImplsBuilder<'a> {
quote! { _pyo3::impl_::pyclass::ThreadCheckerStub<#cls> }
};
let (for_each_py_method, methods_protos, inventory, inventory_class) = match self
.methods_type
{
PyClassMethodsType::Specialization => (
quote! { visitor(collector.py_methods()); },
quote! { visitor(collector.methods_protocol_slots()); },
None,
None,
),
let (pymethods_items, inventory, inventory_class) = match self.methods_type {
PyClassMethodsType::Specialization => {
(quote! { visitor(collector.py_methods()); }, None, None)
}
PyClassMethodsType::Inventory => {
// To allow multiple #[pymethods] block, we define inventory types.
let inventory_class_name = syn::Ident::new(
@ -776,12 +777,7 @@ impl<'a> PyClassImplsBuilder<'a> {
(
quote! {
for inventory in _pyo3::inventory::iter::<<Self as _pyo3::impl_::pyclass::PyClassImpl>::Inventory>() {
visitor(_pyo3::impl_::pyclass::PyClassInventory::methods(inventory));
}
},
quote! {
for inventory in _pyo3::inventory::iter::<<Self as _pyo3::impl_::pyclass::PyClassImpl>::Inventory>() {
visitor(_pyo3::impl_::pyclass::PyClassInventory::slots(inventory));
visitor(_pyo3::impl_::pyclass::PyClassInventory::items(inventory));
}
},
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! {
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());
})
} else {
None
};
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());
visitor(collector.object_protocol_items());
visitor(collector.number_protocol_items());
visitor(collector.iter_protocol_items());
visitor(collector.gc_protocol_items());
visitor(collector.descr_protocol_items());
visitor(collector.mapping_protocol_items());
visitor(collector.sequence_protocol_items());
visitor(collector.async_protocol_items());
visitor(collector.buffer_protocol_items());
})
} else {
None
@ -830,12 +814,15 @@ impl<'a> PyClassImplsBuilder<'a> {
type ThreadChecker = #thread_checker;
#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::*;
let collector = PyClassImplCollector::<Self>::new();
#for_each_py_method;
visitor(collector.py_class_descriptors());
#pyproto_method_visitors
visitor(collector.pyclass_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
}
fn get_new() -> ::std::option::Option<_pyo3::ffi::newfunc> {
use _pyo3::impl_::pyclass::*;
@ -853,17 +840,6 @@ impl<'a> PyClassImplsBuilder<'a> {
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
#weaklist_offset
@ -933,31 +909,20 @@ fn define_inventory_class(inventory_class_name: &syn::Ident) -> TokenStream {
quote! {
#[doc(hidden)]
pub struct #inventory_class_name {
methods: &'static [_pyo3::class::PyMethodDefType],
slots: &'static [_pyo3::ffi::PyType_Slot],
items: _pyo3::impl_::pyclass::PyClassItems,
}
impl #inventory_class_name {
const fn new(
methods: &'static [_pyo3::class::PyMethodDefType],
slots: &'static [_pyo3::ffi::PyType_Slot],
) -> Self {
Self { methods, slots }
const fn new(items: _pyo3::impl_::pyclass::PyClassItems) -> Self {
Self { items }
}
}
impl _pyo3::impl_::pyclass::PyClassInventory for #inventory_class_name {
fn methods(&'static self) -> &'static [_pyo3::class::PyMethodDefType] {
self.methods
}
fn slots(&'static self) -> &'static [_pyo3::ffi::PyType_Slot] {
self.slots
fn items(&self) -> &_pyo3::impl_::pyclass::PyClassItems {
&self.items
}
}
// 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);
}
}

View File

@ -150,35 +150,19 @@ pub fn impl_methods(
let krate = get_pyo3_crate(&options.krate);
Ok(match methods_type {
PyClassMethodsType::Specialization => {
let methods_registration = impl_py_methods(ty, methods);
let protos_registration = impl_protos(ty, proto_impls);
let items = match methods_type {
PyClassMethodsType::Specialization => impl_py_methods(ty, methods, proto_impls),
PyClassMethodsType::Inventory => submit_methods_inventory(ty, methods, proto_impls),
};
quote! {
const _: () = {
use #krate as _pyo3;
Ok(quote! {
const _: () = {
use #krate as _pyo3;
#(#trait_impls)*
#(#trait_impls)*
#protos_registration
#methods_registration
};
}
}
PyClassMethodsType::Inventory => {
let inventory = submit_methods_inventory(ty, methods, proto_impls);
quote! {
const _: () = {
use #krate as _pyo3;
#(#trait_impls)*
#inventory
};
}
}
#items
};
})
}
@ -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
// all succeed.
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 {
#(#method_defs)*
}
impl ::pyo3::impl_::pyclass::PyClassDefaultSlots<#cls>
impl ::pyo3::impl_::pyclass::PyClassDefaultItems<#cls>
for ::pyo3::impl_::pyclass::PyClassImplCollector<#cls> {
fn py_class_default_slots(self) -> &'static [::pyo3::ffi::PyType_Slot] {
&[#(#proto_impls),*]
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(ty: &syn::Type, methods: Vec<TokenStream>) -> TokenStream {
fn impl_py_methods(
ty: &syn::Type,
methods: Vec<TokenStream>,
proto_impls: Vec<TokenStream>,
) -> TokenStream {
quote! {
impl _pyo3::impl_::pyclass::PyMethods<#ty>
for _pyo3::impl_::pyclass::PyClassImplCollector<#ty>
{
fn py_methods(self) -> &'static [_pyo3::impl_::pymethods::PyMethodDefType] {
static METHODS: &[_pyo3::impl_::pymethods::PyMethodDefType] = &[#(#methods),*];
METHODS
fn py_methods(self) -> &'static _pyo3::impl_::pyclass::PyClassItems {
static ITEMS: _pyo3::impl_::pyclass::PyClassItems = _pyo3::impl_::pyclass::PyClassItems {
methods: &[#(#methods),*],
slots: &[#(#proto_impls),*]
};
&ITEMS
}
}
}
@ -298,18 +293,6 @@ fn add_shared_proto_slots(
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(
ty: &syn::Type,
methods: Vec<TokenStream>,
@ -318,7 +301,7 @@ fn submit_methods_inventory(
quote! {
_pyo3::inventory::submit! {
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 protocol_methods = impl_proto_methods(method_names, ty, proto);
let items = impl_proto_items(method_names, py_methods, ty, proto);
Ok(quote! {
const _: () = {
use ::pyo3 as _pyo3; // pyproto doesn't support specifying #[pyo3(crate)]
#trait_impls
#normal_methods
#protocol_methods
#items
};
})
}
fn impl_normal_methods(
fn impl_proto_items(
method_names: HashSet<String>,
py_methods: Vec<TokenStream>,
ty: &syn::Type,
proto: &defs::Proto,
) -> TokenStream {
if 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() {
if method_names.is_empty() && py_methods.is_empty() {
return TokenStream::default();
}
let module = proto.module();
let slots_trait = proto.slots_trait();
let slots_trait_slots = proto.slots_trait_slots();
let items_trait = proto.items_trait();
let items_trait_items = proto.items_trait_items();
let mut tokens = proto
.slot_defs(method_names)
@ -153,11 +128,15 @@ fn impl_proto_methods(
}
quote! {
impl _pyo3::impl_::pyclass::#slots_trait<#ty>
impl _pyo3::impl_::pyclass::#items_trait<#ty>
for _pyo3::impl_::pyclass::PyClassImplCollector<#ty>
{
fn #slots_trait_slots(self) -> &'static [_pyo3::ffi::PyType_Slot] {
&[#(#tokens),*]
fn #items_trait_items(self) -> &'static _pyo3::impl_::pyclass::PyClassItems {
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},
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.
#[inline]
@ -128,6 +133,14 @@ impl<T> Clone 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.
///
/// 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")]
type Inventory: PyClassInventory;
fn for_each_method_def(_visitor: &mut dyn FnMut(&[PyMethodDefType])) {}
fn for_each_proto_slot(_visitor: &mut dyn FnMut(&[ffi::PyType_Slot])) {}
fn for_all_items(visitor: &mut dyn FnMut(&PyClassItems));
#[inline]
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
// multiple-pymethods feature is enabled.
macro_rules! methods_trait {
macro_rules! items_trait {
($name:ident, $function_name: ident) => {
pub trait $name<T> {
fn $function_name(self) -> &'static [PyMethodDefType];
fn $function_name(self) -> &'static PyClassItems;
}
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]`.
#[cfg(feature = "multiple-pymethods")]
pub trait PyClassInventory: inventory::Collect {
/// Returns the methods for a single `#[pymethods] impl` block
fn methods(&'static self) -> &'static [PyMethodDefType];
/// Returns the slots for a single `#[pymethods] impl` block
fn slots(&'static self) -> &'static [ffi::PyType_Slot];
/// Returns the items for a single `#[pymethods] impl` block
fn items(&'static self) -> &'static PyClassItems;
}
// 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"))]
methods_trait!(PyMethods, py_methods);
items_trait!(PyMethods, py_methods);
// All traits describing slots, as well as the fallback implementations for unimplemented protos
//
// Protos which are implemented use dtolnay specialization to implement for PyClassImplCollector<T>.
//
// See https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md
macro_rules! slots_trait {
($name:ident, $function_name: ident) => {
pub trait $name<T> {
fn $function_name(self) -> &'static [ffi::PyType_Slot];
}
impl<T> $name<T> for &'_ PyClassImplCollector<T> {
fn $function_name(self) -> &'static [ffi::PyType_Slot] {
&[]
}
}
};
/// Items from `#[pyproto]` implementations
#[cfg(feature = "pyproto")]
mod pyproto_traits {
use super::*;
items_trait!(PyObjectProtocolItems, object_protocol_items);
items_trait!(PyDescrProtocolItems, descr_protocol_items);
items_trait!(PyGCProtocolItems, gc_protocol_items);
items_trait!(PyIterProtocolItems, iter_protocol_items);
items_trait!(PyMappingProtocolItems, mapping_protocol_items);
items_trait!(PyNumberProtocolItems, number_protocol_items);
items_trait!(PyAsyncProtocolItems, async_protocol_items);
items_trait!(PySequenceProtocolItems, sequence_protocol_items);
items_trait!(PyBufferProtocolItems, buffer_protocol_items);
}
#[cfg(feature = "pyproto")]
pub use pyproto_traits::*;
slots_trait!(PyObjectProtocolSlots, object_protocol_slots);
slots_trait!(PyDescrProtocolSlots, descr_protocol_slots);
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);
// 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"))]
slots_trait!(PyMethodsProtocolSlots, methods_protocol_slots);
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);
items_trait!(PyMethodsProtocolItems, methods_protocol_items);
// Thread checkers

View File

@ -2,7 +2,10 @@
use crate::{
callback::IntoPyCallbackOutput,
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,
PyTypeInfo, Python,
};
@ -52,8 +55,7 @@ where
T::get_free(),
T::dict_offset(),
T::weaklist_offset(),
&T::for_each_method_def,
&T::for_each_proto_slot,
&T::for_all_items,
T::IS_GC,
T::IS_BASETYPE,
)
@ -77,8 +79,7 @@ unsafe fn create_type_object_impl(
tp_free: Option<ffi::freefunc>,
dict_offset: Option<ffi::Py_ssize_t>,
weaklist_offset: Option<ffi::Py_ssize_t>,
for_each_method_def: &dyn Fn(&mut dyn FnMut(&[PyMethodDefType])),
for_each_proto_slot: &dyn Fn(&mut dyn FnMut(&[ffi::PyType_Slot])),
for_all_items: &dyn Fn(&mut dyn FnMut(&PyClassItems)),
is_gc: bool,
is_basetype: bool,
) -> PyResult<*mut ffi::PyTypeObject> {
@ -118,7 +119,7 @@ unsafe fn create_type_object_impl(
let PyClassInfo {
method_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
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)))]
let mut buffer_procs: ffi::PyBufferProcs = Default::default();
for_each_proto_slot(&mut |proto_slots| {
for slot in proto_slots {
for_all_items(&mut |items| {
for slot in items.slots {
has_getitem |= slot.slot == ffi::Py_mp_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;
@ -156,7 +157,7 @@ unsafe fn create_type_object_impl(
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.
@ -320,14 +321,14 @@ struct PyClassInfo {
}
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,
) -> PyClassInfo {
let mut method_defs = Vec::new();
let mut property_defs_map = std::collections::HashMap::new();
for_each_method_def(&mut |class_method_defs| {
for def in class_method_defs {
for_all_items(&mut |items| {
for def in items.methods {
match def {
PyMethodDefType::Getter(getter) => {
getter.copy_to(

View File

@ -1,6 +1,7 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
//! Python type object information
use crate::impl_::pyclass::PyClassItems;
use crate::internal_tricks::extract_cstr_or_leak_cstring;
use crate::once_cell::GILOnceCell;
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 {
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
}
@ -117,7 +118,7 @@ impl LazyStaticType {
py: Python,
type_object: *mut ffi::PyTypeObject,
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`
// 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
// meantime: at worst, we'll just make a useless computation.
let mut items = vec![];
for_each_method_def(&mut |method_defs| {
items.extend(method_defs.iter().filter_map(|def| {
for_all_items(&mut |class_items| {
items.extend(class_items.methods.iter().filter_map(|def| {
if let PyMethodDefType::ClassAttribute(attr) = def {
let key = extract_cstr_or_leak_cstring(
attr.name,

View File

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