diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a021b55..30115fd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update `paste` optional dependency to 1.0. [#2004](https://github.com/PyO3/pyo3/pull/2004) - Drop support for Python 3.6, remove `abi3-py36` feature. [#2006](https://github.com/PyO3/pyo3/pull/2006) - `pyo3-build-config` no longer enables the `resolve-config` feature by default. [#2008](https://github.com/PyO3/pyo3/pull/2008) +- Update `inventory` optional dependency to 0.2. [#2019](https://github.com/PyO3/pyo3/pull/2019) ### Added diff --git a/Cargo.toml b/Cargo.toml index 4b21a6c2..bc8d54da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,8 +26,7 @@ paste = { version = "1.0.6", optional = true } unindent = { version = "0.1.4", optional = true } # support crate for multiple-pymethods feature -# must stay at 0.1.x for Rust 1.41 compatibility -inventory = { version = "0.1.4", optional = true } +inventory = { version = "0.2.0", optional = true } # crate integrations that can be added using the eponymous features anyhow = { version = "1.0", optional = true } diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 52f7c319..9bafc6b6 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -594,7 +594,6 @@ impl<'a> PyClassImplsBuilder<'a> { self.impl_pyclass(), self.impl_extractext(), self.impl_into_py(), - self.impl_methods_inventory(), self.impl_pyclassimpl(), self.impl_freelist(), self.impl_gc(), @@ -667,48 +666,6 @@ impl<'a> PyClassImplsBuilder<'a> { quote! {} } } - - /// To allow multiple #[pymethods] block, we define inventory types. - fn impl_methods_inventory(&self) -> TokenStream { - let cls = self.cls; - let methods_type = self.methods_type; - match methods_type { - PyClassMethodsType::Specialization => quote! {}, - PyClassMethodsType::Inventory => { - // Try to build a unique type for better error messages - let name = format!("Pyo3MethodsInventoryFor{}", cls.unraw()); - let inventory_cls = syn::Ident::new(&name, Span::call_site()); - - quote! { - #[doc(hidden)] - pub struct #inventory_cls { - methods: ::std::vec::Vec<::pyo3::class::PyMethodDefType>, - slots: ::std::vec::Vec<::pyo3::ffi::PyType_Slot>, - } - impl ::pyo3::class::impl_::PyMethodsInventory for #inventory_cls { - fn new( - methods: ::std::vec::Vec<::pyo3::class::PyMethodDefType>, - slots: ::std::vec::Vec<::pyo3::ffi::PyType_Slot>, - ) -> Self { - Self { methods, slots } - } - fn methods(&'static self) -> &'static [::pyo3::class::PyMethodDefType] { - &self.methods - } - fn slots(&'static self) -> &'static [::pyo3::ffi::PyType_Slot] { - &self.slots - } - } - - impl ::pyo3::class::impl_::HasMethodsInventory for #cls { - type Methods = #inventory_cls; - } - - ::pyo3::inventory::collect!(#inventory_cls); - } - } - } - } fn impl_pyclassimpl(&self) -> TokenStream { let cls = self.cls; let doc = self.doc.as_ref().map_or(quote! {"\0"}, |doc| quote! {#doc}); @@ -727,26 +684,37 @@ impl<'a> PyClassImplsBuilder<'a> { quote! { ::pyo3::class::impl_::ThreadCheckerStub<#cls> } }; - let methods_protos = match self.methods_type { - PyClassMethodsType::Specialization => { - quote! { visitor(collector.methods_protocol_slots()); } - } + 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, + ), PyClassMethodsType::Inventory => { - quote! { - for inventory in ::pyo3::inventory::iter::<::Methods>() { - visitor(::pyo3::class::impl_::PyMethodsInventory::slots(inventory)); - } - } + // To allow multiple #[pymethods] block, we define inventory types. + let inventory_class_name = syn::Ident::new( + &format!("Pyo3MethodsInventoryFor{}", cls.unraw()), + Span::call_site(), + ); + ( + quote! { + for inventory in ::pyo3::inventory::iter::<::Inventory>() { + visitor(::pyo3::class::impl_::PyClassInventory::methods(inventory)); + } + }, + quote! { + for inventory in ::pyo3::inventory::iter::<::Inventory>() { + visitor(::pyo3::class::impl_::PyClassInventory::slots(inventory)); + } + }, + Some(quote! { type Inventory = #inventory_class_name; }), + Some(define_inventory_class(&inventory_class_name)), + ) } }; - let for_each_py_method = match self.methods_type { - PyClassMethodsType::Specialization => quote! { visitor(collector.py_methods()); }, - PyClassMethodsType::Inventory => quote! { - for inventory in ::pyo3::inventory::iter::<::Methods>() { - visitor(::pyo3::class::impl_::PyMethodsInventory::methods(inventory)); - } - }, - }; quote! { impl ::pyo3::class::impl_::PyClassImpl for #cls { const DOC: &'static str = #doc; @@ -757,6 +725,7 @@ impl<'a> PyClassImplsBuilder<'a> { type Layout = ::pyo3::PyCell; type BaseType = #base; type ThreadChecker = #thread_checker; + #inventory fn for_each_method_def(visitor: &mut dyn ::std::ops::FnMut(&[::pyo3::class::PyMethodDefType])) { use ::pyo3::class::impl_::*; @@ -807,6 +776,8 @@ impl<'a> PyClassImplsBuilder<'a> { collector.buffer_procs() } } + + #inventory_class } } @@ -865,3 +836,36 @@ impl<'a> PyClassImplsBuilder<'a> { } } } + +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], + } + impl #inventory_class_name { + const fn new( + methods: &'static [::pyo3::class::PyMethodDefType], + slots: &'static [::pyo3::ffi::PyType_Slot], + ) -> Self { + Self { methods, slots } + } + } + + impl ::pyo3::class::impl_::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 + } + } + + // 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); + } +} diff --git a/pyo3-macros-backend/src/pyimpl.rs b/pyo3-macros-backend/src/pyimpl.rs index e2fbf8d8..33aa9749 100644 --- a/pyo3-macros-backend/src/pyimpl.rs +++ b/pyo3-macros-backend/src/pyimpl.rs @@ -211,10 +211,8 @@ fn submit_methods_inventory( ) -> TokenStream { quote! { ::pyo3::inventory::submit! { - #![crate = ::pyo3] { - type Inventory = <#ty as ::pyo3::class::impl_::HasMethodsInventory>::Methods; - ::new(::std::vec![#(#methods),*], ::std::vec![#(#proto_impls),*]) - } + type Inventory = <#ty as ::pyo3::class::impl_::PyClassImpl>::Inventory; + Inventory::new(&[#(#methods),*], &[#(#proto_impls),*]) } } } diff --git a/src/class/impl_.rs b/src/class/impl_.rs index 63435a38..31fe8ea7 100644 --- a/src/class/impl_.rs +++ b/src/class/impl_.rs @@ -67,6 +67,9 @@ pub trait PyClassImpl: Sized { /// can be accessed by multiple threads by `threading` module. type ThreadChecker: PyClassThreadChecker; + #[cfg(feature = "multiple-pymethods")] + type Inventory: PyClassInventory; + fn for_each_method_def(_visitor: &mut dyn FnMut(&[PyMethodDefType])) {} fn get_new() -> Option { None @@ -607,11 +610,8 @@ macro_rules! methods_trait { /// Method storage for `#[pyclass]`. /// Allows arbitrary `#[pymethod]` blocks to submit their methods, /// which are eventually collected by `#[pyclass]`. -#[cfg(all(feature = "macros", feature = "multiple-pymethods"))] -pub trait PyMethodsInventory: inventory::Collect { - /// Create a new instance - fn new(methods: Vec, slots: Vec) -> Self; - +#[cfg(feature = "multiple-pymethods")] +pub trait PyClassInventory: inventory::Collect { /// Returns the methods for a single `#[pymethods] impl` block fn methods(&'static self) -> &'static [PyMethodDefType]; @@ -619,13 +619,6 @@ pub trait PyMethodsInventory: inventory::Collect { fn slots(&'static self) -> &'static [ffi::PyType_Slot]; } -/// Implemented for `#[pyclass]` in our proc macro code. -/// Indicates that the pyclass has its own method storage. -#[cfg(all(feature = "macros", feature = "multiple-pymethods"))] -pub trait HasMethodsInventory { - type Methods: PyMethodsInventory; -} - // Methods from #[pyo3(get, set)] on struct fields. methods_trait!(PyClassDescriptors, py_class_descriptors);