diff --git a/CHANGELOG.md b/CHANGELOG.md index e90482636e9..4fe74cb09ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Change return type of `PyTuple::as_slice` to `&[&PyAny]`. [#971](https://github.com/PyO3/pyo3/pull/971) - Update `num-complex` optional dependendency from `0.2` to `0.3`. [#977](https://github.com/PyO3/pyo3/pull/977) - Update `num-bigint` optional dependendency from `0.2` to `0.3`. [#978](https://github.com/PyO3/pyo3/pull/978) +- `#[pyproto]` is re-implemented without specialization. [#961](https://github.com/PyO3/pyo3/pull/961) ### Removed - Remove `ManagedPyRef` (unused, and needs specialization) [#930](https://github.com/PyO3/pyo3/pull/930) diff --git a/Cargo.toml b/Cargo.toml index 3a72b361438..d18cafd6ee4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ travis-ci = { repository = "PyO3/pyo3", branch = "master" } appveyor = { repository = "fafhrd91/pyo3" } [dependencies] +ctor = { version = "0.1", optional = true } indoc = { version = "0.3.4", optional = true } inventory = { version = "0.1.4", optional = true } libc = "0.2.62" @@ -38,7 +39,7 @@ version_check = "0.9.1" [features] default = ["macros"] -macros = ["indoc", "inventory", "paste", "pyo3cls", "unindent"] +macros = ["ctor", "indoc", "inventory", "paste", "pyo3cls", "unindent"] # this is no longer needed internally, but setuptools-rust assumes this feature python3 = [] diff --git a/guide/src/class.md b/guide/src/class.md index 2a05ded9076..391e6e40e4d 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -956,6 +956,14 @@ impl pyo3::class::methods::HasMethodsInventory for MyClass { type Methods = Pyo3MethodsInventoryForMyClass; } pyo3::inventory::collect!(Pyo3MethodsInventoryForMyClass); + +impl pyo3::class::proto_methods::HasProtoRegistry for MyClass { + fn registry() -> &'static pyo3::class::proto_methods::PyProtoRegistry { + static REGISTRY: pyo3::class::proto_methods::PyProtoRegistry + = pyo3::class::proto_methods::PyProtoRegistry::new(); + ®ISTRY + } +} # let gil = Python::acquire_gil(); # let py = gil.python(); # let cls = py.get_type::(); diff --git a/pyo3-derive-backend/src/defs.rs b/pyo3-derive-backend/src/defs.rs index 01240d44546..d7d73cfc996 100644 --- a/pyo3-derive-backend/src/defs.rs +++ b/pyo3-derive-backend/src/defs.rs @@ -1,10 +1,20 @@ // Copyright (c) 2017-present PyO3 Project and Contributors use crate::func::MethodProto; +/// Predicates for `#[pyproto]`. pub struct Proto { + /// The name of this protocol. E.g., Iter. pub name: &'static str, + /// The name of slot table. E.g., PyIterMethods. + pub slot_table: &'static str, + /// The name of the setter used to set the table to `PyProtoRegistry`. + pub set_slot_table: &'static str, + /// All methods. pub methods: &'static [MethodProto], + /// All methods registered as normal methods like `#[pymethods]`. pub py_methods: &'static [PyMethod], + /// All methods registered to the slot table. + pub slot_setters: &'static [SlotSetter], } impl Proto { @@ -22,6 +32,7 @@ impl Proto { } } +/// Represents a method registered as a normal method like `#[pymethods]`. // TODO(kngwyu): Currently only __radd__-like methods use METH_COEXIST to prevent // __add__-like methods from overriding them. pub struct PyMethod { @@ -47,8 +58,33 @@ impl PyMethod { } } +/// Represents a setter used to register a method to the method table. +pub struct SlotSetter { + /// Protocols necessary for invoking this setter. + /// E.g., we need `__setattr__` and `__delattr__` for invoking `set_setdelitem`. + pub proto_names: &'static [&'static str], + /// The name of the setter called to the method table. + pub set_function: &'static str, + /// Represents a set of setters disabled by this setter. + /// E.g., `set_setdelitem` have to disable `set_setitem` and `set_delitem`. + pub skipped_setters: &'static [&'static str], +} + +impl SlotSetter { + const EMPTY_SETTERS: &'static [&'static str] = &[]; + const fn new(names: &'static [&'static str], set_function: &'static str) -> Self { + SlotSetter { + proto_names: names, + set_function, + skipped_setters: Self::EMPTY_SETTERS, + } + } +} + pub const OBJECT: Proto = Proto { name: "Object", + slot_table: "pyo3::class::basic::PyObjectMethods", + set_slot_table: "set_basic_methods", methods: &[ MethodProto::Binary { name: "__getattr__", @@ -95,40 +131,60 @@ pub const OBJECT: Proto = Proto { pyres: true, proto: "pyo3::class::basic::PyObjectBytesProtocol", }, - MethodProto::Unary { - name: "__bool__", - pyres: false, - proto: "pyo3::class::basic::PyObjectBoolProtocol", - }, MethodProto::Binary { name: "__richcmp__", arg: "Other", pyres: true, proto: "pyo3::class::basic::PyObjectRichcmpProtocol", }, + MethodProto::Unary { + name: "__bool__", + pyres: false, + proto: "pyo3::class::basic::PyObjectBoolProtocol", + }, ], py_methods: &[ PyMethod::new("__format__", "pyo3::class::basic::FormatProtocolImpl"), PyMethod::new("__bytes__", "pyo3::class::basic::BytesProtocolImpl"), PyMethod::new("__unicode__", "pyo3::class::basic::UnicodeProtocolImpl"), ], + slot_setters: &[ + SlotSetter::new(&["__str__"], "set_str"), + SlotSetter::new(&["__repr__"], "set_repr"), + SlotSetter::new(&["__hash__"], "set_hash"), + SlotSetter::new(&["__getattr__"], "set_getattr"), + SlotSetter::new(&["__richcmp__"], "set_richcompare"), + SlotSetter { + proto_names: &["__setattr__", "__delattr__"], + set_function: "set_setdelattr", + skipped_setters: &["set_setattr", "set_delattr"], + }, + SlotSetter::new(&["__setattr__"], "set_setattr"), + SlotSetter::new(&["__delattr__"], "set_delattr"), + SlotSetter::new(&["__bool__"], "set_bool"), + ], }; pub const ASYNC: Proto = Proto { name: "Async", + slot_table: "pyo3::ffi::PyAsyncMethods", + set_slot_table: "set_async_methods", methods: &[ - MethodProto::Unary { + MethodProto::UnaryS { name: "__await__", + arg: "Receiver", pyres: true, proto: "pyo3::class::pyasync::PyAsyncAwaitProtocol", }, - MethodProto::Unary { + MethodProto::UnaryS { name: "__aiter__", + arg: "Receiver", pyres: true, proto: "pyo3::class::pyasync::PyAsyncAiterProtocol", }, - MethodProto::Unary { + MethodProto::UnaryS { name: "__anext__", + arg: "Receiver", pyres: true, proto: "pyo3::class::pyasync::PyAsyncAnextProtocol", }, @@ -155,10 +211,17 @@ pub const ASYNC: Proto = Proto { "pyo3::class::pyasync::PyAsyncAexitProtocolImpl", ), ], + slot_setters: &[ + SlotSetter::new(&["__await__"], "set_await"), + SlotSetter::new(&["__aiter__"], "set_aiter"), + SlotSetter::new(&["__anext__"], "set_anext"), + ], }; pub const BUFFER: Proto = Proto { name: "Buffer", + slot_table: "pyo3::ffi::PyBufferProcs", + set_slot_table: "set_buffer_methods", methods: &[ MethodProto::Unary { name: "bf_getbuffer", @@ -172,10 +235,16 @@ pub const BUFFER: Proto = Proto { }, ], py_methods: &[], + slot_setters: &[ + SlotSetter::new(&["bf_getbuffer"], "set_getbuffer"), + SlotSetter::new(&["bf_releasebuffer"], "set_releasebuffer"), + ], }; pub const CONTEXT: Proto = Proto { name: "Context", + slot_table: "", + set_slot_table: "", methods: &[ MethodProto::Unary { name: "__enter__", @@ -200,10 +269,13 @@ pub const CONTEXT: Proto = Proto { "pyo3::class::context::PyContextExitProtocolImpl", ), ], + slot_setters: &[], }; pub const GC: Proto = Proto { name: "GC", + slot_table: "pyo3::class::gc::PyGCMethods", + set_slot_table: "set_gc_methods", methods: &[ MethodProto::Free { name: "__traverse__", @@ -215,23 +287,31 @@ pub const GC: Proto = Proto { }, ], py_methods: &[], + slot_setters: &[ + SlotSetter::new(&["__traverse__"], "set_traverse"), + SlotSetter::new(&["__clear__"], "set_clear"), + ], }; pub const DESCR: Proto = Proto { name: "Descriptor", + slot_table: "pyo3::class::descr::PyDescrMethods", + set_slot_table: "set_descr_methods", methods: &[ - MethodProto::Ternary { + MethodProto::TernaryS { name: "__get__", - arg1: "Inst", - arg2: "Owner", + arg1: "Receiver", + arg2: "Inst", + arg3: "Owner", pyres: true, proto: "pyo3::class::descr::PyDescrGetProtocol", }, - MethodProto::Ternary { + MethodProto::TernaryS { name: "__set__", - arg1: "Inst", - arg2: "Value", - pyres: true, + arg1: "Receiver", + arg2: "Inst", + arg3: "Value", + pyres: false, proto: "pyo3::class::descr::PyDescrSetProtocol", }, MethodProto::Binary { @@ -254,10 +334,16 @@ pub const DESCR: Proto = Proto { "pyo3::class::context::PyDescrNameProtocolImpl", ), ], + slot_setters: &[ + SlotSetter::new(&["__get__"], "set_descr_get"), + SlotSetter::new(&["__set__"], "set_descr_set"), + ], }; pub const ITER: Proto = Proto { name: "Iter", + slot_table: "pyo3::class::iter::PyIterMethods", + set_slot_table: "set_iter_methods", py_methods: &[], methods: &[ MethodProto::UnaryS { @@ -273,10 +359,16 @@ pub const ITER: Proto = Proto { proto: "pyo3::class::iter::PyIterNextProtocol", }, ], + slot_setters: &[ + SlotSetter::new(&["__iter__"], "set_iter"), + SlotSetter::new(&["__next__"], "set_iternext"), + ], }; pub const MAPPING: Proto = Proto { name: "Mapping", + slot_table: "pyo3::ffi::PyMappingMethods", + set_slot_table: "set_mapping_methods", methods: &[ MethodProto::Unary { name: "__len__", @@ -312,10 +404,23 @@ pub const MAPPING: Proto = Proto { "__reversed__", "pyo3::class::mapping::PyMappingReversedProtocolImpl", )], + slot_setters: &[ + SlotSetter::new(&["__len__"], "set_length"), + SlotSetter::new(&["__getitem__"], "set_getitem"), + SlotSetter { + proto_names: &["__setitem__", "__delitem__"], + set_function: "set_setdelitem", + skipped_setters: &["set_setitem", "set_delitem"], + }, + SlotSetter::new(&["__setitem__"], "set_setitem"), + SlotSetter::new(&["__delitem__"], "set_delitem"), + ], }; pub const SEQ: Proto = Proto { name: "Sequence", + slot_table: "pyo3::ffi::PySequenceMethods", + set_slot_table: "set_sequence_methods", methods: &[ MethodProto::Unary { name: "__len__", @@ -373,10 +478,28 @@ pub const SEQ: Proto = Proto { }, ], py_methods: &[], + slot_setters: &[ + SlotSetter::new(&["__len__"], "set_len"), + SlotSetter::new(&["__concat__"], "set_concat"), + SlotSetter::new(&["__repeat__"], "set_repeat"), + SlotSetter::new(&["__getitem__"], "set_getitem"), + SlotSetter { + proto_names: &["__setitem__", "__delitem__"], + set_function: "set_setdelitem", + skipped_setters: &["set_setitem", "set_delitem"], + }, + SlotSetter::new(&["__setitem__"], "set_setitem"), + SlotSetter::new(&["__delitem__"], "set_delitem"), + SlotSetter::new(&["__contains__"], "set_contains"), + SlotSetter::new(&["__inplace_concat__"], "set_inplace_concat"), + SlotSetter::new(&["__inplace_repeat__"], "set_inplace_repeat"), + ], }; pub const NUM: Proto = Proto { name: "Number", + slot_table: "pyo3::ffi::PyNumberMethods", + set_slot_table: "set_number_methods", methods: &[ MethodProto::BinaryS { name: "__add__", @@ -729,4 +852,106 @@ pub const NUM: Proto = Proto { "pyo3::class::number::PyNumberRoundProtocolImpl", ), ], + slot_setters: &[ + SlotSetter { + proto_names: &["__add__"], + set_function: "set_add", + skipped_setters: &["set_radd"], + }, + SlotSetter::new(&["__radd__"], "set_radd"), + SlotSetter { + proto_names: &["__sub__"], + set_function: "set_sub", + skipped_setters: &["set_rsub"], + }, + SlotSetter::new(&["__rsub__"], "set_rsub"), + SlotSetter { + proto_names: &["__mul__"], + set_function: "set_mul", + skipped_setters: &["set_rmul"], + }, + SlotSetter::new(&["__rmul__"], "set_rmul"), + SlotSetter::new(&["__mod__"], "set_mod"), + SlotSetter { + proto_names: &["__divmod__"], + set_function: "set_divmod", + skipped_setters: &["set_rdivmod"], + }, + SlotSetter::new(&["__rdivmod__"], "set_rdivmod"), + SlotSetter { + proto_names: &["__pow__"], + set_function: "set_pow", + skipped_setters: &["set_rpow"], + }, + SlotSetter::new(&["__rpow__"], "set_rpow"), + SlotSetter::new(&["__neg__"], "set_neg"), + SlotSetter::new(&["__pos__"], "set_pos"), + SlotSetter::new(&["__abs__"], "set_abs"), + SlotSetter::new(&["__invert__"], "set_invert"), + SlotSetter::new(&["__rdivmod__"], "set_rdivmod"), + SlotSetter { + proto_names: &["__lshift__"], + set_function: "set_lshift", + skipped_setters: &["set_rlshift"], + }, + SlotSetter::new(&["__rlshift__"], "set_rlshift"), + SlotSetter { + proto_names: &["__rshift__"], + set_function: "set_rshift", + skipped_setters: &["set_rrshift"], + }, + SlotSetter::new(&["__rrshift__"], "set_rrshift"), + SlotSetter { + proto_names: &["__and__"], + set_function: "set_and", + skipped_setters: &["set_rand"], + }, + SlotSetter::new(&["__rand__"], "set_rand"), + SlotSetter { + proto_names: &["__xor__"], + set_function: "set_xor", + skipped_setters: &["set_rxor"], + }, + SlotSetter::new(&["__rxor__"], "set_rxor"), + SlotSetter { + proto_names: &["__or__"], + set_function: "set_or", + skipped_setters: &["set_ror"], + }, + SlotSetter::new(&["__ror__"], "set_ror"), + SlotSetter::new(&["__int__"], "set_int"), + SlotSetter::new(&["__float__"], "set_float"), + SlotSetter::new(&["__iadd__"], "set_iadd"), + SlotSetter::new(&["__isub__"], "set_isub"), + SlotSetter::new(&["__imul__"], "set_imul"), + SlotSetter::new(&["__imod__"], "set_imod"), + SlotSetter::new(&["__ipow__"], "set_ipow"), + SlotSetter::new(&["__ilshift__"], "set_ilshift"), + SlotSetter::new(&["__irshift__"], "set_irshift"), + SlotSetter::new(&["__iand__"], "set_iand"), + SlotSetter::new(&["__ixor__"], "set_ixor"), + SlotSetter::new(&["__ior__"], "set_ior"), + SlotSetter { + proto_names: &["__floordiv__"], + set_function: "set_floordiv", + skipped_setters: &["set_rfloordiv"], + }, + SlotSetter::new(&["__rfloordiv__"], "set_rfloordiv"), + SlotSetter { + proto_names: &["__truediv__"], + set_function: "set_truediv", + skipped_setters: &["set_rtruediv"], + }, + SlotSetter::new(&["__rtruediv__"], "set_rtruediv"), + SlotSetter::new(&["__ifloordiv__"], "set_ifloordiv"), + SlotSetter::new(&["__itruediv__"], "set_itruediv"), + SlotSetter::new(&["__index__"], "set_index"), + SlotSetter { + proto_names: &["__matmul__"], + set_function: "set_matmul", + skipped_setters: &["set_rmatmul"], + }, + SlotSetter::new(&["__rmatmul__"], "set_rmatmul"), + SlotSetter::new(&["__imatmul__"], "set_imatmul"), + ], }; diff --git a/pyo3-derive-backend/src/pyclass.rs b/pyo3-derive-backend/src/pyclass.rs index 33a45ba61c7..251c9879bf9 100644 --- a/pyo3-derive-backend/src/pyclass.rs +++ b/pyo3-derive-backend/src/pyclass.rs @@ -236,6 +236,19 @@ fn impl_methods_inventory(cls: &syn::Ident) -> TokenStream { } } +/// Implement `HasProtoRegistry` for the class for lazy protocol initialization. +fn impl_proto_registry(cls: &syn::Ident) -> TokenStream { + quote! { + impl pyo3::class::proto_methods::HasProtoRegistry for #cls { + fn registry() -> &'static pyo3::class::proto_methods::PyProtoRegistry { + static REGISTRY: pyo3::class::proto_methods::PyProtoRegistry + = pyo3::class::proto_methods::PyProtoRegistry::new(); + ®ISTRY + } + } + } +} + fn get_class_python_name(cls: &syn::Ident, attr: &PyClassArgs) -> TokenStream { match &attr.name { Some(name) => quote! { #name }, @@ -340,6 +353,7 @@ fn impl_class( }; let impl_inventory = impl_methods_inventory(&cls); + let impl_proto_registry = impl_proto_registry(&cls); let base = &attr.base; let flags = &attr.flags; @@ -414,6 +428,8 @@ fn impl_class( #impl_inventory + #impl_proto_registry + #extra #gc_impl diff --git a/pyo3-derive-backend/src/pyproto.rs b/pyo3-derive-backend/src/pyproto.rs index 09459556b74..1cded509c3e 100644 --- a/pyo3-derive-backend/src/pyproto.rs +++ b/pyo3-derive-backend/src/pyproto.rs @@ -4,9 +4,10 @@ use crate::defs; use crate::func::impl_method_proto; use crate::method::FnSpec; use crate::pymethod; -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::quote; use quote::ToTokens; +use std::collections::HashSet; pub fn build_py_proto(ast: &mut syn::ItemImpl) -> syn::Result { if let Some((_, ref mut path, _)) = ast.trait_ { @@ -60,12 +61,17 @@ fn impl_proto_impl( ) -> syn::Result { let mut trait_impls = TokenStream::new(); let mut py_methods = Vec::new(); + let mut method_names = HashSet::new(); for iimpl in impls.iter_mut() { if let syn::ImplItem::Method(ref mut met) = iimpl { + // impl Py~Protocol<'p> { type = ... } if let Some(m) = proto.get_proto(&met.sig.ident) { impl_method_proto(ty, &mut met.sig, m).to_tokens(&mut trait_impls); + // Insert the method to the HashSet + method_names.insert(met.sig.ident.to_string()); } + // Add non-slot methods to inventory like `#[pymethods]` if let Some(m) = proto.get_method(&met.sig.ident) { let name = &met.sig.ident; let fn_spec = FnSpec::parse(&met.sig, &mut met.attrs, false)?; @@ -76,7 +82,7 @@ fn impl_proto_impl( } else { quote!(0) }; - // TODO(kngwyu): doc + // TODO(kngwyu): Set ml_doc py_methods.push(quote! { pyo3::class::PyMethodDefType::Method({ #method @@ -91,20 +97,78 @@ fn impl_proto_impl( } } } + let inventory_submission = inventory_submission(py_methods, ty); + let slot_initialization = slot_initialization(method_names, ty, proto)?; + Ok(quote! { + #trait_impls + #inventory_submission + #slot_initialization + }) +} +fn inventory_submission(py_methods: Vec, ty: &syn::Type) -> TokenStream { if py_methods.is_empty() { - return Ok(quote! { #trait_impls }); + return quote! {}; } - let inventory_submission = quote! { + quote! { pyo3::inventory::submit! { #![crate = pyo3] { type Inventory = <#ty as pyo3::class::methods::HasMethodsInventory>::Methods; ::new(&[#(#py_methods),*]) } } - }; + } +} + +fn slot_initialization( + method_names: HashSet, + ty: &syn::Type, + proto: &defs::Proto, +) -> syn::Result { + // Some setters cannot coexist. + // E.g., if we have `__add__`, we need to skip `set_radd`. + let mut skipped_setters = Vec::new(); + // Collect initializers + let mut initializers: Vec = vec![]; + 'outer_loop: for m in proto.slot_setters { + if skipped_setters.contains(&m.set_function) { + continue; + } + for name in m.proto_names { + // If this `#[pyproto]` block doesn't provide all required methods, + // let's skip implementing this method. + if !method_names.contains(*name) { + continue 'outer_loop; + } + } + skipped_setters.extend_from_slice(m.skipped_setters); + // Add slot methods to PyProtoRegistry + let set = syn::Ident::new(m.set_function, Span::call_site()); + initializers.push(quote! { table.#set::<#ty>(); }); + } + if initializers.is_empty() { + return Ok(quote! {}); + } + let table: syn::Path = syn::parse_str(proto.slot_table)?; + let set = syn::Ident::new(proto.set_slot_table, Span::call_site()); + let ty_hash = typename_hash(ty); + let init = syn::Ident::new( + &format!("__init_{}_{}", proto.name, ty_hash), + Span::call_site(), + ); Ok(quote! { - #trait_impls - #inventory_submission + #[pyo3::ctor::ctor] + fn #init() { + let mut table = #table::default(); + #(#initializers)* + <#ty as pyo3::class::proto_methods::HasProtoRegistry>::registry().#set(table); + } }) } + +fn typename_hash(ty: &syn::Type) -> u64 { + use std::hash::{Hash, Hasher}; + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + ty.hash(&mut hasher); + hasher.finish() +} diff --git a/src/class/basic.rs b/src/class/basic.rs index 30183cb8135..9af60e51b50 100644 --- a/src/class/basic.rs +++ b/src/class/basic.rs @@ -77,13 +77,6 @@ pub trait PyObjectProtocol<'p>: PyClass { unimplemented!() } - fn __bool__(&'p self) -> Self::Result - where - Self: PyObjectBoolProtocol<'p>, - { - unimplemented!() - } - fn __bytes__(&'p self) -> Self::Result where Self: PyObjectBytesProtocol<'p>, @@ -97,6 +90,12 @@ pub trait PyObjectProtocol<'p>: PyClass { { unimplemented!() } + fn __bool__(&'p self) -> Self::Result + where + Self: PyObjectBoolProtocol<'p>, + { + unimplemented!() + } } pub trait PyObjectGetAttrProtocol<'p>: PyObjectProtocol<'p> { @@ -142,311 +141,160 @@ pub trait PyObjectRichcmpProtocol<'p>: PyObjectProtocol<'p> { type Result: Into>; } -#[doc(hidden)] -pub trait PyObjectProtocolImpl { - fn tp_as_object(_type_object: &mut ffi::PyTypeObject); - fn nb_bool_fn() -> Option; -} - -impl PyObjectProtocolImpl for T { - default fn tp_as_object(_type_object: &mut ffi::PyTypeObject) {} - default fn nb_bool_fn() -> Option { - None - } +/// All FFI functions for basic protocols. +#[derive(Default)] +pub struct PyObjectMethods { + pub tp_str: Option, + pub tp_repr: Option, + pub tp_hash: Option, + pub tp_getattro: Option, + pub tp_richcompare: Option, + pub tp_setattro: Option, + pub nb_bool: Option, } -impl<'p, T> PyObjectProtocolImpl for T -where - T: PyObjectProtocol<'p>, -{ - fn tp_as_object(type_object: &mut ffi::PyTypeObject) { - type_object.tp_str = Self::tp_str(); - type_object.tp_repr = Self::tp_repr(); - type_object.tp_hash = Self::tp_hash(); - type_object.tp_getattro = Self::tp_getattro(); - type_object.tp_richcompare = Self::tp_richcompare(); - type_object.tp_setattro = tp_setattro_impl::tp_setattro::(); - } - fn nb_bool_fn() -> Option { - Self::nb_bool() - } -} - -trait GetAttrProtocolImpl { - fn tp_getattro() -> Option; -} - -impl<'p, T> GetAttrProtocolImpl for T -where - T: PyObjectProtocol<'p>, -{ - default fn tp_getattro() -> Option { - None - } -} - -impl GetAttrProtocolImpl for T -where - T: for<'p> PyObjectGetAttrProtocol<'p>, -{ - fn tp_getattro() -> Option { - #[allow(unused_mut)] - unsafe extern "C" fn wrap( - slf: *mut ffi::PyObject, - arg: *mut ffi::PyObject, - ) -> *mut ffi::PyObject - where - T: for<'p> PyObjectGetAttrProtocol<'p>, - { - crate::callback_body!(py, { - // Behave like python's __getattr__ (as opposed to __getattribute__) and check - // for existing fields and methods first - let existing = ffi::PyObject_GenericGetAttr(slf, arg); - if existing.is_null() { - // PyObject_HasAttr also tries to get an object and clears the error if it fails - ffi::PyErr_Clear(); - } else { - return Ok(existing); - } - - let slf = py.from_borrowed_ptr::>(slf); - let arg = py.from_borrowed_ptr::(arg); - call_ref!(slf, __getattr__, arg) - }) - } - Some(wrap::) - } -} - -/// An object may support setting attributes (by implementing PyObjectSetAttrProtocol) -/// and may support deleting attributes (by implementing PyObjectDelAttrProtocol). -/// We need to generate a single "extern C" function that supports only setting, only deleting -/// or both, and return None in case none of the two is supported. -mod tp_setattro_impl { - use super::*; - - /// setattrofunc PyTypeObject.tp_setattro - /// - /// An optional pointer to the function for setting and deleting attributes. - /// - /// The signature is the same as for PyObject_SetAttr(), but setting v to NULL to delete an - /// attribute must be supported. It is usually convenient to set this field to - /// PyObject_GenericSetAttr(), which implements the normal way of setting object attributes. - pub(super) fn tp_setattro<'p, T: PyObjectProtocol<'p>>() -> Option { - if let Some(set_del) = T::set_del_attr() { - Some(set_del) - } else if let Some(set) = T::set_attr() { - Some(set) - } else if let Some(del) = T::del_attr() { - Some(del) - } else { - None - } - } - - trait SetAttr { - fn set_attr() -> Option; - } - - impl<'p, T: PyObjectProtocol<'p>> SetAttr for T { - default fn set_attr() -> Option { - None - } +#[doc(hidden)] +impl PyObjectMethods { + pub(crate) fn update_typeobj(&self, type_object: &mut ffi::PyTypeObject) { + type_object.tp_str = self.tp_str; + type_object.tp_repr = self.tp_repr; + type_object.tp_hash = self.tp_hash; + type_object.tp_getattro = self.tp_getattro; + type_object.tp_richcompare = self.tp_richcompare; + type_object.tp_setattro = self.tp_setattro; + } + // Set functions used by `#[pyproto]`. + pub fn set_str(&mut self) + where + T: for<'p> PyObjectStrProtocol<'p>, + { + self.tp_str = py_unary_func!(PyObjectStrProtocol, T::__str__); } - - impl SetAttr for T + pub fn set_repr(&mut self) where - T: for<'p> PyObjectSetAttrProtocol<'p>, + T: for<'p> PyObjectReprProtocol<'p>, { - fn set_attr() -> Option { - py_func_set!(PyObjectSetAttrProtocol, T, __setattr__) - } + self.tp_repr = py_unary_func!(PyObjectReprProtocol, T::__repr__); } - - trait DelAttr { - fn del_attr() -> Option; + pub fn set_hash(&mut self) + where + T: for<'p> PyObjectHashProtocol<'p>, + { + self.tp_hash = py_unary_func!( + PyObjectHashProtocol, + T::__hash__, + ffi::Py_hash_t, + HashCallbackOutput + ); } - - impl<'p, T> DelAttr for T + pub fn set_getattr(&mut self) where - T: PyObjectProtocol<'p>, + T: for<'p> PyObjectGetAttrProtocol<'p>, { - default fn del_attr() -> Option { - None - } + self.tp_getattro = tp_getattro::(); } - - impl DelAttr for T + pub fn set_richcompare(&mut self) where - T: for<'p> PyObjectDelAttrProtocol<'p>, + T: for<'p> PyObjectRichcmpProtocol<'p>, { - fn del_attr() -> Option { - py_func_del!(PyObjectDelAttrProtocol, T, __delattr__) - } + self.tp_richcompare = tp_richcompare::(); } - - trait SetDelAttr { - fn set_del_attr() -> Option; + pub fn set_setattr(&mut self) + where + T: for<'p> PyObjectSetAttrProtocol<'p>, + { + self.tp_setattro = py_func_set!(PyObjectSetAttrProtocol, T, __setattr__); } - - impl<'p, T> SetDelAttr for T + pub fn set_delattr(&mut self) where - T: PyObjectProtocol<'p>, + T: for<'p> PyObjectDelAttrProtocol<'p>, { - default fn set_del_attr() -> Option { - None - } + self.tp_setattro = py_func_del!(PyObjectDelAttrProtocol, T, __delattr__); } - - impl SetDelAttr for T + pub fn set_setdelattr(&mut self) where T: for<'p> PyObjectSetAttrProtocol<'p> + for<'p> PyObjectDelAttrProtocol<'p>, { - fn set_del_attr() -> Option { - py_func_set_del!( - PyObjectSetAttrProtocol, - PyObjectDelAttrProtocol, - T, - __setattr__, - __delattr__ - ) - } - } -} - -trait StrProtocolImpl { - fn tp_str() -> Option; -} -impl<'p, T> StrProtocolImpl for T -where - T: PyObjectProtocol<'p>, -{ - default fn tp_str() -> Option { - None - } -} -impl StrProtocolImpl for T -where - T: for<'p> PyObjectStrProtocol<'p>, -{ - fn tp_str() -> Option { - py_unary_func!(PyObjectStrProtocol, T::__str__) - } -} - -trait ReprProtocolImpl { - fn tp_repr() -> Option; -} -impl<'p, T> ReprProtocolImpl for T -where - T: PyObjectProtocol<'p>, -{ - default fn tp_repr() -> Option { - None - } -} -impl ReprProtocolImpl for T -where - T: for<'p> PyObjectReprProtocol<'p>, -{ - fn tp_repr() -> Option { - py_unary_func!(PyObjectReprProtocol, T::__repr__) - } -} - -trait HashProtocolImpl { - fn tp_hash() -> Option; -} -impl<'p, T> HashProtocolImpl for T -where - T: PyObjectProtocol<'p>, -{ - default fn tp_hash() -> Option { - None - } -} -impl HashProtocolImpl for T -where - T: for<'p> PyObjectHashProtocol<'p>, -{ - fn tp_hash() -> Option { - py_unary_func!( - PyObjectHashProtocol, - T::__hash__, - ffi::Py_hash_t, - HashCallbackOutput + self.tp_setattro = py_func_set_del!( + PyObjectSetAttrProtocol, + PyObjectDelAttrProtocol, + T, + __setattr__, + __delattr__ ) } -} - -trait BoolProtocolImpl { - fn nb_bool() -> Option; -} -impl<'p, T> BoolProtocolImpl for T -where - T: PyObjectProtocol<'p>, -{ - default fn nb_bool() -> Option { - None - } -} -impl BoolProtocolImpl for T -where - T: for<'p> PyObjectBoolProtocol<'p>, -{ - fn nb_bool() -> Option { - py_unary_func!(PyObjectBoolProtocol, T::__bool__, c_int) + pub fn set_bool(&mut self) + where + T: for<'p> PyObjectBoolProtocol<'p>, + { + self.nb_bool = py_unary_func!(PyObjectBoolProtocol, T::__bool__, c_int); } } -trait RichcmpProtocolImpl { - fn tp_richcompare() -> Option; -} -impl<'p, T> RichcmpProtocolImpl for T +fn tp_getattro() -> Option where - T: PyObjectProtocol<'p>, + T: for<'p> PyObjectGetAttrProtocol<'p>, { - default fn tp_richcompare() -> Option { - None - } -} -impl RichcmpProtocolImpl for T + unsafe extern "C" fn wrap( + slf: *mut ffi::PyObject, + arg: *mut ffi::PyObject, + ) -> *mut ffi::PyObject + where + T: for<'p> PyObjectGetAttrProtocol<'p>, + { + crate::callback_body!(py, { + // Behave like python's __getattr__ (as opposed to __getattribute__) and check + // for existing fields and methods first + let existing = ffi::PyObject_GenericGetAttr(slf, arg); + if existing.is_null() { + // PyObject_HasAttr also tries to get an object and clears the error if it fails + ffi::PyErr_Clear(); + } else { + return Ok(existing); + } + + let slf = py.from_borrowed_ptr::>(slf); + let arg = py.from_borrowed_ptr::(arg); + call_ref!(slf, __getattr__, arg) + }) + } + Some(wrap::) +} + +fn tp_richcompare() -> Option where T: for<'p> PyObjectRichcmpProtocol<'p>, { - fn tp_richcompare() -> Option { - unsafe extern "C" fn wrap( - slf: *mut ffi::PyObject, - arg: *mut ffi::PyObject, - op: c_int, - ) -> *mut ffi::PyObject - where - T: for<'p> PyObjectRichcmpProtocol<'p>, - { - crate::callback_body!(py, { - let slf = py.from_borrowed_ptr::>(slf); - let arg = py.from_borrowed_ptr::(arg); - - let op = extract_op(op)?; - let arg = arg.extract()?; - - slf.try_borrow()?.__richcmp__(arg, op).into() - }) + fn extract_op(op: c_int) -> PyResult { + match op { + ffi::Py_LT => Ok(CompareOp::Lt), + ffi::Py_LE => Ok(CompareOp::Le), + ffi::Py_EQ => Ok(CompareOp::Eq), + ffi::Py_NE => Ok(CompareOp::Ne), + ffi::Py_GT => Ok(CompareOp::Gt), + ffi::Py_GE => Ok(CompareOp::Ge), + _ => Err(PyErr::new::( + "tp_richcompare called with invalid comparison operator", + )), } - Some(wrap::) } -} + unsafe extern "C" fn wrap( + slf: *mut ffi::PyObject, + arg: *mut ffi::PyObject, + op: c_int, + ) -> *mut ffi::PyObject + where + T: for<'p> PyObjectRichcmpProtocol<'p>, + { + crate::callback_body!(py, { + let slf = py.from_borrowed_ptr::>(slf); + let arg = py.from_borrowed_ptr::(arg); + + let op = extract_op(op)?; + let arg = arg.extract()?; -fn extract_op(op: c_int) -> PyResult { - match op { - ffi::Py_LT => Ok(CompareOp::Lt), - ffi::Py_LE => Ok(CompareOp::Le), - ffi::Py_EQ => Ok(CompareOp::Eq), - ffi::Py_NE => Ok(CompareOp::Ne), - ffi::Py_GT => Ok(CompareOp::Gt), - ffi::Py_GE => Ok(CompareOp::Ge), - _ => Err(PyErr::new::( - "tp_richcompare called with invalid comparison operator", - )), + slf.try_borrow()?.__richcmp__(arg, op).into() + }) } + Some(wrap::) } diff --git a/src/class/buffer.rs b/src/class/buffer.rs index 7b27ef15f57..c77668583cd 100644 --- a/src/class/buffer.rs +++ b/src/class/buffer.rs @@ -5,7 +5,10 @@ //! For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html) //! c-api use crate::err::PyResult; -use crate::{ffi, PyCell, PyClass, PyRefMut}; +use crate::{ + ffi::{self, PyBufferProcs}, + PyCell, PyClass, PyRefMut, +}; use std::os::raw::c_int; /// Buffer protocol interface @@ -37,96 +40,55 @@ pub trait PyBufferReleaseBufferProtocol<'p>: PyBufferProtocol<'p> { type Result: Into>; } +/// Set functions used by `#[pyproto]`. #[doc(hidden)] -pub trait PyBufferProtocolImpl { - fn tp_as_buffer() -> Option; -} - -impl PyBufferProtocolImpl for T { - default fn tp_as_buffer() -> Option { - None - } -} - -impl<'p, T> PyBufferProtocolImpl for T -where - T: PyBufferProtocol<'p>, -{ - #[inline] - #[allow(clippy::needless_update)] // For python 2 it's not useless - fn tp_as_buffer() -> Option { - Some(ffi::PyBufferProcs { - bf_getbuffer: Self::cb_bf_getbuffer(), - bf_releasebuffer: Self::cb_bf_releasebuffer(), - ..ffi::PyBufferProcs_INIT - }) +impl PyBufferProcs { + pub fn set_getbuffer(&mut self) + where + T: for<'p> PyBufferGetBufferProtocol<'p>, + { + self.bf_getbuffer = bf_getbuffer::(); } -} - -trait PyBufferGetBufferProtocolImpl { - fn cb_bf_getbuffer() -> Option; -} - -impl<'p, T> PyBufferGetBufferProtocolImpl for T -where - T: PyBufferProtocol<'p>, -{ - default fn cb_bf_getbuffer() -> Option { - None + pub fn set_releasebuffer(&mut self) + where + T: for<'p> PyBufferReleaseBufferProtocol<'p>, + { + self.bf_releasebuffer = bf_releasebuffer::(); } } -impl PyBufferGetBufferProtocolImpl for T +fn bf_getbuffer() -> Option where T: for<'p> PyBufferGetBufferProtocol<'p>, { - #[inline] - fn cb_bf_getbuffer() -> Option { - unsafe extern "C" fn wrap( - slf: *mut ffi::PyObject, - arg1: *mut ffi::Py_buffer, - arg2: c_int, - ) -> c_int - where - T: for<'p> PyBufferGetBufferProtocol<'p>, - { - crate::callback_body!(py, { - let slf = py.from_borrowed_ptr::>(slf); - T::bf_getbuffer(slf.try_borrow_mut()?, arg1, arg2).into() - }) - } - Some(wrap::) - } -} - -trait PyBufferReleaseBufferProtocolImpl { - fn cb_bf_releasebuffer() -> Option; -} - -impl<'p, T> PyBufferReleaseBufferProtocolImpl for T -where - T: PyBufferProtocol<'p>, -{ - default fn cb_bf_releasebuffer() -> Option { - None + unsafe extern "C" fn wrap( + slf: *mut ffi::PyObject, + arg1: *mut ffi::Py_buffer, + arg2: c_int, + ) -> c_int + where + T: for<'p> PyBufferGetBufferProtocol<'p>, + { + crate::callback_body!(py, { + let slf = py.from_borrowed_ptr::>(slf); + T::bf_getbuffer(slf.try_borrow_mut()?, arg1, arg2).into() + }) } + Some(wrap::) } -impl PyBufferReleaseBufferProtocolImpl for T +fn bf_releasebuffer() -> Option where T: for<'p> PyBufferReleaseBufferProtocol<'p>, { - #[inline] - fn cb_bf_releasebuffer() -> Option { - unsafe extern "C" fn wrap(slf: *mut ffi::PyObject, arg1: *mut ffi::Py_buffer) - where - T: for<'p> PyBufferReleaseBufferProtocol<'p>, - { - crate::callback_body!(py, { - let slf = py.from_borrowed_ptr::>(slf); - T::bf_releasebuffer(slf.try_borrow_mut()?, arg1).into() - }) - } - Some(wrap::) + unsafe extern "C" fn wrap(slf: *mut ffi::PyObject, arg1: *mut ffi::Py_buffer) + where + T: for<'p> PyBufferReleaseBufferProtocol<'p>, + { + crate::callback_body!(py, { + let slf = py.from_borrowed_ptr::>(slf); + T::bf_releasebuffer(slf.try_borrow_mut()?, arg1).into() + }) } + Some(wrap::) } diff --git a/src/class/descr.rs b/src/class/descr.rs index 1349f7a734c..83421b4499b 100644 --- a/src/class/descr.rs +++ b/src/class/descr.rs @@ -5,23 +5,26 @@ //! [Python information]( //! https://docs.python.org/3/reference/datamodel.html#implementing-descriptors) -use crate::class::methods::PyMethodDef; use crate::err::PyResult; -use crate::types::{PyAny, PyType}; +use crate::types::PyAny; use crate::{ffi, FromPyObject, IntoPy, PyClass, PyObject}; use std::os::raw::c_int; /// Descriptor interface #[allow(unused_variables)] pub trait PyDescrProtocol<'p>: PyClass { - fn __get__(&'p self, instance: &'p PyAny, owner: Option<&'p PyType>) -> Self::Result + fn __get__( + slf: Self::Receiver, + instance: Self::Inst, + owner: Option, + ) -> Self::Result where Self: PyDescrGetProtocol<'p>, { unimplemented!() } - fn __set__(&'p self, instance: &'p PyAny, value: &'p PyAny) -> Self::Result + fn __set__(slf: Self::Receiver, instance: Self::Inst, value: Self::Value) -> Self::Result where Self: PyDescrSetProtocol<'p>, { @@ -44,6 +47,7 @@ pub trait PyDescrProtocol<'p>: PyClass { } pub trait PyDescrGetProtocol<'p>: PyDescrProtocol<'p> { + type Receiver: crate::derive_utils::TryFromPyCell<'p, Self>; type Inst: FromPyObject<'p>; type Owner: FromPyObject<'p>; type Success: IntoPy; @@ -51,6 +55,7 @@ pub trait PyDescrGetProtocol<'p>: PyDescrProtocol<'p> { } pub trait PyDescrSetProtocol<'p>: PyDescrProtocol<'p> { + type Receiver: crate::derive_utils::TryFromPyCell<'p, Self>; type Inst: FromPyObject<'p>; type Value: FromPyObject<'p>; type Result: Into>; @@ -66,76 +71,29 @@ pub trait PyDescrSetNameProtocol<'p>: PyDescrProtocol<'p> { type Result: Into>; } -trait PyDescrGetProtocolImpl { - fn tp_descr_get() -> Option; -} -impl<'p, T> PyDescrGetProtocolImpl for T -where - T: PyDescrProtocol<'p>, -{ - default fn tp_descr_get() -> Option { - None - } +/// All FFI functions for description protocols. +#[derive(Default)] +pub struct PyDescrMethods { + pub tp_descr_get: Option, + pub tp_descr_set: Option, } -impl PyDescrGetProtocolImpl for T -where - T: for<'p> PyDescrGetProtocol<'p>, -{ - fn tp_descr_get() -> Option { - py_ternary_func!(PyDescrGetProtocol, T::__get__) - } -} - -trait PyDescrSetProtocolImpl { - fn tp_descr_set() -> Option; -} -impl<'p, T> PyDescrSetProtocolImpl for T -where - T: PyDescrProtocol<'p>, -{ - default fn tp_descr_set() -> Option { - None - } -} -impl PyDescrSetProtocolImpl for T -where - T: for<'p> PyDescrSetProtocol<'p>, -{ - fn tp_descr_set() -> Option { - py_ternary_func!(PyDescrSetProtocol, T::__set__, c_int) - } -} - -trait PyDescrDelProtocolImpl { - fn __del__() -> Option { - None +#[doc(hidden)] +impl PyDescrMethods { + pub(crate) fn update_typeobj(&self, type_object: &mut ffi::PyTypeObject) { + type_object.tp_descr_get = self.tp_descr_get; + type_object.tp_descr_set = self.tp_descr_set; } -} -impl<'p, T> PyDescrDelProtocolImpl for T where T: PyDescrProtocol<'p> {} - -trait PyDescrSetNameProtocolImpl { - fn __set_name__() -> Option { - None + pub fn set_descr_get(&mut self) + where + T: for<'p> PyDescrGetProtocol<'p>, + { + self.tp_descr_get = py_ternarys_func!(PyDescrGetProtocol, T::__get__); } -} -impl<'p, T> PyDescrSetNameProtocolImpl for T where T: PyDescrProtocol<'p> {} - -#[doc(hidden)] -pub trait PyDescrProtocolImpl { - fn tp_as_descr(_type_object: &mut ffi::PyTypeObject); -} - -impl PyDescrProtocolImpl for T { - default fn tp_as_descr(_type_object: &mut ffi::PyTypeObject) {} -} - -impl<'p, T> PyDescrProtocolImpl for T -where - T: PyDescrProtocol<'p>, -{ - fn tp_as_descr(type_object: &mut ffi::PyTypeObject) { - type_object.tp_descr_get = Self::tp_descr_get(); - type_object.tp_descr_set = Self::tp_descr_set(); + pub fn set_descr_set(&mut self) + where + T: for<'p> PyDescrSetProtocol<'p>, + { + self.tp_descr_set = py_ternarys_func!(PyDescrSetProtocol, T::__set__, c_int); } } diff --git a/src/class/gc.rs b/src/class/gc.rs index 040a6352410..f639b8dcc35 100644 --- a/src/class/gc.rs +++ b/src/class/gc.rs @@ -18,25 +18,36 @@ pub trait PyGCProtocol<'p>: PyClass { pub trait PyGCTraverseProtocol<'p>: PyGCProtocol<'p> {} pub trait PyGCClearProtocol<'p>: PyGCProtocol<'p> {} -#[doc(hidden)] -pub trait PyGCProtocolImpl { - fn update_type_object(_type_object: &mut ffi::PyTypeObject); +/// All FFI functions for gc protocols. +#[derive(Default)] +pub struct PyGCMethods { + pub tp_traverse: Option, + pub tp_clear: Option, } -impl<'p, T> PyGCProtocolImpl for T { - default fn update_type_object(_type_object: &mut ffi::PyTypeObject) {} -} +#[doc(hidden)] +impl PyGCMethods { + pub(crate) fn update_typeobj(&self, type_object: &mut ffi::PyTypeObject) { + type_object.tp_traverse = self.tp_traverse; + type_object.tp_clear = self.tp_clear; + } -impl<'p, T> PyGCProtocolImpl for T -where - T: PyGCProtocol<'p>, -{ - fn update_type_object(type_object: &mut ffi::PyTypeObject) { - type_object.tp_traverse = Self::tp_traverse(); - type_object.tp_clear = Self::tp_clear(); + pub fn set_traverse(&mut self) + where + T: for<'p> PyGCTraverseProtocol<'p>, + { + self.tp_traverse = tp_traverse::(); + } + + pub fn set_clear(&mut self) + where + T: for<'p> PyGCClearProtocol<'p>, + { + self.tp_clear = tp_clear::(); } } +/// Object visitor for GC. #[derive(Copy, Clone)] pub struct PyVisit<'p> { visit: ffi::visitproc, @@ -48,6 +59,7 @@ pub struct PyVisit<'p> { } impl<'p> PyVisit<'p> { + /// Visit `obj`. pub fn call(&self, obj: &T) -> Result<(), PyTraverseError> where T: AsPyPointer, @@ -61,88 +73,55 @@ impl<'p> PyVisit<'p> { } } -trait PyGCTraverseProtocolImpl { - fn tp_traverse() -> Option; -} - -impl<'p, T> PyGCTraverseProtocolImpl for T -where - T: PyGCProtocol<'p>, -{ - default fn tp_traverse() -> Option { - None - } -} - -#[doc(hidden)] -impl PyGCTraverseProtocolImpl for T +fn tp_traverse() -> Option where T: for<'p> PyGCTraverseProtocol<'p>, { - #[inline] - fn tp_traverse() -> Option { - unsafe extern "C" fn tp_traverse( - slf: *mut ffi::PyObject, - visit: ffi::visitproc, - arg: *mut c_void, - ) -> c_int - where - T: for<'p> PyGCTraverseProtocol<'p>, - { - let pool = crate::GILPool::new(); - let py = pool.python(); - let slf = py.from_borrowed_ptr::>(slf); - - let visit = PyVisit { - visit, - arg, - _py: py, - }; - let borrow = slf.try_borrow(); - if let Ok(borrow) = borrow { - match borrow.__traverse__(visit) { - Ok(()) => 0, - Err(PyTraverseError(code)) => code, - } - } else { - 0 + unsafe extern "C" fn tp_traverse( + slf: *mut ffi::PyObject, + visit: ffi::visitproc, + arg: *mut c_void, + ) -> c_int + where + T: for<'p> PyGCTraverseProtocol<'p>, + { + let pool = crate::GILPool::new(); + let py = pool.python(); + let slf = py.from_borrowed_ptr::>(slf); + + let visit = PyVisit { + visit, + arg, + _py: py, + }; + let borrow = slf.try_borrow(); + if let Ok(borrow) = borrow { + match borrow.__traverse__(visit) { + Ok(()) => 0, + Err(PyTraverseError(code)) => code, } + } else { + 0 } - - Some(tp_traverse::) } -} - -trait PyGCClearProtocolImpl { - fn tp_clear() -> Option; -} -impl<'p, T> PyGCClearProtocolImpl for T -where - T: PyGCProtocol<'p>, -{ - default fn tp_clear() -> Option { - None - } + Some(tp_traverse::) } -impl PyGCClearProtocolImpl for T +fn tp_clear() -> Option where T: for<'p> PyGCClearProtocol<'p>, { - #[inline] - fn tp_clear() -> Option { - unsafe extern "C" fn tp_clear(slf: *mut ffi::PyObject) -> c_int - where - T: for<'p> PyGCClearProtocol<'p>, - { - let pool = crate::GILPool::new(); - let py = pool.python(); - let slf = py.from_borrowed_ptr::>(slf); - - slf.borrow_mut().__clear__(); - 0 - } - Some(tp_clear::) + unsafe extern "C" fn tp_clear(slf: *mut ffi::PyObject) -> c_int + where + T: for<'p> PyGCClearProtocol<'p>, + { + let pool = crate::GILPool::new(); + let py = pool.python(); + let slf = py.from_borrowed_ptr::>(slf); + + slf.borrow_mut().__clear__(); + 0 } + Some(tp_clear::) } diff --git a/src/class/iter.rs b/src/class/iter.rs index 08c12963df3..04fbd1e953f 100644 --- a/src/class/iter.rs +++ b/src/class/iter.rs @@ -40,69 +40,29 @@ pub trait PyIterNextProtocol<'p>: PyIterProtocol<'p> { type Result: Into>>; } -#[doc(hidden)] -pub trait PyIterProtocolImpl { - fn tp_as_iter(_typeob: &mut ffi::PyTypeObject); -} - -impl PyIterProtocolImpl for T { - default fn tp_as_iter(_typeob: &mut ffi::PyTypeObject) {} -} - -impl<'p, T> PyIterProtocolImpl for T -where - T: PyIterProtocol<'p>, -{ - #[inline] - fn tp_as_iter(typeob: &mut ffi::PyTypeObject) { - typeob.tp_iter = Self::tp_iter(); - typeob.tp_iternext = Self::tp_iternext(); - } +#[derive(Default)] +pub struct PyIterMethods { + pub tp_iter: Option, + pub tp_iternext: Option, } -trait PyIterIterProtocolImpl { - fn tp_iter() -> Option; -} - -impl<'p, T> PyIterIterProtocolImpl for T -where - T: PyIterProtocol<'p>, -{ - default fn tp_iter() -> Option { - None - } -} - -impl PyIterIterProtocolImpl for T -where - T: for<'p> PyIterIterProtocol<'p>, -{ - #[inline] - fn tp_iter() -> Option { - py_unarys_func!(PyIterIterProtocol, T::__iter__) +#[doc(hidden)] +impl PyIterMethods { + pub(crate) fn update_typeobj(&self, type_object: &mut ffi::PyTypeObject) { + type_object.tp_iter = self.tp_iter; + type_object.tp_iternext = self.tp_iternext; } -} - -trait PyIterNextProtocolImpl { - fn tp_iternext() -> Option; -} - -impl<'p, T> PyIterNextProtocolImpl for T -where - T: PyIterProtocol<'p>, -{ - default fn tp_iternext() -> Option { - None + pub fn set_iter(&mut self) + where + T: for<'p> PyIterIterProtocol<'p>, + { + self.tp_iter = py_unarys_func!(PyIterIterProtocol, T::__iter__); } -} - -impl PyIterNextProtocolImpl for T -where - T: for<'p> PyIterNextProtocol<'p>, -{ - #[inline] - fn tp_iternext() -> Option { - py_unarys_func!(PyIterNextProtocol, T::__next__, IterNextConverter) + pub fn set_iternext(&mut self) + where + T: for<'p> PyIterNextProtocol<'p>, + { + self.tp_iternext = py_unarys_func!(PyIterNextProtocol, T::__next__, IterNextConverter); } } diff --git a/src/class/macros.rs b/src/class/macros.rs index 54d82779d98..8141aedff20 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -34,8 +34,9 @@ macro_rules! py_unarys_func { { $crate::callback_body!(py, { let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); - let borrow = ::try_from_pycell(slf) - .map_err(|e| e.into())?; + let borrow = + >::try_from_pycell(slf) + .map_err(|e| e.into())?; $class::$f(borrow).into()$(.map($conv))? }) @@ -106,7 +107,7 @@ macro_rules! py_binary_num_func { #[macro_export] #[doc(hidden)] -macro_rules! py_binary_reverse_num_func { +macro_rules! py_binary_reversed_num_func { ($trait:ident, $class:ident :: $f:ident) => {{ unsafe extern "C" fn wrap( lhs: *mut ffi::PyObject, @@ -177,7 +178,7 @@ macro_rules! py_ssizearg_func { #[macro_export] #[doc(hidden)] -macro_rules! py_ternary_func { +macro_rules! py_ternarys_func { ($trait:ident, $class:ident :: $f:ident, $return_type:ty) => {{ unsafe extern "C" fn wrap( slf: *mut $crate::ffi::PyObject, @@ -189,6 +190,9 @@ macro_rules! py_ternary_func { { $crate::callback_body!(py, { let slf = py.from_borrowed_ptr::<$crate::PyCell>(slf); + let slf = + >::try_from_pycell(slf) + .map_err(|e| e.into())?; let arg1 = py .from_borrowed_ptr::<$crate::types::PyAny>(arg1) .extract()?; @@ -196,14 +200,14 @@ macro_rules! py_ternary_func { .from_borrowed_ptr::<$crate::types::PyAny>(arg2) .extract()?; - slf.try_borrow()?.$f(arg1, arg2).into() + $class::$f(slf, arg1, arg2).into() }) } Some(wrap::) }}; ($trait:ident, $class:ident :: $f:ident) => { - py_ternary_func!($trait, $class::$f, *mut $crate::ffi::PyObject); + py_ternarys_func!($trait, $class::$f, *mut $crate::ffi::PyObject); }; } @@ -240,7 +244,7 @@ macro_rules! py_ternary_num_func { #[macro_export] #[doc(hidden)] -macro_rules! py_ternary_reverse_num_func { +macro_rules! py_ternary_reversed_num_func { ($trait:ident, $class:ident :: $f:ident) => {{ unsafe extern "C" fn wrap( arg1: *mut $crate::ffi::PyObject, diff --git a/src/class/mapping.rs b/src/class/mapping.rs index 2f5fa8e59c4..4e51ebed0bf 100644 --- a/src/class/mapping.rs +++ b/src/class/mapping.rs @@ -75,154 +75,41 @@ pub trait PyMappingReversedProtocol<'p>: PyMappingProtocol<'p> { } #[doc(hidden)] -pub trait PyMappingProtocolImpl { - fn tp_as_mapping() -> Option; -} - -impl PyMappingProtocolImpl for T { - default fn tp_as_mapping() -> Option { - None - } -} - -impl<'p, T> PyMappingProtocolImpl for T -where - T: PyMappingProtocol<'p>, -{ - #[inline] - fn tp_as_mapping() -> Option { - let f = if let Some(df) = Self::mp_del_subscript() { - Some(df) - } else { - Self::mp_ass_subscript() - }; - - Some(ffi::PyMappingMethods { - mp_length: Self::mp_length(), - mp_subscript: Self::mp_subscript(), - mp_ass_subscript: f, - }) - } -} - -trait PyMappingLenProtocolImpl { - fn mp_length() -> Option; -} - -impl<'p, T> PyMappingLenProtocolImpl for T -where - T: PyMappingProtocol<'p>, -{ - default fn mp_length() -> Option { - None - } -} - -impl PyMappingLenProtocolImpl for T -where - T: for<'p> PyMappingLenProtocol<'p>, -{ - #[inline] - fn mp_length() -> Option { - py_len_func!(PyMappingLenProtocol, T::__len__) - } -} - -trait PyMappingGetItemProtocolImpl { - fn mp_subscript() -> Option; -} - -impl<'p, T> PyMappingGetItemProtocolImpl for T -where - T: PyMappingProtocol<'p>, -{ - default fn mp_subscript() -> Option { - None - } -} - -impl PyMappingGetItemProtocolImpl for T -where - T: for<'p> PyMappingGetItemProtocol<'p>, -{ - #[inline] - fn mp_subscript() -> Option { - py_binary_func!(PyMappingGetItemProtocol, T::__getitem__) - } -} - -trait PyMappingSetItemProtocolImpl { - fn mp_ass_subscript() -> Option; -} - -impl<'p, T> PyMappingSetItemProtocolImpl for T -where - T: PyMappingProtocol<'p>, -{ - default fn mp_ass_subscript() -> Option { - None +impl ffi::PyMappingMethods { + pub fn set_length(&mut self) + where + T: for<'p> PyMappingLenProtocol<'p>, + { + self.mp_length = py_len_func!(PyMappingLenProtocol, T::__len__); } -} - -impl PyMappingSetItemProtocolImpl for T -where - T: for<'p> PyMappingSetItemProtocol<'p>, -{ - #[inline] - fn mp_ass_subscript() -> Option { - py_func_set!(PyMappingSetItemProtocol, T, __setitem__) + pub fn set_getitem(&mut self) + where + T: for<'p> PyMappingGetItemProtocol<'p>, + { + self.mp_subscript = py_binary_func!(PyMappingGetItemProtocol, T::__getitem__); } -} - -/// Returns `None` if PyMappingDelItemProtocol isn't implemented, otherwise dispatches to -/// `DelSetItemDispatch` -trait DeplItemDipatch { - fn mp_del_subscript() -> Option; -} - -impl<'p, T> DeplItemDipatch for T -where - T: PyMappingProtocol<'p>, -{ - default fn mp_del_subscript() -> Option { - None + pub fn set_setitem(&mut self) + where + T: for<'p> PyMappingSetItemProtocol<'p>, + { + self.mp_ass_subscript = py_func_set!(PyMappingSetItemProtocol, T, __setitem__); } -} - -/// Returns `py_func_set_del` if PyMappingSetItemProtocol is implemented, otherwise `py_func_del` -trait DelSetItemDispatch: Sized + for<'p> PyMappingDelItemProtocol<'p> { - fn det_set_dispatch() -> Option; -} - -impl DelSetItemDispatch for T -where - T: Sized + for<'p> PyMappingDelItemProtocol<'p>, -{ - default fn det_set_dispatch() -> Option { - py_func_del!(PyMappingDelItemProtocol, Self, __delitem__) + pub fn set_delitem(&mut self) + where + T: for<'p> PyMappingDelItemProtocol<'p>, + { + self.mp_ass_subscript = py_func_del!(PyMappingDelItemProtocol, T, __delitem__); } -} - -impl DelSetItemDispatch for T -where - T: for<'p> PyMappingSetItemProtocol<'p> + for<'p> PyMappingDelItemProtocol<'p>, -{ - fn det_set_dispatch() -> Option { - py_func_set_del!( + pub fn set_setdelitem(&mut self) + where + T: for<'p> PyMappingSetItemProtocol<'p> + for<'p> PyMappingDelItemProtocol<'p>, + { + self.mp_ass_subscript = py_func_set_del!( PyMappingSetItemProtocol, PyMappingDelItemProtocol, T, __setitem__, __delitem__ - ) - } -} - -impl DeplItemDipatch for T -where - T: Sized + for<'p> PyMappingDelItemProtocol<'p>, -{ - fn mp_del_subscript() -> Option { - ::det_set_dispatch() + ); } } diff --git a/src/class/mod.rs b/src/class/mod.rs index df828ecb75c..fd37a51fd17 100644 --- a/src/class/mod.rs +++ b/src/class/mod.rs @@ -14,6 +14,7 @@ pub mod iter; pub mod mapping; pub mod methods; pub mod number; +pub mod proto_methods; pub mod pyasync; pub mod sequence; diff --git a/src/class/number.rs b/src/class/number.rs index ab5270b4ddb..7e6e6a107a1 100644 --- a/src/class/number.rs +++ b/src/class/number.rs @@ -3,7 +3,6 @@ //! Python Number Interface //! Trait and support implementation for implementing number protocol -use crate::class::basic::PyObjectProtocolImpl; use crate::err::PyResult; use crate::{ffi, FromPyObject, IntoPy, PyClass, PyObject}; @@ -617,1124 +616,298 @@ pub trait PyNumberIndexProtocol<'p>: PyNumberProtocol<'p> { } #[doc(hidden)] -pub trait PyNumberProtocolImpl: PyObjectProtocolImpl { - fn tp_as_number() -> Option; -} - -impl<'p, T> PyNumberProtocolImpl for T { - default fn tp_as_number() -> Option { - if let Some(nb_bool) = ::nb_bool_fn() { - let meth = ffi::PyNumberMethods { - nb_bool: Some(nb_bool), - ..ffi::PyNumberMethods_INIT - }; - Some(meth) - } else { - None - } - } -} - -impl<'p, T> PyNumberProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - fn tp_as_number() -> Option { - Some(ffi::PyNumberMethods { - nb_add: Self::nb_add().or_else(Self::nb_add_fallback), - nb_subtract: Self::nb_subtract().or_else(Self::nb_sub_fallback), - nb_multiply: Self::nb_multiply().or_else(Self::nb_mul_fallback), - nb_remainder: Self::nb_remainder(), - nb_divmod: Self::nb_divmod().or_else(Self::nb_divmod_fallback), - nb_power: Self::nb_power().or_else(Self::nb_pow_fallback), - nb_negative: Self::nb_negative(), - nb_positive: Self::nb_positive(), - nb_absolute: Self::nb_absolute(), - nb_bool: ::nb_bool_fn(), - nb_invert: Self::nb_invert(), - nb_lshift: Self::nb_lshift().or_else(Self::nb_lshift_fallback), - nb_rshift: Self::nb_rshift().or_else(Self::nb_rshift_fallback), - nb_and: Self::nb_and().or_else(Self::nb_and_fallback), - nb_xor: Self::nb_xor().or_else(Self::nb_xor_fallback), - nb_or: Self::nb_or().or_else(Self::nb_or_fallback), - nb_int: Self::nb_int(), - nb_reserved: ::std::ptr::null_mut(), - nb_float: Self::nb_float(), - nb_inplace_add: Self::nb_inplace_add(), - nb_inplace_subtract: Self::nb_inplace_subtract(), - nb_inplace_multiply: Self::nb_inplace_multiply(), - nb_inplace_remainder: Self::nb_inplace_remainder(), - nb_inplace_power: Self::nb_inplace_power(), - nb_inplace_lshift: Self::nb_inplace_lshift(), - nb_inplace_rshift: Self::nb_inplace_rshift(), - nb_inplace_and: Self::nb_inplace_and(), - nb_inplace_xor: Self::nb_inplace_xor(), - nb_inplace_or: Self::nb_inplace_or(), - nb_floor_divide: Self::nb_floor_divide().or_else(Self::nb_floordiv_fallback), - nb_true_divide: Self::nb_true_divide().or_else(Self::nb_truediv_fallback), - nb_inplace_floor_divide: Self::nb_inplace_floor_divide(), - nb_inplace_true_divide: Self::nb_inplace_true_divide(), - nb_index: Self::nb_index(), - nb_matrix_multiply: Self::nb_matrix_multiply().or_else(Self::nb_matmul_fallback), - nb_inplace_matrix_multiply: Self::nb_inplace_matrix_multiply(), - }) - } -} - -trait PyNumberAddProtocolImpl { - fn nb_add() -> Option; -} - -impl<'p, T> PyNumberAddProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_add() -> Option { - None - } -} - -impl PyNumberAddProtocolImpl for T -where - T: for<'p> PyNumberAddProtocol<'p>, -{ - fn nb_add() -> Option { - py_binary_num_func!(PyNumberAddProtocol, T::__add__) - } -} - -trait PyNumberSubProtocolImpl { - fn nb_subtract() -> Option; -} - -impl<'p, T> PyNumberSubProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_subtract() -> Option { - None - } -} - -impl PyNumberSubProtocolImpl for T -where - T: for<'p> PyNumberSubProtocol<'p>, -{ - fn nb_subtract() -> Option { - py_binary_num_func!(PyNumberSubProtocol, T::__sub__) - } -} - -trait PyNumberMulProtocolImpl { - fn nb_multiply() -> Option; -} - -impl<'p, T> PyNumberMulProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_multiply() -> Option { - None - } -} - -impl PyNumberMulProtocolImpl for T -where - T: for<'p> PyNumberMulProtocol<'p>, -{ - fn nb_multiply() -> Option { - py_binary_num_func!(PyNumberMulProtocol, T::__mul__) - } -} - -trait PyNumberMatmulProtocolImpl { - fn nb_matrix_multiply() -> Option; -} - -impl<'p, T> PyNumberMatmulProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_matrix_multiply() -> Option { - None - } -} - -impl PyNumberMatmulProtocolImpl for T -where - T: for<'p> PyNumberMatmulProtocol<'p>, -{ - fn nb_matrix_multiply() -> Option { - py_binary_num_func!(PyNumberMatmulProtocol, T::__matmul__) - } -} - -trait PyNumberTruedivProtocolImpl { - fn nb_true_divide() -> Option; -} - -impl<'p, T> PyNumberTruedivProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_true_divide() -> Option { - None - } -} - -impl PyNumberTruedivProtocolImpl for T -where - T: for<'p> PyNumberTruedivProtocol<'p>, -{ - fn nb_true_divide() -> Option { - py_binary_num_func!(PyNumberTruedivProtocol, T::__truediv__) - } -} - -trait PyNumberFloordivProtocolImpl { - fn nb_floor_divide() -> Option; -} - -impl<'p, T> PyNumberFloordivProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_floor_divide() -> Option { - None - } -} - -impl PyNumberFloordivProtocolImpl for T -where - T: for<'p> PyNumberFloordivProtocol<'p>, -{ - fn nb_floor_divide() -> Option { - py_binary_num_func!(PyNumberFloordivProtocol, T::__floordiv__) - } -} - -trait PyNumberModProtocolImpl { - fn nb_remainder() -> Option; -} - -impl<'p, T> PyNumberModProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_remainder() -> Option { - None - } -} - -impl PyNumberModProtocolImpl for T -where - T: for<'p> PyNumberModProtocol<'p>, -{ - fn nb_remainder() -> Option { - py_binary_num_func!(PyNumberModProtocol, T::__mod__) - } -} - -trait PyNumberDivmodProtocolImpl { - fn nb_divmod() -> Option; -} - -impl<'p, T> PyNumberDivmodProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_divmod() -> Option { - None - } -} - -impl PyNumberDivmodProtocolImpl for T -where - T: for<'p> PyNumberDivmodProtocol<'p>, -{ - fn nb_divmod() -> Option { - py_binary_num_func!(PyNumberDivmodProtocol, T::__divmod__) - } -} - -trait PyNumberPowProtocolImpl { - fn nb_power() -> Option; -} - -impl<'p, T> PyNumberPowProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_power() -> Option { - None - } -} - -impl PyNumberPowProtocolImpl for T -where - T: for<'p> PyNumberPowProtocol<'p>, -{ - fn nb_power() -> Option { - py_ternary_num_func!(PyNumberPowProtocol, T::__pow__) - } -} - -trait PyNumberLShiftProtocolImpl { - fn nb_lshift() -> Option; -} - -impl<'p, T> PyNumberLShiftProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_lshift() -> Option { - None - } -} - -impl PyNumberLShiftProtocolImpl for T -where - T: for<'p> PyNumberLShiftProtocol<'p>, -{ - fn nb_lshift() -> Option { - py_binary_num_func!(PyNumberLShiftProtocol, T::__lshift__) - } -} - -trait PyNumberRShiftProtocolImpl { - fn nb_rshift() -> Option; -} - -impl<'p, T> PyNumberRShiftProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_rshift() -> Option { - None - } -} - -impl PyNumberRShiftProtocolImpl for T -where - T: for<'p> PyNumberRShiftProtocol<'p>, -{ - fn nb_rshift() -> Option { - py_binary_num_func!(PyNumberRShiftProtocol, T::__rshift__) - } -} - -trait PyNumberAndProtocolImpl { - fn nb_and() -> Option; -} - -impl<'p, T> PyNumberAndProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_and() -> Option { - None - } -} - -impl PyNumberAndProtocolImpl for T -where - T: for<'p> PyNumberAndProtocol<'p>, -{ - fn nb_and() -> Option { - py_binary_num_func!(PyNumberAndProtocol, T::__and__) - } -} - -trait PyNumberXorProtocolImpl { - fn nb_xor() -> Option; -} - -impl<'p, T> PyNumberXorProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_xor() -> Option { - None - } -} - -impl PyNumberXorProtocolImpl for T -where - T: for<'p> PyNumberXorProtocol<'p>, -{ - fn nb_xor() -> Option { - py_binary_num_func!(PyNumberXorProtocol, T::__xor__) +impl ffi::PyNumberMethods { + pub(crate) fn from_nb_bool(nb_bool: ffi::inquiry) -> *mut Self { + let mut nm = ffi::PyNumberMethods_INIT; + nm.nb_bool = Some(nb_bool); + Box::into_raw(Box::new(nm)) } -} - -trait PyNumberOrProtocolImpl { - fn nb_or() -> Option; -} - -impl<'p, T> PyNumberOrProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_or() -> Option { - None - } -} - -impl PyNumberOrProtocolImpl for T -where - T: for<'p> PyNumberOrProtocol<'p>, -{ - fn nb_or() -> Option { - py_binary_num_func!(PyNumberOrProtocol, T::__or__) - } -} - -trait PyNumberIAddProtocolImpl { - fn nb_inplace_add() -> Option; -} - -impl<'p, T> PyNumberIAddProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_inplace_add() -> Option { - None - } -} - -impl PyNumberIAddProtocolImpl for T -where - T: for<'p> PyNumberIAddProtocol<'p>, -{ - fn nb_inplace_add() -> Option { - py_binary_self_func!(PyNumberIAddProtocol, T::__iadd__) - } -} - -trait PyNumberISubProtocolImpl { - fn nb_inplace_subtract() -> Option; -} - -impl<'p, T> PyNumberISubProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_inplace_subtract() -> Option { - None - } -} - -impl PyNumberISubProtocolImpl for T -where - T: for<'p> PyNumberISubProtocol<'p>, -{ - fn nb_inplace_subtract() -> Option { - py_binary_self_func!(PyNumberISubProtocol, T::__isub__) - } -} - -trait PyNumberIMulProtocolImpl { - fn nb_inplace_multiply() -> Option; -} - -impl<'p, T> PyNumberIMulProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_inplace_multiply() -> Option { - None - } -} - -impl PyNumberIMulProtocolImpl for T -where - T: for<'p> PyNumberIMulProtocol<'p>, -{ - fn nb_inplace_multiply() -> Option { - py_binary_self_func!(PyNumberIMulProtocol, T::__imul__) - } -} - -trait PyNumberIMatmulProtocolImpl { - fn nb_inplace_matrix_multiply() -> Option; -} - -impl<'p, T> PyNumberIMatmulProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_inplace_matrix_multiply() -> Option { - None - } -} - -impl PyNumberIMatmulProtocolImpl for T -where - T: for<'p> PyNumberIMatmulProtocol<'p>, -{ - fn nb_inplace_matrix_multiply() -> Option { - py_binary_self_func!(PyNumberIMatmulProtocol, T::__imatmul__) - } -} - -trait PyNumberITruedivProtocolImpl { - fn nb_inplace_true_divide() -> Option; -} - -impl<'p, T> PyNumberITruedivProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_inplace_true_divide() -> Option { - None - } -} - -impl PyNumberITruedivProtocolImpl for T -where - T: for<'p> PyNumberITruedivProtocol<'p>, -{ - fn nb_inplace_true_divide() -> Option { - py_binary_self_func!(PyNumberITruedivProtocol, T::__itruediv__) - } -} - -trait PyNumberIFloordivProtocolImpl { - fn nb_inplace_floor_divide() -> Option; -} - -impl<'p, T> PyNumberIFloordivProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_inplace_floor_divide() -> Option { - None - } -} - -impl PyNumberIFloordivProtocolImpl for T -where - T: for<'p> PyNumberIFloordivProtocol<'p>, -{ - fn nb_inplace_floor_divide() -> Option { - py_binary_self_func!(PyNumberIFloordivProtocol, T::__ifloordiv__) - } -} - -trait PyNumberIModProtocolImpl { - fn nb_inplace_remainder() -> Option; -} - -impl<'p, T> PyNumberIModProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_inplace_remainder() -> Option { - None - } -} - -impl PyNumberIModProtocolImpl for T -where - T: for<'p> PyNumberIModProtocol<'p>, -{ - fn nb_inplace_remainder() -> Option { - py_binary_self_func!(PyNumberIModProtocol, T::__imod__) - } -} - -trait PyNumberIPowProtocolImpl { - fn nb_inplace_power() -> Option; -} - -impl<'p, T> PyNumberIPowProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_inplace_power() -> Option { - None - } -} - -impl PyNumberIPowProtocolImpl for T -where - T: for<'p> PyNumberIPowProtocol<'p>, -{ - fn nb_inplace_power() -> Option { - py_dummy_ternary_self_func!(PyNumberIPowProtocol, T::__ipow__) - } -} - -trait PyNumberILShiftProtocolImpl { - fn nb_inplace_lshift() -> Option; -} - -impl<'p, T> PyNumberILShiftProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_inplace_lshift() -> Option { - None - } -} - -impl PyNumberILShiftProtocolImpl for T -where - T: for<'p> PyNumberILShiftProtocol<'p>, -{ - fn nb_inplace_lshift() -> Option { - py_binary_self_func!(PyNumberILShiftProtocol, T::__ilshift__) - } -} - -trait PyNumberIRShiftProtocolImpl { - fn nb_inplace_rshift() -> Option; -} - -impl<'p, T> PyNumberIRShiftProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_inplace_rshift() -> Option { - None - } -} - -impl PyNumberIRShiftProtocolImpl for T -where - T: for<'p> PyNumberIRShiftProtocol<'p>, -{ - fn nb_inplace_rshift() -> Option { - py_binary_self_func!(PyNumberIRShiftProtocol, T::__irshift__) - } -} - -trait PyNumberIAndProtocolImpl { - fn nb_inplace_and() -> Option; -} - -impl<'p, T> PyNumberIAndProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_inplace_and() -> Option { - None - } -} - -impl PyNumberIAndProtocolImpl for T -where - T: for<'p> PyNumberIAndProtocol<'p>, -{ - fn nb_inplace_and() -> Option { - py_binary_self_func!(PyNumberIAndProtocol, T::__iand__) + pub fn set_add(&mut self) + where + T: for<'p> PyNumberAddProtocol<'p>, + { + self.nb_add = py_binary_num_func!(PyNumberAddProtocol, T::__add__); } -} - -trait PyNumberIXorProtocolImpl { - fn nb_inplace_xor() -> Option; -} - -impl<'p, T> PyNumberIXorProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_inplace_xor() -> Option { - None + pub fn set_radd(&mut self) + where + T: for<'p> PyNumberRAddProtocol<'p>, + { + self.nb_add = py_binary_reversed_num_func!(PyNumberRAddProtocol, T::__radd__); } -} - -impl PyNumberIXorProtocolImpl for T -where - T: for<'p> PyNumberIXorProtocol<'p>, -{ - fn nb_inplace_xor() -> Option { - py_binary_self_func!(PyNumberIXorProtocol, T::__ixor__) + pub fn set_sub(&mut self) + where + T: for<'p> PyNumberSubProtocol<'p>, + { + self.nb_subtract = py_binary_num_func!(PyNumberSubProtocol, T::__sub__); } -} - -trait PyNumberIOrProtocolImpl { - fn nb_inplace_or() -> Option; -} - -impl<'p, T> PyNumberIOrProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_inplace_or() -> Option { - None + pub fn set_rsub(&mut self) + where + T: for<'p> PyNumberRSubProtocol<'p>, + { + self.nb_subtract = py_binary_reversed_num_func!(PyNumberRSubProtocol, T::__rsub__); } -} - -impl PyNumberIOrProtocolImpl for T -where - T: for<'p> PyNumberIOrProtocol<'p>, -{ - fn nb_inplace_or() -> Option { - py_binary_self_func!(PyNumberIOrProtocol, T::__ior__) + pub fn set_mul(&mut self) + where + T: for<'p> PyNumberMulProtocol<'p>, + { + self.nb_multiply = py_binary_num_func!(PyNumberMulProtocol, T::__mul__); } -} - -// Fallback trait for nb_add -trait PyNumberAddFallback { - fn nb_add_fallback() -> Option; -} - -impl<'p, T> PyNumberAddFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_add_fallback() -> Option { - None + pub fn set_rmul(&mut self) + where + T: for<'p> PyNumberRMulProtocol<'p>, + { + self.nb_multiply = py_binary_reversed_num_func!(PyNumberRMulProtocol, T::__rmul__); } -} - -impl PyNumberAddFallback for T -where - T: for<'p> PyNumberRAddProtocol<'p>, -{ - fn nb_add_fallback() -> Option { - py_binary_reverse_num_func!(PyNumberRAddProtocol, T::__radd__) + pub fn set_mod(&mut self) + where + T: for<'p> PyNumberModProtocol<'p>, + { + self.nb_remainder = py_binary_num_func!(PyNumberModProtocol, T::__mod__); } -} - -trait PyNumberSubFallback { - fn nb_sub_fallback() -> Option; -} - -impl<'p, T> PyNumberSubFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_sub_fallback() -> Option { - None + pub fn set_divmod(&mut self) + where + T: for<'p> PyNumberDivmodProtocol<'p>, + { + self.nb_divmod = py_binary_num_func!(PyNumberDivmodProtocol, T::__divmod__); } -} - -impl PyNumberSubFallback for T -where - T: for<'p> PyNumberRSubProtocol<'p>, -{ - fn nb_sub_fallback() -> Option { - py_binary_reverse_num_func!(PyNumberRSubProtocol, T::__rsub__) + pub fn set_rdivmod(&mut self) + where + T: for<'p> PyNumberRDivmodProtocol<'p>, + { + self.nb_divmod = py_binary_reversed_num_func!(PyNumberRDivmodProtocol, T::__rdivmod__); } -} - -trait PyNumberMulFallback { - fn nb_mul_fallback() -> Option; -} - -impl<'p, T> PyNumberMulFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_mul_fallback() -> Option { - None + pub fn set_pow(&mut self) + where + T: for<'p> PyNumberPowProtocol<'p>, + { + self.nb_power = py_ternary_num_func!(PyNumberPowProtocol, T::__pow__); } -} - -impl PyNumberMulFallback for T -where - T: for<'p> PyNumberRMulProtocol<'p>, -{ - fn nb_mul_fallback() -> Option { - py_binary_reverse_num_func!(PyNumberRMulProtocol, T::__rmul__) + pub fn set_rpow(&mut self) + where + T: for<'p> PyNumberRPowProtocol<'p>, + { + self.nb_power = py_ternary_reversed_num_func!(PyNumberRPowProtocol, T::__rpow__); } -} - -trait PyNumberMatmulFallback { - fn nb_matmul_fallback() -> Option; -} - -impl<'p, T> PyNumberMatmulFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_matmul_fallback() -> Option { - None + pub fn set_neg(&mut self) + where + T: for<'p> PyNumberNegProtocol<'p>, + { + self.nb_negative = py_unary_func!(PyNumberNegProtocol, T::__neg__); } -} - -impl PyNumberMatmulFallback for T -where - T: for<'p> PyNumberRMatmulProtocol<'p>, -{ - fn nb_matmul_fallback() -> Option { - py_binary_reverse_num_func!(PyNumberRMatmulProtocol, T::__rmatmul__) + pub fn set_pos(&mut self) + where + T: for<'p> PyNumberPosProtocol<'p>, + { + self.nb_positive = py_unary_func!(PyNumberPosProtocol, T::__pos__); } -} - -trait PyNumberTruedivFallback { - fn nb_truediv_fallback() -> Option; -} - -impl<'p, T> PyNumberTruedivFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_truediv_fallback() -> Option { - None + pub fn set_abs(&mut self) + where + T: for<'p> PyNumberAbsProtocol<'p>, + { + self.nb_absolute = py_unary_func!(PyNumberAbsProtocol, T::__abs__); } -} - -impl PyNumberTruedivFallback for T -where - T: for<'p> PyNumberRTruedivProtocol<'p>, -{ - fn nb_truediv_fallback() -> Option { - py_binary_reverse_num_func!(PyNumberRTruedivProtocol, T::__rtruediv__) + pub fn set_invert(&mut self) + where + T: for<'p> PyNumberInvertProtocol<'p>, + { + self.nb_invert = py_unary_func!(PyNumberInvertProtocol, T::__invert__); } -} - -trait PyNumberFloordivFallback { - fn nb_floordiv_fallback() -> Option; -} - -impl<'p, T> PyNumberFloordivFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_floordiv_fallback() -> Option { - None + pub fn set_lshift(&mut self) + where + T: for<'p> PyNumberLShiftProtocol<'p>, + { + self.nb_lshift = py_binary_num_func!(PyNumberLShiftProtocol, T::__lshift__); } -} - -impl PyNumberFloordivFallback for T -where - T: for<'p> PyNumberRFloordivProtocol<'p>, -{ - fn nb_floordiv_fallback() -> Option { - py_binary_reverse_num_func!(PyNumberRFloordivProtocol, T::__rfloordiv__) + pub fn set_rlshift(&mut self) + where + T: for<'p> PyNumberRLShiftProtocol<'p>, + { + self.nb_lshift = py_binary_reversed_num_func!(PyNumberRLShiftProtocol, T::__rlshift__); } -} - -trait PyNumberModFallback { - fn nb_mod_fallback() -> Option; -} - -impl<'p, T> PyNumberModFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_mod_fallback() -> Option { - None + pub fn set_rshift(&mut self) + where + T: for<'p> PyNumberRShiftProtocol<'p>, + { + self.nb_rshift = py_binary_num_func!(PyNumberRShiftProtocol, T::__rshift__); } -} - -impl PyNumberModFallback for T -where - T: for<'p> PyNumberRModProtocol<'p>, -{ - fn nb_mod_fallback() -> Option { - py_binary_reverse_num_func!(PyNumberRModProtocol, T::__rmod__) + pub fn set_rrshift(&mut self) + where + T: for<'p> PyNumberRRShiftProtocol<'p>, + { + self.nb_rshift = py_binary_reversed_num_func!(PyNumberRRShiftProtocol, T::__rrshift__); } -} - -trait PyNumberDivmodFallback { - fn nb_divmod_fallback() -> Option; -} - -impl<'p, T> PyNumberDivmodFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_divmod_fallback() -> Option { - None + pub fn set_and(&mut self) + where + T: for<'p> PyNumberAndProtocol<'p>, + { + self.nb_and = py_binary_num_func!(PyNumberAndProtocol, T::__and__); } -} - -impl PyNumberDivmodFallback for T -where - T: for<'p> PyNumberRDivmodProtocol<'p>, -{ - fn nb_divmod_fallback() -> Option { - py_binary_reverse_num_func!(PyNumberRDivmodProtocol, T::__rdivmod__) + pub fn set_rand(&mut self) + where + T: for<'p> PyNumberRAndProtocol<'p>, + { + self.nb_and = py_binary_reversed_num_func!(PyNumberRAndProtocol, T::__rand__); } -} - -trait PyNumberPowFallback { - fn nb_pow_fallback() -> Option; -} - -impl<'p, T> PyNumberPowFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_pow_fallback() -> Option { - None + pub fn set_xor(&mut self) + where + T: for<'p> PyNumberXorProtocol<'p>, + { + self.nb_xor = py_binary_num_func!(PyNumberXorProtocol, T::__xor__); } -} - -impl PyNumberPowFallback for T -where - T: for<'p> PyNumberRPowProtocol<'p>, -{ - fn nb_pow_fallback() -> Option { - py_ternary_reverse_num_func!(PyNumberRPowProtocol, T::__rpow__) + pub fn set_rxor(&mut self) + where + T: for<'p> PyNumberRXorProtocol<'p>, + { + self.nb_xor = py_binary_reversed_num_func!(PyNumberRXorProtocol, T::__rxor__); } -} - -trait PyNumberLShiftFallback { - fn nb_lshift_fallback() -> Option; -} - -impl<'p, T> PyNumberLShiftFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_lshift_fallback() -> Option { - None + pub fn set_or(&mut self) + where + T: for<'p> PyNumberOrProtocol<'p>, + { + self.nb_or = py_binary_num_func!(PyNumberOrProtocol, T::__or__); } -} - -impl PyNumberLShiftFallback for T -where - T: for<'p> PyNumberRLShiftProtocol<'p>, -{ - fn nb_lshift_fallback() -> Option { - py_binary_reverse_num_func!(PyNumberRLShiftProtocol, T::__rlshift__) + pub fn set_ror(&mut self) + where + T: for<'p> PyNumberROrProtocol<'p>, + { + self.nb_or = py_binary_reversed_num_func!(PyNumberROrProtocol, T::__ror__); } -} - -trait PyNumberRRshiftFallback { - fn nb_rshift_fallback() -> Option; -} - -impl<'p, T> PyNumberRRshiftFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_rshift_fallback() -> Option { - None + pub fn set_int(&mut self) + where + T: for<'p> PyNumberIntProtocol<'p>, + { + self.nb_int = py_unary_func!(PyNumberIntProtocol, T::__int__); } -} - -impl PyNumberRRshiftFallback for T -where - T: for<'p> PyNumberRRShiftProtocol<'p>, -{ - fn nb_rshift_fallback() -> Option { - py_binary_reverse_num_func!(PyNumberRRShiftProtocol, T::__rrshift__) + pub fn set_float(&mut self) + where + T: for<'p> PyNumberFloatProtocol<'p>, + { + self.nb_float = py_unary_func!(PyNumberFloatProtocol, T::__float__); } -} - -trait PyNumberAndFallback { - fn nb_and_fallback() -> Option; -} - -impl<'p, T> PyNumberAndFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_and_fallback() -> Option { - None + pub fn set_iadd(&mut self) + where + T: for<'p> PyNumberIAddProtocol<'p>, + { + self.nb_inplace_add = py_binary_self_func!(PyNumberIAddProtocol, T::__iadd__); } -} - -impl PyNumberAndFallback for T -where - T: for<'p> PyNumberRAndProtocol<'p>, -{ - fn nb_and_fallback() -> Option { - py_binary_reverse_num_func!(PyNumberRAndProtocol, T::__rand__) + pub fn set_isub(&mut self) + where + T: for<'p> PyNumberISubProtocol<'p>, + { + self.nb_inplace_subtract = py_binary_self_func!(PyNumberISubProtocol, T::__isub__); } -} - -trait PyNumberXorFallback { - fn nb_xor_fallback() -> Option; -} - -impl<'p, T> PyNumberXorFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_xor_fallback() -> Option { - None + pub fn set_imul(&mut self) + where + T: for<'p> PyNumberIMulProtocol<'p>, + { + self.nb_inplace_multiply = py_binary_self_func!(PyNumberIMulProtocol, T::__imul__); } -} - -impl PyNumberXorFallback for T -where - T: for<'p> PyNumberRXorProtocol<'p>, -{ - fn nb_xor_fallback() -> Option { - py_binary_reverse_num_func!(PyNumberRXorProtocol, T::__rxor__) + pub fn set_imod(&mut self) + where + T: for<'p> PyNumberIModProtocol<'p>, + { + self.nb_inplace_remainder = py_binary_self_func!(PyNumberIModProtocol, T::__imod__); } -} - -trait PyNumberOrFallback { - fn nb_or_fallback() -> Option; -} - -impl<'p, T> PyNumberOrFallback for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_or_fallback() -> Option { - None + pub fn set_ipow(&mut self) + where + T: for<'p> PyNumberIPowProtocol<'p>, + { + self.nb_inplace_power = py_dummy_ternary_self_func!(PyNumberIPowProtocol, T::__ipow__) } -} - -impl PyNumberOrFallback for T -where - T: for<'p> PyNumberROrProtocol<'p>, -{ - fn nb_or_fallback() -> Option { - py_binary_reverse_num_func!(PyNumberROrProtocol, T::__ror__) + pub fn set_ilshift(&mut self) + where + T: for<'p> PyNumberILShiftProtocol<'p>, + { + self.nb_inplace_lshift = py_binary_self_func!(PyNumberILShiftProtocol, T::__ilshift__); } -} - -trait PyNumberNegProtocolImpl { - fn nb_negative() -> Option; -} - -impl<'p, T> PyNumberNegProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_negative() -> Option { - None + pub fn set_irshift(&mut self) + where + T: for<'p> PyNumberIRShiftProtocol<'p>, + { + self.nb_inplace_rshift = py_binary_self_func!(PyNumberIRShiftProtocol, T::__irshift__); } -} - -impl PyNumberNegProtocolImpl for T -where - T: for<'p> PyNumberNegProtocol<'p>, -{ - #[inline] - fn nb_negative() -> Option { - py_unary_func!(PyNumberNegProtocol, T::__neg__) + pub fn set_iand(&mut self) + where + T: for<'p> PyNumberIAndProtocol<'p>, + { + self.nb_inplace_and = py_binary_self_func!(PyNumberIAndProtocol, T::__iand__); } -} - -trait PyNumberPosProtocolImpl { - fn nb_positive() -> Option; -} - -impl<'p, T> PyNumberPosProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_positive() -> Option { - None + pub fn set_ixor(&mut self) + where + T: for<'p> PyNumberIXorProtocol<'p>, + { + self.nb_inplace_xor = py_binary_self_func!(PyNumberIXorProtocol, T::__ixor__); } -} - -impl PyNumberPosProtocolImpl for T -where - T: for<'p> PyNumberPosProtocol<'p>, -{ - fn nb_positive() -> Option { - py_unary_func!(PyNumberPosProtocol, T::__pos__) + pub fn set_ior(&mut self) + where + T: for<'p> PyNumberIOrProtocol<'p>, + { + self.nb_inplace_or = py_binary_self_func!(PyNumberIOrProtocol, T::__ior__); } -} - -trait PyNumberAbsProtocolImpl { - fn nb_absolute() -> Option; -} - -impl<'p, T> PyNumberAbsProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_absolute() -> Option { - None + pub fn set_floordiv(&mut self) + where + T: for<'p> PyNumberFloordivProtocol<'p>, + { + self.nb_floor_divide = py_binary_num_func!(PyNumberFloordivProtocol, T::__floordiv__); } -} - -impl PyNumberAbsProtocolImpl for T -where - T: for<'p> PyNumberAbsProtocol<'p>, -{ - fn nb_absolute() -> Option { - py_unary_func!(PyNumberAbsProtocol, T::__abs__) + pub fn set_rfloordiv(&mut self) + where + T: for<'p> PyNumberRFloordivProtocol<'p>, + { + self.nb_floor_divide = + py_binary_reversed_num_func!(PyNumberRFloordivProtocol, T::__rfloordiv__); } -} - -trait PyNumberInvertProtocolImpl { - fn nb_invert() -> Option; -} - -impl<'p, T> PyNumberInvertProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_invert() -> Option { - None + pub fn set_truediv(&mut self) + where + T: for<'p> PyNumberTruedivProtocol<'p>, + { + self.nb_true_divide = py_binary_num_func!(PyNumberTruedivProtocol, T::__truediv__); } -} - -impl PyNumberInvertProtocolImpl for T -where - T: for<'p> PyNumberInvertProtocol<'p>, -{ - fn nb_invert() -> Option { - py_unary_func!(PyNumberInvertProtocol, T::__invert__) + pub fn set_rtruediv(&mut self) + where + T: for<'p> PyNumberRTruedivProtocol<'p>, + { + self.nb_true_divide = + py_binary_reversed_num_func!(PyNumberRTruedivProtocol, T::__rtruediv__); } -} - -trait PyNumberIntProtocolImpl { - fn nb_int() -> Option; -} - -impl<'p, T> PyNumberIntProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_int() -> Option { - None + pub fn set_ifloordiv(&mut self) + where + T: for<'p> PyNumberIFloordivProtocol<'p>, + { + self.nb_inplace_floor_divide = + py_binary_self_func!(PyNumberIFloordivProtocol, T::__ifloordiv__); } -} - -impl PyNumberIntProtocolImpl for T -where - T: for<'p> PyNumberIntProtocol<'p>, -{ - fn nb_int() -> Option { - py_unary_func!(PyNumberIntProtocol, T::__int__) + pub fn set_itruediv(&mut self) + where + T: for<'p> PyNumberITruedivProtocol<'p>, + { + self.nb_inplace_true_divide = + py_binary_self_func!(PyNumberITruedivProtocol, T::__itruediv__); } -} - -trait PyNumberFloatProtocolImpl { - fn nb_float() -> Option; -} - -impl<'p, T> PyNumberFloatProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_float() -> Option { - None + pub fn set_index(&mut self) + where + T: for<'p> PyNumberIndexProtocol<'p>, + { + self.nb_index = py_unary_func!(PyNumberIndexProtocol, T::__index__); } -} - -impl PyNumberFloatProtocolImpl for T -where - T: for<'p> PyNumberFloatProtocol<'p>, -{ - fn nb_float() -> Option { - py_unary_func!(PyNumberFloatProtocol, T::__float__) + pub fn set_matmul(&mut self) + where + T: for<'p> PyNumberMatmulProtocol<'p>, + { + self.nb_matrix_multiply = py_binary_num_func!(PyNumberMatmulProtocol, T::__matmul__); } -} - -trait PyNumberIndexProtocolImpl { - fn nb_index() -> Option; -} - -impl<'p, T> PyNumberIndexProtocolImpl for T -where - T: PyNumberProtocol<'p>, -{ - default fn nb_index() -> Option { - None + pub fn set_rmatmul(&mut self) + where + T: for<'p> PyNumberRMatmulProtocol<'p>, + { + self.nb_matrix_multiply = + py_binary_reversed_num_func!(PyNumberRMatmulProtocol, T::__rmatmul__); } -} - -impl PyNumberIndexProtocolImpl for T -where - T: for<'p> PyNumberIndexProtocol<'p>, -{ - fn nb_index() -> Option { - py_unary_func!(PyNumberIndexProtocol, T::__index__) + pub fn set_imatmul(&mut self) + where + T: for<'p> PyNumberIMatmulProtocol<'p>, + { + self.nb_inplace_matrix_multiply = + py_binary_self_func!(PyNumberIMatmulProtocol, T::__imatmul__); } } diff --git a/src/class/proto_methods.rs b/src/class/proto_methods.rs new file mode 100644 index 00000000000..76e75d9f440 --- /dev/null +++ b/src/class/proto_methods.rs @@ -0,0 +1,154 @@ +use crate::class::{ + basic::PyObjectMethods, descr::PyDescrMethods, gc::PyGCMethods, iter::PyIterMethods, +}; +use crate::ffi::{ + PyAsyncMethods, PyBufferProcs, PyMappingMethods, PyNumberMethods, PySequenceMethods, +}; +use std::{ + ptr::{self, NonNull}, + sync::atomic::{AtomicPtr, Ordering}, +}; + +/// Defines all method tables we need for object protocols. +// Note(kngwyu): default implementations are for rust-numpy. Please don't remove them. +pub trait PyProtoMethods { + fn async_methods() -> Option> { + None + } + fn basic_methods() -> Option> { + None + } + fn buffer_methods() -> Option> { + None + } + fn descr_methods() -> Option> { + None + } + fn gc_methods() -> Option> { + None + } + fn mapping_methods() -> Option> { + None + } + fn number_methods() -> Option> { + None + } + fn iter_methods() -> Option> { + None + } + fn sequence_methods() -> Option> { + None + } +} + +/// Indicates that a type has a protocol registry. Implemented by `#[pyclass]`. +#[doc(hidden)] +pub trait HasProtoRegistry: Sized + 'static { + fn registry() -> &'static PyProtoRegistry; +} + +impl PyProtoMethods for T { + fn async_methods() -> Option> { + NonNull::new(Self::registry().async_methods.load(Ordering::Relaxed)) + } + fn basic_methods() -> Option> { + NonNull::new(Self::registry().basic_methods.load(Ordering::Relaxed)) + } + fn buffer_methods() -> Option> { + NonNull::new(Self::registry().buffer_methods.load(Ordering::Relaxed)) + } + fn descr_methods() -> Option> { + NonNull::new(Self::registry().descr_methods.load(Ordering::Relaxed)) + } + fn gc_methods() -> Option> { + NonNull::new(Self::registry().gc_methods.load(Ordering::Relaxed)) + } + fn mapping_methods() -> Option> { + NonNull::new(Self::registry().mapping_methods.load(Ordering::Relaxed)) + } + fn number_methods() -> Option> { + NonNull::new(Self::registry().number_methods.load(Ordering::Relaxed)) + } + fn iter_methods() -> Option> { + NonNull::new(Self::registry().iter_methods.load(Ordering::Relaxed)) + } + fn sequence_methods() -> Option> { + NonNull::new(Self::registry().sequence_methods.load(Ordering::Relaxed)) + } +} + +/// Stores all method protocols. +/// Used in the proc-macro code as a static variable. +#[doc(hidden)] +pub struct PyProtoRegistry { + /// Async protocols. + async_methods: AtomicPtr, + /// Basic protocols. + basic_methods: AtomicPtr, + /// Buffer protocols. + buffer_methods: AtomicPtr, + /// Descr pProtocols. + descr_methods: AtomicPtr, + /// GC protocols. + gc_methods: AtomicPtr, + /// Mapping protocols. + mapping_methods: AtomicPtr, + /// Number protocols. + number_methods: AtomicPtr, + /// Iterator protocols. + iter_methods: AtomicPtr, + /// Sequence protocols. + sequence_methods: AtomicPtr, +} + +impl PyProtoRegistry { + pub const fn new() -> Self { + PyProtoRegistry { + async_methods: AtomicPtr::new(ptr::null_mut()), + basic_methods: AtomicPtr::new(ptr::null_mut()), + buffer_methods: AtomicPtr::new(ptr::null_mut()), + descr_methods: AtomicPtr::new(ptr::null_mut()), + gc_methods: AtomicPtr::new(ptr::null_mut()), + mapping_methods: AtomicPtr::new(ptr::null_mut()), + number_methods: AtomicPtr::new(ptr::null_mut()), + iter_methods: AtomicPtr::new(ptr::null_mut()), + sequence_methods: AtomicPtr::new(ptr::null_mut()), + } + } + pub fn set_async_methods(&self, methods: PyAsyncMethods) { + self.async_methods + .store(Box::into_raw(Box::new(methods)), Ordering::Relaxed) + } + pub fn set_basic_methods(&self, methods: PyObjectMethods) { + self.basic_methods + .store(Box::into_raw(Box::new(methods)), Ordering::Relaxed) + } + pub fn set_buffer_methods(&self, methods: PyBufferProcs) { + self.buffer_methods + .store(Box::into_raw(Box::new(methods)), Ordering::Relaxed) + } + pub fn set_descr_methods(&self, methods: PyDescrMethods) { + self.descr_methods + .store(Box::into_raw(Box::new(methods)), Ordering::Relaxed) + } + pub fn set_gc_methods(&self, methods: PyGCMethods) { + self.gc_methods + .store(Box::into_raw(Box::new(methods)), Ordering::Relaxed) + } + pub fn set_mapping_methods(&self, methods: PyMappingMethods) { + self.mapping_methods + .store(Box::into_raw(Box::new(methods)), Ordering::Relaxed) + } + pub fn set_number_methods(&self, methods: PyNumberMethods) { + self.number_methods + .store(Box::into_raw(Box::new(methods)), Ordering::Relaxed) + } + pub fn set_iter_methods(&self, methods: PyIterMethods) { + self.iter_methods + .store(Box::into_raw(Box::new(methods)), Ordering::Relaxed) + } + pub fn set_sequence_methods(&self, methods: PySequenceMethods) { + self.sequence_methods + .store(Box::into_raw(Box::new(methods)), Ordering::Relaxed) + } +} diff --git a/src/class/pyasync.rs b/src/class/pyasync.rs index a65a91d6e91..4cb3e102f37 100644 --- a/src/class/pyasync.rs +++ b/src/class/pyasync.rs @@ -8,6 +8,7 @@ //! [PEP-0492](https://www.python.org/dev/peps/pep-0492/) //! +use crate::derive_utils::TryFromPyCell; use crate::err::PyResult; use crate::{ffi, PyClass, PyObject}; @@ -16,21 +17,21 @@ use crate::{ffi, PyClass, PyObject}; /// Each method in this trait corresponds to Python async/await implementation. #[allow(unused_variables)] pub trait PyAsyncProtocol<'p>: PyClass { - fn __await__(&'p self) -> Self::Result + fn __await__(slf: Self::Receiver) -> Self::Result where Self: PyAsyncAwaitProtocol<'p>, { unimplemented!() } - fn __aiter__(&'p self) -> Self::Result + fn __aiter__(slf: Self::Receiver) -> Self::Result where Self: PyAsyncAiterProtocol<'p>, { unimplemented!() } - fn __anext__(&'p mut self) -> Self::Result + fn __anext__(slf: Self::Receiver) -> Self::Result where Self: PyAsyncAnextProtocol<'p>, { @@ -58,16 +59,19 @@ pub trait PyAsyncProtocol<'p>: PyClass { } pub trait PyAsyncAwaitProtocol<'p>: PyAsyncProtocol<'p> { + type Receiver: TryFromPyCell<'p, Self>; type Success: crate::IntoPy; type Result: Into>; } pub trait PyAsyncAiterProtocol<'p>: PyAsyncProtocol<'p> { + type Receiver: TryFromPyCell<'p, Self>; type Success: crate::IntoPy; type Result: Into>; } pub trait PyAsyncAnextProtocol<'p>: PyAsyncProtocol<'p> { + type Receiver: TryFromPyCell<'p, Self>; type Success: crate::IntoPy; type Result: Into>>; } @@ -86,91 +90,29 @@ pub trait PyAsyncAexitProtocol<'p>: PyAsyncProtocol<'p> { } #[doc(hidden)] -pub trait PyAsyncProtocolImpl { - fn tp_as_async() -> Option; -} - -impl PyAsyncProtocolImpl for T { - default fn tp_as_async() -> Option { - None - } -} - -impl<'p, T> PyAsyncProtocolImpl for T -where - T: PyAsyncProtocol<'p>, -{ - #[inline] - fn tp_as_async() -> Option { - Some(ffi::PyAsyncMethods { - am_await: Self::am_await(), - am_aiter: Self::am_aiter(), - am_anext: Self::am_anext(), - }) - } -} - -trait PyAsyncAwaitProtocolImpl { - fn am_await() -> Option; -} - -impl<'p, T> PyAsyncAwaitProtocolImpl for T -where - T: PyAsyncProtocol<'p>, -{ - default fn am_await() -> Option { - None - } -} - -impl PyAsyncAwaitProtocolImpl for T -where - T: for<'p> PyAsyncAwaitProtocol<'p>, -{ - #[inline] - fn am_await() -> Option { - py_unary_func!(PyAsyncAwaitProtocol, T::__await__) - } -} - -trait PyAsyncAiterProtocolImpl { - fn am_aiter() -> Option; -} - -impl<'p, T> PyAsyncAiterProtocolImpl for T -where - T: PyAsyncProtocol<'p>, -{ - default fn am_aiter() -> Option { - None +impl ffi::PyAsyncMethods { + pub fn set_await(&mut self) + where + T: for<'p> PyAsyncAwaitProtocol<'p>, + { + self.am_await = py_unarys_func!(PyAsyncAwaitProtocol, T::__await__); } -} - -impl PyAsyncAiterProtocolImpl for T -where - T: for<'p> PyAsyncAiterProtocol<'p>, -{ - #[inline] - fn am_aiter() -> Option { - py_unary_func!(PyAsyncAiterProtocol, T::__aiter__) + pub fn set_aiter(&mut self) + where + T: for<'p> PyAsyncAiterProtocol<'p>, + { + self.am_aiter = py_unarys_func!(PyAsyncAiterProtocol, T::__aiter__); } -} - -trait PyAsyncAnextProtocolImpl { - fn am_anext() -> Option; -} - -impl<'p, T> PyAsyncAnextProtocolImpl for T -where - T: PyAsyncProtocol<'p>, -{ - default fn am_anext() -> Option { - None + pub fn set_anext(&mut self) + where + T: for<'p> PyAsyncAnextProtocol<'p>, + { + self.am_anext = anext::am_anext::(); } } mod anext { - use super::{PyAsyncAnextProtocol, PyAsyncAnextProtocolImpl}; + use super::PyAsyncAnextProtocol; use crate::callback::IntoPyCallbackOutput; use crate::err::PyResult; use crate::IntoPyPointer; @@ -191,19 +133,11 @@ mod anext { } } - impl PyAsyncAnextProtocolImpl for T + #[inline] + pub(super) fn am_anext() -> Option where T: for<'p> PyAsyncAnextProtocol<'p>, { - #[inline] - fn am_anext() -> Option { - py_unary_func!( - PyAsyncAnextProtocol, - T::__anext__, - call_mut, - *mut crate::ffi::PyObject, - IterANextOutput - ) - } + py_unarys_func!(PyAsyncAnextProtocol, T::__anext__, IterANextOutput) } } diff --git a/src/class/sequence.rs b/src/class/sequence.rs index ad88032c968..3a0f605d19f 100644 --- a/src/class/sequence.rs +++ b/src/class/sequence.rs @@ -127,355 +127,168 @@ pub trait PySequenceInplaceRepeatProtocol<'p>: PySequenceProtocol<'p> + IntoPy

Option; -} - -impl PySequenceProtocolImpl for T { - default fn tp_as_sequence() -> Option { - None - } -} - -impl<'p, T> PySequenceProtocolImpl for T -where - T: PySequenceProtocol<'p>, -{ - fn tp_as_sequence() -> Option { - Some(ffi::PySequenceMethods { - sq_length: Self::sq_length(), - sq_concat: Self::sq_concat(), - sq_repeat: Self::sq_repeat(), - sq_item: Self::sq_item(), - was_sq_slice: ::std::ptr::null_mut(), - sq_ass_item: sq_ass_item_impl::sq_ass_item::(), - was_sq_ass_slice: ::std::ptr::null_mut(), - sq_contains: Self::sq_contains(), - sq_inplace_concat: Self::sq_inplace_concat(), - sq_inplace_repeat: Self::sq_inplace_repeat(), - }) - } -} - -trait PySequenceLenProtocolImpl { - fn sq_length() -> Option; -} - -impl<'p, T> PySequenceLenProtocolImpl for T -where - T: PySequenceProtocol<'p>, -{ - default fn sq_length() -> Option { - None - } -} - -impl PySequenceLenProtocolImpl for T -where - T: for<'p> PySequenceLenProtocol<'p>, -{ - fn sq_length() -> Option { - py_len_func!(PySequenceLenProtocol, T::__len__) - } -} - -trait PySequenceGetItemProtocolImpl { - fn sq_item() -> Option; -} - -impl<'p, T> PySequenceGetItemProtocolImpl for T -where - T: PySequenceProtocol<'p>, -{ - default fn sq_item() -> Option { - None - } -} - -impl PySequenceGetItemProtocolImpl for T -where - T: for<'p> PySequenceGetItemProtocol<'p>, -{ - fn sq_item() -> Option { - py_ssizearg_func!(PySequenceGetItemProtocol, T::__getitem__) - } -} - -/// It can be possible to delete and set items (PySequenceSetItemProtocol and -/// PySequenceDelItemProtocol implemented), only to delete (PySequenceDelItemProtocol implemented) -/// or no deleting or setting is possible -mod sq_ass_item_impl { - use super::*; - - /// ssizeobjargproc PySequenceMethods.sq_ass_item - /// - /// This function is used by PySequence_SetItem() and has the same signature. It is also used - /// by PyObject_SetItem() and PyObject_DelItem(), after trying the item assignment and deletion - /// via the mp_ass_subscript slot. This slot may be left to NULL if the object does not support - /// item assignment and deletion. - pub(super) fn sq_ass_item<'p, T>() -> Option +impl ffi::PySequenceMethods { + pub fn set_len(&mut self) where - T: PySequenceProtocol<'p>, + T: for<'p> PySequenceLenProtocol<'p>, { - if let Some(del_set_item) = T::del_set_item() { - Some(del_set_item) - } else if let Some(del_item) = T::del_item() { - Some(del_item) - } else if let Some(set_item) = T::set_item() { - Some(set_item) - } else { - None - } + self.sq_length = py_len_func!(PySequenceLenProtocol, T::__len__); } - - trait SetItem { - fn set_item() -> Option; - } - - impl<'p, T> SetItem for T + pub fn set_concat(&mut self) where - T: PySequenceProtocol<'p>, + T: for<'p> PySequenceConcatProtocol<'p>, { - default fn set_item() -> Option { - None - } + self.sq_concat = py_binary_func!(PySequenceConcatProtocol, T::__concat__); } - - impl SetItem for T + pub fn set_repeat(&mut self) where - T: for<'p> PySequenceSetItemProtocol<'p>, + T: for<'p> PySequenceRepeatProtocol<'p>, { - fn set_item() -> Option { - unsafe extern "C" fn wrap( - slf: *mut ffi::PyObject, - key: ffi::Py_ssize_t, - value: *mut ffi::PyObject, - ) -> c_int - where - T: for<'p> PySequenceSetItemProtocol<'p>, - { - crate::callback_body!(py, { - let slf = py.from_borrowed_ptr::>(slf); - - if value.is_null() { - return Err(PyErr::new::(format!( - "Item deletion is not supported by {:?}", - stringify!(T) - ))); - } - - let mut slf = slf.try_borrow_mut()?; - let value = py.from_borrowed_ptr::(value); - let value = value.extract()?; - slf.__setitem__(key.into(), value).into() - }) - } - Some(wrap::) - } + self.sq_repeat = py_ssizearg_func!(PySequenceRepeatProtocol, T::__repeat__); } - - trait DelItem { - fn del_item() -> Option; + pub fn set_getitem(&mut self) + where + T: for<'p> PySequenceGetItemProtocol<'p>, + { + self.sq_item = py_ssizearg_func!(PySequenceGetItemProtocol, T::__getitem__); } - - impl<'p, T> DelItem for T + pub fn set_setitem(&mut self) where - T: PySequenceProtocol<'p>, + T: for<'p> PySequenceSetItemProtocol<'p>, { - default fn del_item() -> Option { - None - } + self.sq_ass_item = sq_ass_item_impl::set_item::(); } - - impl DelItem for T + pub fn set_delitem(&mut self) where T: for<'p> PySequenceDelItemProtocol<'p>, { - fn del_item() -> Option { - unsafe extern "C" fn wrap( - slf: *mut ffi::PyObject, - key: ffi::Py_ssize_t, - value: *mut ffi::PyObject, - ) -> c_int - where - T: for<'p> PySequenceDelItemProtocol<'p>, - { - crate::callback_body!(py, { - let slf = py.from_borrowed_ptr::>(slf); - - if value.is_null() { - slf.borrow_mut().__delitem__(key.into()).into() - } else { - Err(PyErr::new::(format!( - "Item assignment not supported by {:?}", - stringify!(T) - ))) - } - }) - } - Some(wrap::) - } - } - - trait DelSetItem { - fn del_set_item() -> Option; + self.sq_ass_item = sq_ass_item_impl::del_item::(); } - - impl<'p, T> DelSetItem for T + pub fn set_setdelitem(&mut self) where - T: PySequenceProtocol<'p>, + T: for<'p> PySequenceDelItemProtocol<'p> + for<'p> PySequenceSetItemProtocol<'p>, { - default fn del_set_item() -> Option { - None - } + self.sq_ass_item = sq_ass_item_impl::set_del_item::(); } - - impl DelSetItem for T + pub fn set_contains(&mut self) where - T: for<'p> PySequenceSetItemProtocol<'p> + for<'p> PySequenceDelItemProtocol<'p>, + T: for<'p> PySequenceContainsProtocol<'p>, { - fn del_set_item() -> Option { - unsafe extern "C" fn wrap( - slf: *mut ffi::PyObject, - key: ffi::Py_ssize_t, - value: *mut ffi::PyObject, - ) -> c_int - where - T: for<'p> PySequenceSetItemProtocol<'p> + for<'p> PySequenceDelItemProtocol<'p>, - { - crate::callback_body!(py, { - let slf = py.from_borrowed_ptr::>(slf); - - if value.is_null() { - call_mut!(slf, __delitem__; key.into()) - } else { - let value = py.from_borrowed_ptr::(value); - let mut slf_ = slf.try_borrow_mut()?; - let value = value.extract()?; - slf_.__setitem__(key.into(), value).into() - } - }) - } - Some(wrap::) - } - } -} - -trait PySequenceContainsProtocolImpl { - fn sq_contains() -> Option; -} - -impl<'p, T> PySequenceContainsProtocolImpl for T -where - T: PySequenceProtocol<'p>, -{ - default fn sq_contains() -> Option { - None - } -} - -impl PySequenceContainsProtocolImpl for T -where - T: for<'p> PySequenceContainsProtocol<'p>, -{ - fn sq_contains() -> Option { - py_binary_func!(PySequenceContainsProtocol, T::__contains__, c_int) - } -} - -trait PySequenceConcatProtocolImpl { - fn sq_concat() -> Option; -} - -impl<'p, T> PySequenceConcatProtocolImpl for T -where - T: PySequenceProtocol<'p>, -{ - default fn sq_concat() -> Option { - None - } -} - -impl PySequenceConcatProtocolImpl for T -where - T: for<'p> PySequenceConcatProtocol<'p>, -{ - fn sq_concat() -> Option { - py_binary_func!(PySequenceConcatProtocol, T::__concat__) - } -} - -trait PySequenceRepeatProtocolImpl { - fn sq_repeat() -> Option; -} - -impl<'p, T> PySequenceRepeatProtocolImpl for T -where - T: PySequenceProtocol<'p>, -{ - default fn sq_repeat() -> Option { - None - } -} - -impl PySequenceRepeatProtocolImpl for T -where - T: for<'p> PySequenceRepeatProtocol<'p>, -{ - fn sq_repeat() -> Option { - py_ssizearg_func!(PySequenceRepeatProtocol, T::__repeat__) - } -} - -trait PySequenceInplaceConcatProtocolImpl { - fn sq_inplace_concat() -> Option; -} - -impl<'p, T> PySequenceInplaceConcatProtocolImpl for T -where - T: PySequenceProtocol<'p>, -{ - default fn sq_inplace_concat() -> Option { - None + self.sq_contains = py_binary_func!(PySequenceContainsProtocol, T::__contains__, c_int); } -} - -impl PySequenceInplaceConcatProtocolImpl for T -where - T: for<'p> PySequenceInplaceConcatProtocol<'p>, -{ - fn sq_inplace_concat() -> Option { - py_binary_func!( + pub fn set_inplace_concat(&mut self) + where + T: for<'p> PySequenceInplaceConcatProtocol<'p>, + { + self.sq_inplace_concat = py_binary_func!( PySequenceInplaceConcatProtocol, T::__inplace_concat__, *mut ffi::PyObject, call_mut ) } + pub fn set_inplace_repeat(&mut self) + where + T: for<'p> PySequenceInplaceRepeatProtocol<'p>, + { + self.sq_inplace_repeat = py_ssizearg_func!( + PySequenceInplaceRepeatProtocol, + T::__inplace_repeat__, + call_mut + ) + } } -trait PySequenceInplaceRepeatProtocolImpl { - fn sq_inplace_repeat() -> Option; -} +/// It can be possible to delete and set items (PySequenceSetItemProtocol and +/// PySequenceDelItemProtocol implemented), only to delete (PySequenceDelItemProtocol implemented) +/// or no deleting or setting is possible +mod sq_ass_item_impl { + use super::*; -impl<'p, T> PySequenceInplaceRepeatProtocolImpl for T -where - T: PySequenceProtocol<'p>, -{ - default fn sq_inplace_repeat() -> Option { - None + pub(super) fn set_item() -> Option + where + T: for<'p> PySequenceSetItemProtocol<'p>, + { + unsafe extern "C" fn wrap( + slf: *mut ffi::PyObject, + key: ffi::Py_ssize_t, + value: *mut ffi::PyObject, + ) -> c_int + where + T: for<'p> PySequenceSetItemProtocol<'p>, + { + crate::callback_body!(py, { + let slf = py.from_borrowed_ptr::>(slf); + + if value.is_null() { + return Err(PyErr::new::(format!( + "Item deletion is not supported by {:?}", + stringify!(T) + ))); + } + + let mut slf = slf.try_borrow_mut()?; + let value = py.from_borrowed_ptr::(value); + let value = value.extract()?; + slf.__setitem__(key.into(), value).into() + }) + } + Some(wrap::) } -} -impl PySequenceInplaceRepeatProtocolImpl for T -where - T: for<'p> PySequenceInplaceRepeatProtocol<'p>, -{ - fn sq_inplace_repeat() -> Option { - py_ssizearg_func!( - PySequenceInplaceRepeatProtocol, - T::__inplace_repeat__, - call_mut - ) + pub(super) fn del_item() -> Option + where + T: for<'p> PySequenceDelItemProtocol<'p>, + { + unsafe extern "C" fn wrap( + slf: *mut ffi::PyObject, + key: ffi::Py_ssize_t, + value: *mut ffi::PyObject, + ) -> c_int + where + T: for<'p> PySequenceDelItemProtocol<'p>, + { + crate::callback_body!(py, { + let slf = py.from_borrowed_ptr::>(slf); + + if value.is_null() { + slf.borrow_mut().__delitem__(key.into()).into() + } else { + Err(PyErr::new::(format!( + "Item assignment not supported by {:?}", + stringify!(T) + ))) + } + }) + } + Some(wrap::) + } + + pub(super) fn set_del_item() -> Option + where + T: for<'p> PySequenceSetItemProtocol<'p> + for<'p> PySequenceDelItemProtocol<'p>, + { + unsafe extern "C" fn wrap( + slf: *mut ffi::PyObject, + key: ffi::Py_ssize_t, + value: *mut ffi::PyObject, + ) -> c_int + where + T: for<'p> PySequenceSetItemProtocol<'p> + for<'p> PySequenceDelItemProtocol<'p>, + { + crate::callback_body!(py, { + let slf = py.from_borrowed_ptr::>(slf); + + if value.is_null() { + call_mut!(slf, __delitem__; key.into()) + } else { + let value = py.from_borrowed_ptr::(value); + let mut slf_ = slf.try_borrow_mut()?; + let value = value.extract()?; + slf_.__setitem__(key.into(), value).into() + } + }) + } + Some(wrap::) } } diff --git a/src/ffi/object.rs b/src/ffi/object.rs index 175105b8e1f..ef43db5b7bc 100644 --- a/src/ffi/object.rs +++ b/src/ffi/object.rs @@ -438,14 +438,16 @@ mod typeobject { impl Default for PyAsyncMethods { #[inline] fn default() -> Self { - unsafe { mem::zeroed() } + PyAsyncMethods_INIT } } + pub const PyAsyncMethods_INIT: PyAsyncMethods = PyAsyncMethods { am_await: None, am_aiter: None, am_anext: None, }; + #[repr(C)] #[derive(Copy, Clone, Debug)] pub struct PyBufferProcs { @@ -456,9 +458,10 @@ mod typeobject { impl Default for PyBufferProcs { #[inline] fn default() -> Self { - unsafe { mem::zeroed() } + PyBufferProcs_INIT } } + pub const PyBufferProcs_INIT: PyBufferProcs = PyBufferProcs { bf_getbuffer: None, bf_releasebuffer: None, diff --git a/src/instance.rs b/src/instance.rs index ad2fd59b0b4..2dce9efd9f0 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -47,7 +47,6 @@ pub unsafe trait PyNativeType: Sized { pub struct Py(NonNull, PhantomData); unsafe impl Send for Py {} - unsafe impl Sync for Py {} impl Py diff --git a/src/lib.rs b/src/lib.rs index 0051461347b..92204145722 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -153,6 +153,7 @@ pub use crate::types::PyAny; #[cfg(feature = "macros")] #[doc(hidden)] pub use { + ctor, // Re-exported for pyproto indoc, // Re-exported for py_run inventory, // Re-exported for pymethods paste, // Re-exported for wrap_function diff --git a/src/pyclass.rs b/src/pyclass.rs index 95351cf45e4..d3b395e45c8 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -1,5 +1,6 @@ //! `PyClass` trait use crate::class::methods::{PyClassAttributeDef, PyMethodDefType, PyMethods}; +use crate::class::proto_methods::PyProtoMethods; use crate::conversion::{IntoPyPointer, ToPyObject}; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; use crate::type_object::{type_flags, PyLayout}; @@ -77,6 +78,7 @@ pub trait PyClass: + Sized + PyClassAlloc + PyMethods + + PyProtoMethods + Send { /// Specify this class has `#[pyclass(dict)]` or not. @@ -141,35 +143,43 @@ where } // GC support - ::update_type_object(type_object); + if let Some(gc) = T::gc_methods() { + unsafe { gc.as_ref() }.update_typeobj(type_object); + } // descriptor protocol - ::tp_as_descr(type_object); + if let Some(descr) = T::descr_methods() { + unsafe { descr.as_ref() }.update_typeobj(type_object); + } // iterator methods - ::tp_as_iter(type_object); + if let Some(iter) = T::iter_methods() { + unsafe { iter.as_ref() }.update_typeobj(type_object); + } + // nb_bool is a part of PyObjectProtocol, but should be placed under tp_as_number + let mut nb_bool = None; // basic methods - ::tp_as_object(type_object); - - fn to_ptr(value: Option) -> *mut T { - value - .map(|v| Box::into_raw(Box::new(v))) - .unwrap_or_else(ptr::null_mut) + if let Some(basic) = T::basic_methods() { + unsafe { basic.as_ref() }.update_typeobj(type_object); + nb_bool = unsafe { basic.as_ref() }.nb_bool; } // number methods - type_object.tp_as_number = to_ptr(::tp_as_number()); + type_object.tp_as_number = T::number_methods() + .map(|mut p| { + unsafe { p.as_mut() }.nb_bool = nb_bool; + p.as_ptr() + }) + .unwrap_or_else(|| nb_bool.map_or_else(ptr::null_mut, ffi::PyNumberMethods::from_nb_bool)); // mapping methods - type_object.tp_as_mapping = - to_ptr(::tp_as_mapping()); + type_object.tp_as_mapping = T::mapping_methods().map_or_else(ptr::null_mut, |p| p.as_ptr()); // sequence methods - type_object.tp_as_sequence = - to_ptr(::tp_as_sequence()); + type_object.tp_as_sequence = T::sequence_methods().map_or_else(ptr::null_mut, |p| p.as_ptr()); // async methods - type_object.tp_as_async = to_ptr(::tp_as_async()); + type_object.tp_as_async = T::async_methods().map_or_else(ptr::null_mut, |p| p.as_ptr()); // buffer protocol - type_object.tp_as_buffer = to_ptr(::tp_as_buffer()); + type_object.tp_as_buffer = T::buffer_methods().map_or_else(ptr::null_mut, |p| p.as_ptr()); let (new, call, mut methods, attrs) = py_class_method_defs::(); diff --git a/tests/test_dunder.rs b/tests/test_dunder.rs index 421873f0f4b..60b242cc4f4 100644 --- a/tests/test_dunder.rs +++ b/tests/test_dunder.rs @@ -1,5 +1,6 @@ use pyo3::class::{ - PyContextProtocol, PyIterProtocol, PyMappingProtocol, PyObjectProtocol, PySequenceProtocol, + PyAsyncProtocol, PyContextProtocol, PyDescrProtocol, PyIterProtocol, PyMappingProtocol, + PyObjectProtocol, PySequenceProtocol, }; use pyo3::exceptions::{IndexError, ValueError}; use pyo3::prelude::*; @@ -552,3 +553,131 @@ fn getattr_doesnt_override_member() { py_assert!(py, inst, "inst.data == 4"); py_assert!(py, inst, "inst.a == 8"); } + +/// Wraps a Python future and yield it once. +#[pyclass] +struct OnceFuture { + future: PyObject, + polled: bool, +} + +#[pymethods] +impl OnceFuture { + #[new] + fn new(future: PyObject) -> Self { + OnceFuture { + future, + polled: false, + } + } +} + +#[pyproto] +impl PyAsyncProtocol for OnceFuture { + fn __await__(slf: PyRef<'p, Self>) -> PyResult> { + Ok(slf) + } +} + +#[pyproto] +impl PyIterProtocol for OnceFuture { + fn __iter__(slf: PyRef<'p, Self>) -> PyResult> { + Ok(slf) + } + fn __next__(mut slf: PyRefMut) -> PyResult> { + if !slf.polled { + slf.polled = true; + Ok(Some(slf.future.clone())) + } else { + Ok(None) + } + } +} + +#[test] +fn test_await() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let once = py.get_type::(); + let source = pyo3::indoc::indoc!( + r#" +import asyncio +import sys + +async def main(): + res = await Once(await asyncio.sleep(0.1)) + return res +# For an odd error similar to https://bugs.python.org/issue38563 +if sys.platform == "win32" and sys.version_info >= (3, 8, 0): + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) +# get_event_loop can raise an error: https://github.com/PyO3/pyo3/pull/961#issuecomment-645238579 +loop = asyncio.new_event_loop() +asyncio.set_event_loop(loop) +assert loop.run_until_complete(main()) is None +loop.close() +"# + ); + let globals = PyModule::import(py, "__main__").unwrap().dict(); + globals.set_item("Once", once).unwrap(); + py.run(source, Some(globals), None) + .map_err(|e| e.print(py)) + .unwrap(); +} + +/// Increment the count when `__get__` is called. +#[pyclass] +struct DescrCounter { + #[pyo3(get)] + count: usize, +} + +#[pymethods] +impl DescrCounter { + #[new] + fn new() -> Self { + DescrCounter { count: 0 } + } +} + +#[pyproto] +impl PyDescrProtocol for DescrCounter { + fn __get__( + mut slf: PyRefMut<'p, Self>, + _instance: &PyAny, + _owner: Option<&'p PyType>, + ) -> PyResult> { + slf.count += 1; + Ok(slf) + } + fn __set__( + _slf: PyRef<'p, Self>, + _instance: &PyAny, + mut new_value: PyRefMut<'p, Self>, + ) -> PyResult<()> { + new_value.count = _slf.count; + Ok(()) + } +} + +#[test] +fn descr_getset() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let counter = py.get_type::(); + let source = pyo3::indoc::indoc!( + r#" +class Class: + counter = Counter() +c = Class() +c.counter # count += 1 +assert c.counter.count == 2 +c.counter = Counter() +assert c.counter.count == 3 +"# + ); + let globals = PyModule::import(py, "__main__").unwrap().dict(); + globals.set_item("Counter", counter).unwrap(); + py.run(source, Some(globals), None) + .map_err(|e| e.print(py)) + .unwrap(); +}