pymethods: support protocols with `multiple-pymethods` feature
This commit is contained in:
parent
4b2345fe80
commit
0e0e6f8bf5
|
@ -346,14 +346,21 @@ fn impl_methods_inventory(cls: &syn::Ident) -> TokenStream {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct #inventory_cls {
|
pub struct #inventory_cls {
|
||||||
methods: ::std::vec::Vec<::pyo3::class::PyMethodDefType>,
|
methods: ::std::vec::Vec<::pyo3::class::PyMethodDefType>,
|
||||||
|
slots: ::std::vec::Vec<::pyo3::ffi::PyType_Slot>,
|
||||||
}
|
}
|
||||||
impl ::pyo3::class::impl_::PyMethodsInventory for #inventory_cls {
|
impl ::pyo3::class::impl_::PyMethodsInventory for #inventory_cls {
|
||||||
fn new(methods: ::std::vec::Vec<::pyo3::class::PyMethodDefType>) -> Self {
|
fn new(
|
||||||
Self { methods }
|
methods: ::std::vec::Vec<::pyo3::class::PyMethodDefType>,
|
||||||
|
slots: ::std::vec::Vec<::pyo3::ffi::PyType_Slot>,
|
||||||
|
) -> Self {
|
||||||
|
Self { methods, slots }
|
||||||
}
|
}
|
||||||
fn get(&'static self) -> &'static [::pyo3::class::PyMethodDefType] {
|
fn methods(&'static self) -> &'static [::pyo3::class::PyMethodDefType] {
|
||||||
&self.methods
|
&self.methods
|
||||||
}
|
}
|
||||||
|
fn slots(&'static self) -> &'static [::pyo3::ffi::PyType_Slot] {
|
||||||
|
&self.slots
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::pyo3::class::impl_::HasMethodsInventory for #cls {
|
impl ::pyo3::class::impl_::HasMethodsInventory for #cls {
|
||||||
|
@ -450,15 +457,12 @@ fn impl_class(
|
||||||
};
|
};
|
||||||
|
|
||||||
let (impl_inventory, for_each_py_method) = match methods_type {
|
let (impl_inventory, for_each_py_method) = match methods_type {
|
||||||
PyClassMethodsType::Specialization => (
|
PyClassMethodsType::Specialization => (None, quote! { visitor(collector.py_methods()); }),
|
||||||
::std::option::Option::None,
|
|
||||||
quote! { visitor(collector.py_methods()); },
|
|
||||||
),
|
|
||||||
PyClassMethodsType::Inventory => (
|
PyClassMethodsType::Inventory => (
|
||||||
Some(impl_methods_inventory(cls)),
|
Some(impl_methods_inventory(cls)),
|
||||||
quote! {
|
quote! {
|
||||||
for inventory in ::pyo3::inventory::iter::<<Self as ::pyo3::class::impl_::HasMethodsInventory>::Methods>() {
|
for inventory in ::pyo3::inventory::iter::<<Self as ::pyo3::class::impl_::HasMethodsInventory>::Methods>() {
|
||||||
visitor(::pyo3::class::impl_::PyMethodsInventory::get(inventory));
|
visitor(::pyo3::class::impl_::PyMethodsInventory::methods(inventory));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -466,9 +470,15 @@ fn impl_class(
|
||||||
|
|
||||||
let methods_protos = match methods_type {
|
let methods_protos = match methods_type {
|
||||||
PyClassMethodsType::Specialization => {
|
PyClassMethodsType::Specialization => {
|
||||||
Some(quote! { visitor(collector.methods_protocol_slots()); })
|
quote! { visitor(collector.methods_protocol_slots()); }
|
||||||
|
}
|
||||||
|
PyClassMethodsType::Inventory => {
|
||||||
|
quote! {
|
||||||
|
for inventory in ::pyo3::inventory::iter::<<Self as ::pyo3::class::impl_::HasMethodsInventory>::Methods>() {
|
||||||
|
visitor(::pyo3::class::impl_::PyMethodsInventory::slots(inventory));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
PyClassMethodsType::Inventory => None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let base = &attr.base;
|
let base = &attr.base;
|
||||||
|
|
|
@ -85,32 +85,29 @@ pub fn impl_methods(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let methods_registration = match methods_type {
|
add_shared_proto_slots(ty, &mut proto_impls, implemented_proto_fragments);
|
||||||
PyClassMethodsType::Specialization => impl_py_methods(ty, methods),
|
|
||||||
PyClassMethodsType::Inventory => submit_methods_inventory(ty, methods),
|
|
||||||
};
|
|
||||||
|
|
||||||
let protos_registration = match methods_type {
|
Ok(match methods_type {
|
||||||
PyClassMethodsType::Specialization => {
|
PyClassMethodsType::Specialization => {
|
||||||
Some(impl_protos(ty, proto_impls, implemented_proto_fragments))
|
let methods_registration = impl_py_methods(ty, methods);
|
||||||
}
|
let protos_registration = impl_protos(ty, proto_impls);
|
||||||
PyClassMethodsType::Inventory => {
|
|
||||||
if proto_impls.is_empty() {
|
quote! {
|
||||||
None
|
#(#trait_impls)*
|
||||||
} else {
|
|
||||||
panic!(
|
#protos_registration
|
||||||
"cannot implement protos in #[pymethods] using `multiple-pymethods` feature"
|
|
||||||
);
|
#methods_registration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
PyClassMethodsType::Inventory => {
|
||||||
|
let inventory = submit_methods_inventory(ty, methods, proto_impls);
|
||||||
|
quote! {
|
||||||
|
#(#trait_impls)*
|
||||||
|
|
||||||
Ok(quote! {
|
#inventory
|
||||||
#(#trait_impls)*
|
}
|
||||||
|
}
|
||||||
#protos_registration
|
|
||||||
|
|
||||||
#methods_registration
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,11 +144,11 @@ fn impl_py_methods(ty: &syn::Type, methods: Vec<TokenStream>) -> TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_protos(
|
fn add_shared_proto_slots(
|
||||||
ty: &syn::Type,
|
ty: &syn::Type,
|
||||||
mut proto_impls: Vec<TokenStream>,
|
proto_impls: &mut Vec<TokenStream>,
|
||||||
mut implemented_proto_fragments: HashSet<String>,
|
mut implemented_proto_fragments: HashSet<String>,
|
||||||
) -> TokenStream {
|
) {
|
||||||
macro_rules! try_add_shared_slot {
|
macro_rules! try_add_shared_slot {
|
||||||
($first:literal, $second:literal, $slot:ident) => {{
|
($first:literal, $second:literal, $slot:ident) => {{
|
||||||
let first_implemented = implemented_proto_fragments.remove($first);
|
let first_implemented = implemented_proto_fragments.remove($first);
|
||||||
|
@ -184,6 +181,10 @@ fn impl_protos(
|
||||||
);
|
);
|
||||||
try_add_shared_slot!("__pow__", "__rpow__", generate_pyclass_pow_slot);
|
try_add_shared_slot!("__pow__", "__rpow__", generate_pyclass_pow_slot);
|
||||||
|
|
||||||
|
assert!(implemented_proto_fragments.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_protos(ty: &syn::Type, proto_impls: Vec<TokenStream>) -> TokenStream {
|
||||||
quote! {
|
quote! {
|
||||||
impl ::pyo3::class::impl_::PyMethodsProtocolSlots<#ty>
|
impl ::pyo3::class::impl_::PyMethodsProtocolSlots<#ty>
|
||||||
for ::pyo3::class::impl_::PyClassImplCollector<#ty>
|
for ::pyo3::class::impl_::PyClassImplCollector<#ty>
|
||||||
|
@ -195,16 +196,16 @@ fn impl_protos(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn submit_methods_inventory(ty: &syn::Type, methods: Vec<TokenStream>) -> TokenStream {
|
fn submit_methods_inventory(
|
||||||
if methods.is_empty() {
|
ty: &syn::Type,
|
||||||
return TokenStream::default();
|
methods: Vec<TokenStream>,
|
||||||
}
|
proto_impls: Vec<TokenStream>,
|
||||||
|
) -> TokenStream {
|
||||||
quote! {
|
quote! {
|
||||||
::pyo3::inventory::submit! {
|
::pyo3::inventory::submit! {
|
||||||
#![crate = ::pyo3] {
|
#![crate = ::pyo3] {
|
||||||
type Inventory = <#ty as ::pyo3::class::impl_::HasMethodsInventory>::Methods;
|
type Inventory = <#ty as ::pyo3::class::impl_::HasMethodsInventory>::Methods;
|
||||||
<Inventory as ::pyo3::class::impl_::PyMethodsInventory>::new(::std::vec![#(#methods),*])
|
<Inventory as ::pyo3::class::impl_::PyMethodsInventory>::new(::std::vec![#(#methods),*], ::std::vec![#(#proto_impls),*])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -612,10 +612,13 @@ macro_rules! methods_trait {
|
||||||
#[cfg(all(feature = "macros", feature = "multiple-pymethods"))]
|
#[cfg(all(feature = "macros", feature = "multiple-pymethods"))]
|
||||||
pub trait PyMethodsInventory: inventory::Collect {
|
pub trait PyMethodsInventory: inventory::Collect {
|
||||||
/// Create a new instance
|
/// Create a new instance
|
||||||
fn new(methods: Vec<PyMethodDefType>) -> Self;
|
fn new(methods: Vec<PyMethodDefType>, slots: Vec<ffi::PyType_Slot>) -> Self;
|
||||||
|
|
||||||
/// Returns the methods for a single `#[pymethods] impl` block
|
/// Returns the methods for a single `#[pymethods] impl` block
|
||||||
fn get(&'static self) -> &'static [PyMethodDefType];
|
fn methods(&'static self) -> &'static [PyMethodDefType];
|
||||||
|
|
||||||
|
/// Returns the slots for a single `#[pymethods] impl` block
|
||||||
|
fn slots(&'static self) -> &'static [ffi::PyType_Slot];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implemented for `#[pyclass]` in our proc macro code.
|
/// Implemented for `#[pyclass]` in our proc macro code.
|
||||||
|
@ -663,6 +666,7 @@ slots_trait!(PyAsyncProtocolSlots, async_protocol_slots);
|
||||||
slots_trait!(PySequenceProtocolSlots, sequence_protocol_slots);
|
slots_trait!(PySequenceProtocolSlots, sequence_protocol_slots);
|
||||||
slots_trait!(PyBufferProtocolSlots, buffer_protocol_slots);
|
slots_trait!(PyBufferProtocolSlots, buffer_protocol_slots);
|
||||||
|
|
||||||
|
// Protocol slots from #[pymethods] if not using inventory.
|
||||||
#[cfg(not(feature = "multiple-pymethods"))]
|
#[cfg(not(feature = "multiple-pymethods"))]
|
||||||
slots_trait!(PyMethodsProtocolSlots, methods_protocol_slots);
|
slots_trait!(PyMethodsProtocolSlots, methods_protocol_slots);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![cfg(not(feature = "multiple-pymethods"))]
|
|
||||||
|
|
||||||
use pyo3::class::basic::CompareOp;
|
use pyo3::class::basic::CompareOp;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use pyo3::py_run;
|
use pyo3::py_run;
|
||||||
|
|
|
@ -2,8 +2,6 @@ mod hygiene {
|
||||||
mod misc;
|
mod misc;
|
||||||
mod pyclass;
|
mod pyclass;
|
||||||
mod pyfunction;
|
mod pyfunction;
|
||||||
// cannot implement protos in #[pymethods] using `multiple-pymethods` feature
|
|
||||||
#[cfg(not(feature = "multiple-pymethods"))]
|
|
||||||
mod pymethods;
|
mod pymethods;
|
||||||
mod pymodule;
|
mod pymodule;
|
||||||
mod pyproto;
|
mod pyproto;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#![cfg(not(feature = "multiple-pymethods"))]
|
|
||||||
|
|
||||||
use pyo3::exceptions::PyValueError;
|
use pyo3::exceptions::PyValueError;
|
||||||
use pyo3::types::{PySlice, PyType};
|
use pyo3::types::{PySlice, PyType};
|
||||||
use pyo3::{exceptions::PyAttributeError, prelude::*};
|
use pyo3::{exceptions::PyAttributeError, prelude::*};
|
||||||
|
|
|
@ -210,30 +210,6 @@ fn sequence() {
|
||||||
py_expect_exception!(py, c, "c['abc']", PyTypeError);
|
py_expect_exception!(py, c, "c['abc']", PyTypeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
|
||||||
struct Callable {}
|
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl Callable {
|
|
||||||
#[__call__]
|
|
||||||
fn __call__(&self, arg: i32) -> i32 {
|
|
||||||
arg * 6
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn callable() {
|
|
||||||
let gil = Python::acquire_gil();
|
|
||||||
let py = gil.python();
|
|
||||||
|
|
||||||
let c = Py::new(py, Callable {}).unwrap();
|
|
||||||
py_assert!(py, c, "callable(c)");
|
|
||||||
py_assert!(py, c, "c(7) == 42");
|
|
||||||
|
|
||||||
let nc = Py::new(py, Comparisons { val: 0 }).unwrap();
|
|
||||||
py_assert!(py, nc, "not callable(nc)");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct SetItem {
|
struct SetItem {
|
|
@ -6,15 +6,15 @@ error[E0277]: the trait bound `PyDict: PyClass` is not satisfied
|
||||||
|
|
|
|
||||||
= note: required because of the requirements on the impl of `PyClassBaseType` for `PyDict`
|
= note: required because of the requirements on the impl of `PyClassBaseType` for `PyDict`
|
||||||
note: required by a bound in `PyClassBaseType`
|
note: required by a bound in `PyClassBaseType`
|
||||||
--> src/class/impl_.rs:762:1
|
--> src/class/impl_.rs:766:1
|
||||||
|
|
|
|
||||||
762 | / pub trait PyClassBaseType: Sized {
|
766 | / pub trait PyClassBaseType: Sized {
|
||||||
763 | | type Dict;
|
767 | | type Dict;
|
||||||
764 | | type WeakRef;
|
768 | | type WeakRef;
|
||||||
765 | | type LayoutAsBase: PyCellLayout<Self>;
|
769 | | type LayoutAsBase: PyCellLayout<Self>;
|
||||||
... |
|
... |
|
||||||
768 | | type Initializer: PyObjectInit<Self>;
|
772 | | type Initializer: PyObjectInit<Self>;
|
||||||
769 | | }
|
773 | | }
|
||||||
| |_^ required by this bound in `PyClassBaseType`
|
| |_^ required by this bound in `PyClassBaseType`
|
||||||
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@ error[E0277]: the trait bound `PyDict: PyClass` is not satisfied
|
||||||
|
|
|
|
||||||
= note: required because of the requirements on the impl of `PyClassBaseType` for `PyDict`
|
= note: required because of the requirements on the impl of `PyClassBaseType` for `PyDict`
|
||||||
note: required by a bound in `ThreadCheckerInherited`
|
note: required by a bound in `ThreadCheckerInherited`
|
||||||
--> src/class/impl_.rs:749:47
|
--> src/class/impl_.rs:753:47
|
||||||
|
|
|
|
||||||
749 | pub struct ThreadCheckerInherited<T: Send, U: PyClassBaseType>(PhantomData<T>, U::ThreadChecker);
|
753 | pub struct ThreadCheckerInherited<T: Send, U: PyClassBaseType>(PhantomData<T>, U::ThreadChecker);
|
||||||
| ^^^^^^^^^^^^^^^ required by this bound in `ThreadCheckerInherited`
|
| ^^^^^^^^^^^^^^^ required by this bound in `ThreadCheckerInherited`
|
||||||
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
|
@ -11,8 +11,8 @@ note: required because it appears within the type `NotThreadSafe`
|
||||||
5 | struct NotThreadSafe {
|
5 | struct NotThreadSafe {
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
note: required by a bound in `ThreadCheckerStub`
|
note: required by a bound in `ThreadCheckerStub`
|
||||||
--> src/class/impl_.rs:706:33
|
--> src/class/impl_.rs:710:33
|
||||||
|
|
|
|
||||||
706 | pub struct ThreadCheckerStub<T: Send>(PhantomData<T>);
|
710 | pub struct ThreadCheckerStub<T: Send>(PhantomData<T>);
|
||||||
| ^^^^ required by this bound in `ThreadCheckerStub`
|
| ^^^^ required by this bound in `ThreadCheckerStub`
|
||||||
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the attribute macro `pyclass` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
Loading…
Reference in New Issue