diff --git a/guide/src/class.md b/guide/src/class.md index 62052772..c564cbaa 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -772,23 +772,31 @@ impl pyo3::class::methods::HasMethodsInventory for MyClass { } pyo3::inventory::collect!(Pyo3MethodsInventoryForMyClass); - -pub struct Pyo3ProtoInventoryForMyClass { - def: pyo3::class::proto_methods::PyProtoMethodDef, -} -impl pyo3::class::proto_methods::PyProtoInventory for Pyo3ProtoInventoryForMyClass { - fn new(def: pyo3::class::proto_methods::PyProtoMethodDef) -> Self { - Self { def } +impl pyo3::class::proto_methods::PyProtoMethods for MyClass { + fn for_each_proto_slot(visitor: Visitor) { + // Implementation which uses dtolnay specialization to load all slots. + use pyo3::class::proto_methods::*; + let protocols = PyClassProtocols::::new(); + protocols.object_protocol_slots() + .iter() + .chain(protocols.number_protocol_slots()) + .chain(protocols.iter_protocol_slots()) + .chain(protocols.gc_protocol_slots()) + .chain(protocols.descr_protocol_slots()) + .chain(protocols.mapping_protocol_slots()) + .chain(protocols.sequence_protocol_slots()) + .chain(protocols.async_protocol_slots()) + .chain(protocols.buffer_protocol_slots()) + .cloned() + .for_each(visitor); } - fn get(&'static self) -> &'static pyo3::class::proto_methods::PyProtoMethodDef { - &self.def + + fn get_buffer() -> Option<&'static pyo3::class::proto_methods::PyBufferProcs> { + use pyo3::class::proto_methods::*; + let protocols = PyClassProtocols::::new(); + protocols.buffer_procs() } } -impl pyo3::class::proto_methods::HasProtoInventory for MyClass { - type ProtoMethods = Pyo3ProtoInventoryForMyClass; -} -pyo3::inventory::collect!(Pyo3ProtoInventoryForMyClass); - impl pyo3::pyclass::PyClassSend for MyClass { type ThreadChecker = pyo3::pyclass::ThreadCheckerStub; diff --git a/pyo3-macros-backend/src/defs.rs b/pyo3-macros-backend/src/defs.rs index 6dcf93e1..7ad8b88c 100644 --- a/pyo3-macros-backend/src/defs.rs +++ b/pyo3-macros-backend/src/defs.rs @@ -6,14 +6,18 @@ use std::collections::HashSet; pub struct Proto { /// The name of this protocol. E.g., Iter. pub name: &'static str, - /// Extension trait that has `get_*` methods - pub extension_trait: &'static str, + /// The path to the module which contains this proto implementation. + module: &'static str, + /// Trait which stores the slots + pub slots_trait: &'static str, + /// Trait method which accesses the slots. + pub slots_trait_slots: &'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. - slot_getters: &'static [SlotGetter], + slot_defs: &'static [SlotDef], } impl Proto { @@ -23,32 +27,41 @@ impl Proto { { self.methods.iter().find(|m| query == m.name) } + pub(crate) fn get_method(&self, query: Q) -> Option<&'static PyMethod> where Q: PartialEq<&'static str>, { self.py_methods.iter().find(|m| query == m.name) } + + // Returns the hard-coded module as a path + #[inline] + pub(crate) fn module(&self) -> syn::Path { + syn::parse_str(self.module).expect("module def not valid path") + } + // Since the order matters, we expose only the iterator instead of the slice. - pub(crate) fn slot_getters( + pub(crate) fn slot_defs( &self, mut implemented_protocols: HashSet, - ) -> impl Iterator { - self.slot_getters.iter().filter_map(move |getter| { - // If any required method is not implemented, we skip this setter. - if getter + ) -> impl Iterator { + self.slot_defs.iter().filter(move |slot_def| { + // If any required method is not implemented, we skip this def. + let all_methods_implemented = slot_def .proto_names .iter() - .any(|name| !implemented_protocols.contains(*name)) - { - return None; + .all(|name| implemented_protocols.contains(*name)); + + if all_methods_implemented { + // To use 'paired' def in priority, we remove used protocols. + // For example, if add_radd is already used, we shouldn't use add and radd. + for name in slot_def.proto_names { + implemented_protocols.remove(*name); + } } - // To use 'paired' setter in priority, we remove used protocols. - // For example, if set_add_radd is already used, we shouldn't use set_add and set_radd. - for name in getter.proto_names { - implemented_protocols.remove(*name); - } - Some(getter.get_function) + + all_methods_implemented }) } } @@ -79,584 +92,517 @@ impl PyMethod { } } -/// Represents a setter used to register a method to the method table. -struct SlotGetter { - /// Protocols necessary for invoking this setter. +/// Represents a slot definition. +pub struct SlotDef { + /// Protocols necessary to meet this def. /// 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 get_function: &'static str, + /// The Python slot name. + pub slot: &'static str, + /// The name of the function in pyo3 which implements the slot. + pub slot_impl: &'static str, } -impl SlotGetter { - const fn new(names: &'static [&'static str], get_function: &'static str) -> Self { - SlotGetter { - proto_names: names, - get_function, +impl SlotDef { + const fn new( + proto_names: &'static [&'static str], + slot: &'static str, + slot_impl: &'static str, + ) -> Self { + SlotDef { + proto_names, + slot, + slot_impl, } } } pub const OBJECT: Proto = Proto { name: "Object", - extension_trait: "pyo3::class::basic::PyBasicSlots", + module: "pyo3::class::basic", + slots_trait: "PyObjectProtocolSlots", + slots_trait_slots: "object_protocol_slots", methods: &[ - MethodProto::new("__getattr__", "pyo3::class::basic::PyObjectGetAttrProtocol") + MethodProto::new("__getattr__", "PyObjectGetAttrProtocol") .args(&["Name"]) .has_self(), - MethodProto::new("__setattr__", "pyo3::class::basic::PyObjectSetAttrProtocol") + MethodProto::new("__setattr__", "PyObjectSetAttrProtocol") .args(&["Name", "Value"]) .has_self(), - MethodProto::new("__delattr__", "pyo3::class::basic::PyObjectDelAttrProtocol") + MethodProto::new("__delattr__", "PyObjectDelAttrProtocol") .args(&["Name"]) .has_self(), - MethodProto::new("__str__", "pyo3::class::basic::PyObjectStrProtocol").has_self(), - MethodProto::new("__repr__", "pyo3::class::basic::PyObjectReprProtocol").has_self(), - MethodProto::new("__format__", "pyo3::class::basic::PyObjectFormatProtocol") + MethodProto::new("__str__", "PyObjectStrProtocol").has_self(), + MethodProto::new("__repr__", "PyObjectReprProtocol").has_self(), + MethodProto::new("__format__", "PyObjectFormatProtocol") .args(&["Format"]) .has_self(), - MethodProto::new("__hash__", "pyo3::class::basic::PyObjectHashProtocol").has_self(), - MethodProto::new("__bytes__", "pyo3::class::basic::PyObjectBytesProtocol").has_self(), - MethodProto::new("__richcmp__", "pyo3::class::basic::PyObjectRichcmpProtocol") + MethodProto::new("__hash__", "PyObjectHashProtocol").has_self(), + MethodProto::new("__bytes__", "PyObjectBytesProtocol").has_self(), + MethodProto::new("__richcmp__", "PyObjectRichcmpProtocol") .args(&["Other"]) .has_self(), - MethodProto::new("__bool__", "pyo3::class::basic::PyObjectBoolProtocol").has_self(), + MethodProto::new("__bool__", "PyObjectBoolProtocol").has_self(), ], py_methods: &[ - PyMethod::new("__format__", "pyo3::class::basic::FormatProtocolImpl"), - PyMethod::new("__bytes__", "pyo3::class::basic::BytesProtocolImpl"), - PyMethod::new("__unicode__", "pyo3::class::basic::UnicodeProtocolImpl"), + PyMethod::new("__format__", "FormatProtocolImpl"), + PyMethod::new("__bytes__", "BytesProtocolImpl"), + PyMethod::new("__unicode__", "UnicodeProtocolImpl"), ], - slot_getters: &[ - SlotGetter::new(&["__str__"], "get_str"), - SlotGetter::new(&["__repr__"], "get_repr"), - SlotGetter::new(&["__hash__"], "get_hash"), - SlotGetter::new(&["__getattr__"], "get_getattr"), - SlotGetter::new(&["__richcmp__"], "get_richcmp"), - SlotGetter::new(&["__setattr__", "__delattr__"], "get_setdelattr"), - SlotGetter::new(&["__setattr__"], "get_setattr"), - SlotGetter::new(&["__delattr__"], "get_delattr"), - SlotGetter::new(&["__bool__"], "get_bool"), + slot_defs: &[ + SlotDef::new(&["__str__"], "Py_tp_str", "str"), + SlotDef::new(&["__repr__"], "Py_tp_repr", "repr"), + SlotDef::new(&["__hash__"], "Py_tp_hash", "hash"), + SlotDef::new(&["__getattr__"], "Py_tp_getattro", "getattr"), + SlotDef::new(&["__richcmp__"], "Py_tp_richcompare", "richcmp"), + SlotDef::new( + &["__setattr__", "__delattr__"], + "Py_tp_setattro", + "setdelattr", + ), + SlotDef::new(&["__setattr__"], "Py_tp_setattro", "setattr"), + SlotDef::new(&["__delattr__"], "Py_tp_setattro", "delattr"), + SlotDef::new(&["__bool__"], "Py_nb_bool", "bool"), ], }; pub const ASYNC: Proto = Proto { name: "Async", - extension_trait: "pyo3::class::pyasync::PyAsyncSlots", + module: "pyo3::class::pyasync", + slots_trait: "PyAsyncProtocolSlots", + slots_trait_slots: "async_protocol_slots", methods: &[ - MethodProto::new("__await__", "pyo3::class::pyasync::PyAsyncAwaitProtocol") - .args(&["Receiver"]), - MethodProto::new("__aiter__", "pyo3::class::pyasync::PyAsyncAiterProtocol") - .args(&["Receiver"]), - MethodProto::new("__anext__", "pyo3::class::pyasync::PyAsyncAnextProtocol") - .args(&["Receiver"]), - MethodProto::new("__aenter__", "pyo3::class::pyasync::PyAsyncAenterProtocol").has_self(), - MethodProto::new("__aexit__", "pyo3::class::pyasync::PyAsyncAexitProtocol") + MethodProto::new("__await__", "PyAsyncAwaitProtocol").args(&["Receiver"]), + MethodProto::new("__aiter__", "PyAsyncAiterProtocol").args(&["Receiver"]), + MethodProto::new("__anext__", "PyAsyncAnextProtocol").args(&["Receiver"]), + MethodProto::new("__aenter__", "PyAsyncAenterProtocol").has_self(), + MethodProto::new("__aexit__", "PyAsyncAexitProtocol") .args(&["ExcType", "ExcValue", "Traceback"]) .has_self(), ], py_methods: &[ - PyMethod::new( - "__aenter__", - "pyo3::class::pyasync::PyAsyncAenterProtocolImpl", - ), - PyMethod::new( - "__aexit__", - "pyo3::class::pyasync::PyAsyncAexitProtocolImpl", - ), + PyMethod::new("__aenter__", "PyAsyncAenterProtocolImpl"), + PyMethod::new("__aexit__", "PyAsyncAexitProtocolImpl"), ], - slot_getters: &[ - SlotGetter::new(&["__await__"], "get_await"), - SlotGetter::new(&["__aiter__"], "get_aiter"), - SlotGetter::new(&["__anext__"], "get_anext"), + slot_defs: &[ + SlotDef::new(&["__await__"], "Py_am_await", "await_"), + SlotDef::new(&["__aiter__"], "Py_am_aiter", "aiter"), + SlotDef::new(&["__anext__"], "Py_am_anext", "anext"), ], }; pub const BUFFER: Proto = Proto { name: "Buffer", - extension_trait: "pyo3::class::buffer::PyBufferSlots", + module: "pyo3::class::buffer", + slots_trait: "PyBufferProtocolSlots", + slots_trait_slots: "buffer_protocol_slots", methods: &[ - MethodProto::new( - "bf_getbuffer", - "pyo3::class::buffer::PyBufferGetBufferProtocol", - ) - .has_self(), - MethodProto::new( - "bf_releasebuffer", - "pyo3::class::buffer::PyBufferReleaseBufferProtocol", - ) - .has_self(), + MethodProto::new("bf_getbuffer", "PyBufferGetBufferProtocol").has_self(), + MethodProto::new("bf_releasebuffer", "PyBufferReleaseBufferProtocol").has_self(), ], py_methods: &[], - slot_getters: &[ - SlotGetter::new(&["bf_getbuffer"], "get_getbuffer"), - SlotGetter::new(&["bf_releasebuffer"], "get_releasebuffer"), + slot_defs: &[ + SlotDef::new(&["bf_getbuffer"], "Py_bf_getbuffer", "getbuffer"), + SlotDef::new( + &["bf_releasebuffer"], + "Py_bf_releasebuffer", + "releasebuffer", + ), ], }; pub const CONTEXT: Proto = Proto { name: "Context", - extension_trait: "", + module: "pyo3::class::context", + slots_trait: "", + slots_trait_slots: "", methods: &[ - MethodProto::new("__enter__", "pyo3::class::context::PyContextEnterProtocol").has_self(), - MethodProto::new("__exit__", "pyo3::class::context::PyContextExitProtocol") + MethodProto::new("__enter__", "PyContextEnterProtocol").has_self(), + MethodProto::new("__exit__", "PyContextExitProtocol") .args(&["ExcType", "ExcValue", "Traceback"]) .has_self(), ], py_methods: &[ - PyMethod::new( - "__enter__", - "pyo3::class::context::PyContextEnterProtocolImpl", - ), - PyMethod::new( - "__exit__", - "pyo3::class::context::PyContextExitProtocolImpl", - ), + PyMethod::new("__enter__", "PyContextEnterProtocolImpl"), + PyMethod::new("__exit__", "PyContextExitProtocolImpl"), ], - slot_getters: &[], + slot_defs: &[], }; pub const GC: Proto = Proto { name: "GC", - extension_trait: "pyo3::class::gc::PyGCSlots", + module: "pyo3::class::gc", + slots_trait: "PyGCProtocolSlots", + slots_trait_slots: "gc_protocol_slots", methods: &[ - MethodProto::new("__traverse__", "pyo3::class::gc::PyGCTraverseProtocol") + MethodProto::new("__traverse__", "PyGCTraverseProtocol") .has_self() .no_result(), - MethodProto::new("__clear__", "pyo3::class::gc::PyGCClearProtocol") + MethodProto::new("__clear__", "PyGCClearProtocol") .has_self() .no_result(), ], py_methods: &[], - slot_getters: &[ - SlotGetter::new(&["__traverse__"], "get_traverse"), - SlotGetter::new(&["__clear__"], "get_clear"), + slot_defs: &[ + SlotDef::new(&["__traverse__"], "Py_tp_traverse", "traverse"), + SlotDef::new(&["__clear__"], "Py_tp_clear", "clear"), ], }; pub const DESCR: Proto = Proto { name: "Descriptor", - extension_trait: "pyo3::class::descr::PyDescrSlots", + module: "pyo3::class::descr", + slots_trait: "PyDescrProtocolSlots", + slots_trait_slots: "descr_protocol_slots", methods: &[ - MethodProto::new("__get__", "pyo3::class::descr::PyDescrGetProtocol") - .args(&["Receiver", "Inst", "Owner"]), - MethodProto::new("__set__", "pyo3::class::descr::PyDescrSetProtocol") - .args(&["Receiver", "Inst", "Value"]), - MethodProto::new("__det__", "pyo3::class::descr::PyDescrDelProtocol") + MethodProto::new("__get__", "PyDescrGetProtocol").args(&["Receiver", "Inst", "Owner"]), + MethodProto::new("__set__", "PyDescrSetProtocol").args(&["Receiver", "Inst", "Value"]), + MethodProto::new("__det__", "PyDescrDelProtocol") .args(&["Inst"]) .has_self(), - MethodProto::new("__set_name__", "pyo3::class::descr::PyDescrSetNameProtocol") + MethodProto::new("__set_name__", "PyDescrSetNameProtocol") .args(&["Inst"]) .has_self(), ], py_methods: &[ - PyMethod::new("__del__", "pyo3::class::context::PyDescrDelProtocolImpl"), - PyMethod::new( - "__set_name__", - "pyo3::class::context::PyDescrNameProtocolImpl", - ), + PyMethod::new("__del__", "PyDescrDelProtocolImpl"), + PyMethod::new("__set_name__", "PyDescrNameProtocolImpl"), ], - slot_getters: &[ - SlotGetter::new(&["__get__"], "get_descr_get"), - SlotGetter::new(&["__set__"], "get_descr_set"), + slot_defs: &[ + SlotDef::new(&["__get__"], "Py_tp_descr_get", "descr_get"), + SlotDef::new(&["__set__"], "Py_tp_descr_set", "descr_set"), ], }; pub const ITER: Proto = Proto { name: "Iter", - extension_trait: "pyo3::class::iter::PyIterSlots", + module: "pyo3::class::iter", + slots_trait: "PyIterProtocolSlots", + slots_trait_slots: "iter_protocol_slots", py_methods: &[], methods: &[ - MethodProto::new("__iter__", "pyo3::class::iter::PyIterIterProtocol").args(&["Receiver"]), - MethodProto::new("__next__", "pyo3::class::iter::PyIterNextProtocol").args(&["Receiver"]), + MethodProto::new("__iter__", "PyIterIterProtocol").args(&["Receiver"]), + MethodProto::new("__next__", "PyIterNextProtocol").args(&["Receiver"]), ], - slot_getters: &[ - SlotGetter::new(&["__iter__"], "get_iter"), - SlotGetter::new(&["__next__"], "get_iternext"), + slot_defs: &[ + SlotDef::new(&["__iter__"], "Py_tp_iter", "iter"), + SlotDef::new(&["__next__"], "Py_tp_iternext", "iternext"), ], }; pub const MAPPING: Proto = Proto { name: "Mapping", - extension_trait: "pyo3::class::mapping::PyMappingSlots", + module: "pyo3::class::mapping", + slots_trait: "PyMappingProtocolSlots", + slots_trait_slots: "mapping_protocol_slots", methods: &[ - MethodProto::new("__len__", "pyo3::class::mapping::PyMappingLenProtocol").has_self(), - MethodProto::new( - "__getitem__", - "pyo3::class::mapping::PyMappingGetItemProtocol", - ) - .args(&["Key"]) - .has_self(), - MethodProto::new( - "__setitem__", - "pyo3::class::mapping::PyMappingSetItemProtocol", - ) - .args(&["Key", "Value"]) - .has_self(), - MethodProto::new( - "__delitem__", - "pyo3::class::mapping::PyMappingDelItemProtocol", - ) - .args(&["Key"]) - .has_self(), - MethodProto::new( - "__reversed__", - "pyo3::class::mapping::PyMappingReversedProtocol", - ) - .has_self(), + MethodProto::new("__len__", "PyMappingLenProtocol").has_self(), + MethodProto::new("__getitem__", "PyMappingGetItemProtocol") + .args(&["Key"]) + .has_self(), + MethodProto::new("__setitem__", "PyMappingSetItemProtocol") + .args(&["Key", "Value"]) + .has_self(), + MethodProto::new("__delitem__", "PyMappingDelItemProtocol") + .args(&["Key"]) + .has_self(), + MethodProto::new("__reversed__", "PyMappingReversedProtocol").has_self(), ], py_methods: &[PyMethod::new( "__reversed__", - "pyo3::class::mapping::PyMappingReversedProtocolImpl", + "PyMappingReversedProtocolImpl", )], - slot_getters: &[ - SlotGetter::new(&["__len__"], "get_len"), - SlotGetter::new(&["__getitem__"], "get_getitem"), - SlotGetter::new(&["__setitem__", "__delitem__"], "get_setdelitem"), - SlotGetter::new(&["__setitem__"], "get_setitem"), - SlotGetter::new(&["__delitem__"], "get_delitem"), + slot_defs: &[ + SlotDef::new(&["__len__"], "Py_mp_length", "len"), + SlotDef::new(&["__getitem__"], "Py_mp_subscript", "getitem"), + SlotDef::new( + &["__setitem__", "__delitem__"], + "Py_mp_ass_subscript", + "setdelitem", + ), + SlotDef::new(&["__setitem__"], "Py_mp_ass_subscript", "setitem"), + SlotDef::new(&["__delitem__"], "Py_mp_ass_subscript", "delitem"), ], }; pub const SEQ: Proto = Proto { name: "Sequence", - extension_trait: "pyo3::class::sequence::PySequenceSlots", + module: "pyo3::class::sequence", + slots_trait: "PySequenceProtocolSlots", + slots_trait_slots: "sequence_protocol_slots", methods: &[ - MethodProto::new("__len__", "pyo3::class::sequence::PySequenceLenProtocol").has_self(), - MethodProto::new( - "__getitem__", - "pyo3::class::sequence::PySequenceGetItemProtocol", - ) - .args(&["Index"]) - .has_self(), - MethodProto::new( - "__setitem__", - "pyo3::class::sequence::PySequenceSetItemProtocol", - ) - .args(&["Index", "Value"]) - .has_self(), - MethodProto::new( - "__delitem__", - "pyo3::class::sequence::PySequenceDelItemProtocol", - ) - .args(&["Index"]) - .has_self(), - MethodProto::new( - "__contains__", - "pyo3::class::sequence::PySequenceContainsProtocol", - ) - .args(&["Item"]) - .has_self(), - MethodProto::new( - "__concat__", - "pyo3::class::sequence::PySequenceConcatProtocol", - ) - .args(&["Other"]) - .has_self(), - MethodProto::new( - "__repeat__", - "pyo3::class::sequence::PySequenceRepeatProtocol", - ) - .args(&["Index"]) - .has_self(), - MethodProto::new( - "__inplace_concat__", - "pyo3::class::sequence::PySequenceInplaceConcatProtocol", - ) - .args(&["Other"]) - .has_self(), - MethodProto::new( - "__inplace_repeat__", - "pyo3::class::sequence::PySequenceInplaceRepeatProtocol", - ) - .args(&["Index"]) - .has_self(), + MethodProto::new("__len__", "PySequenceLenProtocol").has_self(), + MethodProto::new("__getitem__", "PySequenceGetItemProtocol") + .args(&["Index"]) + .has_self(), + MethodProto::new("__setitem__", "PySequenceSetItemProtocol") + .args(&["Index", "Value"]) + .has_self(), + MethodProto::new("__delitem__", "PySequenceDelItemProtocol") + .args(&["Index"]) + .has_self(), + MethodProto::new("__contains__", "PySequenceContainsProtocol") + .args(&["Item"]) + .has_self(), + MethodProto::new("__concat__", "PySequenceConcatProtocol") + .args(&["Other"]) + .has_self(), + MethodProto::new("__repeat__", "PySequenceRepeatProtocol") + .args(&["Index"]) + .has_self(), + MethodProto::new("__inplace_concat__", "PySequenceInplaceConcatProtocol") + .args(&["Other"]) + .has_self(), + MethodProto::new("__inplace_repeat__", "PySequenceInplaceRepeatProtocol") + .args(&["Index"]) + .has_self(), ], py_methods: &[], - slot_getters: &[ - SlotGetter::new(&["__len__"], "get_len"), - SlotGetter::new(&["__concat__"], "get_concat"), - SlotGetter::new(&["__repeat__"], "get_repeat"), - SlotGetter::new(&["__getitem__"], "get_getitem"), - SlotGetter::new(&["__setitem__", "__delitem__"], "get_setdelitem"), - SlotGetter::new(&["__setitem__"], "get_setitem"), - SlotGetter::new(&["__delitem__"], "get_delitem"), - SlotGetter::new(&["__contains__"], "get_contains"), - SlotGetter::new(&["__inplace_concat__"], "get_inplace_concat"), - SlotGetter::new(&["__inplace_repeat__"], "get_inplace_repeat"), + slot_defs: &[ + SlotDef::new(&["__len__"], "Py_sq_length", "len"), + SlotDef::new(&["__concat__"], "Py_sq_concat", "concat"), + SlotDef::new(&["__repeat__"], "Py_sq_repeat", "repeat"), + SlotDef::new(&["__getitem__"], "Py_sq_item", "getitem"), + SlotDef::new( + &["__setitem__", "__delitem__"], + "Py_sq_ass_item", + "setdelitem", + ), + SlotDef::new(&["__setitem__"], "Py_sq_ass_item", "setitem"), + SlotDef::new(&["__delitem__"], "Py_sq_ass_item", "delitem"), + SlotDef::new(&["__contains__"], "Py_sq_contains", "contains"), + SlotDef::new( + &["__inplace_concat__"], + "Py_sq_inplace_concat", + "inplace_concat", + ), + SlotDef::new( + &["__inplace_repeat__"], + "Py_sq_inplace_repeat", + "inplace_repeat", + ), ], }; pub const NUM: Proto = Proto { name: "Number", - extension_trait: "pyo3::class::number::PyNumberSlots", + module: "pyo3::class::number", + slots_trait: "PyNumberProtocolSlots", + slots_trait_slots: "number_protocol_slots", methods: &[ - MethodProto::new("__add__", "pyo3::class::number::PyNumberAddProtocol") - .args(&["Left", "Right"]), - MethodProto::new("__sub__", "pyo3::class::number::PyNumberSubProtocol") - .args(&["Left", "Right"]), - MethodProto::new("__mul__", "pyo3::class::number::PyNumberMulProtocol") - .args(&["Left", "Right"]), - MethodProto::new("__matmul__", "pyo3::class::number::PyNumberMatmulProtocol") - .args(&["Left", "Right"]), - MethodProto::new( - "__truediv__", - "pyo3::class::number::PyNumberTruedivProtocol", - ) - .args(&["Left", "Right"]), - MethodProto::new( - "__floordiv__", - "pyo3::class::number::PyNumberFloordivProtocol", - ) - .args(&["Left", "Right"]), - MethodProto::new("__mod__", "pyo3::class::number::PyNumberModProtocol") - .args(&["Left", "Right"]), - MethodProto::new("__divmod__", "pyo3::class::number::PyNumberDivmodProtocol") - .args(&["Left", "Right"]), - MethodProto::new("__pow__", "pyo3::class::number::PyNumberPowProtocol") - .args(&["Left", "Right", "Modulo"]), - MethodProto::new("__lshift__", "pyo3::class::number::PyNumberLShiftProtocol") - .args(&["Left", "Right"]), - MethodProto::new("__rshift__", "pyo3::class::number::PyNumberRShiftProtocol") - .args(&["Left", "Right"]), - MethodProto::new("__and__", "pyo3::class::number::PyNumberAndProtocol") - .args(&["Left", "Right"]), - MethodProto::new("__xor__", "pyo3::class::number::PyNumberXorProtocol") - .args(&["Left", "Right"]), - MethodProto::new("__or__", "pyo3::class::number::PyNumberOrProtocol") - .args(&["Left", "Right"]), - MethodProto::new("__radd__", "pyo3::class::number::PyNumberRAddProtocol") + MethodProto::new("__add__", "PyNumberAddProtocol").args(&["Left", "Right"]), + MethodProto::new("__sub__", "PyNumberSubProtocol").args(&["Left", "Right"]), + MethodProto::new("__mul__", "PyNumberMulProtocol").args(&["Left", "Right"]), + MethodProto::new("__matmul__", "PyNumberMatmulProtocol").args(&["Left", "Right"]), + MethodProto::new("__truediv__", "PyNumberTruedivProtocol").args(&["Left", "Right"]), + MethodProto::new("__floordiv__", "PyNumberFloordivProtocol").args(&["Left", "Right"]), + MethodProto::new("__mod__", "PyNumberModProtocol").args(&["Left", "Right"]), + MethodProto::new("__divmod__", "PyNumberDivmodProtocol").args(&["Left", "Right"]), + MethodProto::new("__pow__", "PyNumberPowProtocol").args(&["Left", "Right", "Modulo"]), + MethodProto::new("__lshift__", "PyNumberLShiftProtocol").args(&["Left", "Right"]), + MethodProto::new("__rshift__", "PyNumberRShiftProtocol").args(&["Left", "Right"]), + MethodProto::new("__and__", "PyNumberAndProtocol").args(&["Left", "Right"]), + MethodProto::new("__xor__", "PyNumberXorProtocol").args(&["Left", "Right"]), + MethodProto::new("__or__", "PyNumberOrProtocol").args(&["Left", "Right"]), + MethodProto::new("__radd__", "PyNumberRAddProtocol") .args(&["Other"]) .has_self(), - MethodProto::new("__rsub__", "pyo3::class::number::PyNumberRSubProtocol") + MethodProto::new("__rsub__", "PyNumberRSubProtocol") .args(&["Other"]) .has_self(), - MethodProto::new("__rmul__", "pyo3::class::number::PyNumberRMulProtocol") + MethodProto::new("__rmul__", "PyNumberRMulProtocol") .args(&["Other"]) .has_self(), - MethodProto::new( - "__rmatmul__", - "pyo3::class::number::PyNumberRMatmulProtocol", - ) - .args(&["Other"]) - .has_self(), - MethodProto::new( - "__rtruediv__", - "pyo3::class::number::PyNumberRTruedivProtocol", - ) - .args(&["Other"]) - .has_self(), - MethodProto::new( - "__rfloordiv__", - "pyo3::class::number::PyNumberRFloordivProtocol", - ) - .args(&["Other"]) - .has_self(), - MethodProto::new("__rmod__", "pyo3::class::number::PyNumberRModProtocol") + MethodProto::new("__rmatmul__", "PyNumberRMatmulProtocol") .args(&["Other"]) .has_self(), - MethodProto::new( - "__rdivmod__", - "pyo3::class::number::PyNumberRDivmodProtocol", - ) - .args(&["Other"]) - .has_self(), - MethodProto::new("__rpow__", "pyo3::class::number::PyNumberRPowProtocol") + MethodProto::new("__rtruediv__", "PyNumberRTruedivProtocol") + .args(&["Other"]) + .has_self(), + MethodProto::new("__rfloordiv__", "PyNumberRFloordivProtocol") + .args(&["Other"]) + .has_self(), + MethodProto::new("__rmod__", "PyNumberRModProtocol") + .args(&["Other"]) + .has_self(), + MethodProto::new("__rdivmod__", "PyNumberRDivmodProtocol") + .args(&["Other"]) + .has_self(), + MethodProto::new("__rpow__", "PyNumberRPowProtocol") .args(&["Other", "Modulo"]) .has_self(), - MethodProto::new( - "__rlshift__", - "pyo3::class::number::PyNumberRLShiftProtocol", - ) - .args(&["Other"]) - .has_self(), - MethodProto::new( - "__rrshift__", - "pyo3::class::number::PyNumberRRShiftProtocol", - ) - .args(&["Other"]) - .has_self(), - MethodProto::new("__rand__", "pyo3::class::number::PyNumberRAndProtocol") + MethodProto::new("__rlshift__", "PyNumberRLShiftProtocol") .args(&["Other"]) .has_self(), - MethodProto::new("__rxor__", "pyo3::class::number::PyNumberRXorProtocol") + MethodProto::new("__rrshift__", "PyNumberRRShiftProtocol") .args(&["Other"]) .has_self(), - MethodProto::new("__ror__", "pyo3::class::number::PyNumberROrProtocol") + MethodProto::new("__rand__", "PyNumberRAndProtocol") .args(&["Other"]) .has_self(), - MethodProto::new("__iadd__", "pyo3::class::number::PyNumberIAddProtocol") + MethodProto::new("__rxor__", "PyNumberRXorProtocol") .args(&["Other"]) .has_self(), - MethodProto::new("__isub__", "pyo3::class::number::PyNumberISubProtocol") + MethodProto::new("__ror__", "PyNumberROrProtocol") .args(&["Other"]) .has_self(), - MethodProto::new("__imul__", "pyo3::class::number::PyNumberIMulProtocol") + MethodProto::new("__iadd__", "PyNumberIAddProtocol") .args(&["Other"]) .has_self(), - MethodProto::new( - "__imatmul__", - "pyo3::class::number::PyNumberIMatmulProtocol", - ) - .args(&["Other"]) - .has_self(), - MethodProto::new( - "__itruediv__", - "pyo3::class::number::PyNumberITruedivProtocol", - ) - .args(&["Other"]) - .has_self(), - MethodProto::new( - "__ifloordiv__", - "pyo3::class::number::PyNumberIFloordivProtocol", - ) - .args(&["Other"]) - .has_self(), - MethodProto::new("__imod__", "pyo3::class::number::PyNumberIModProtocol") + MethodProto::new("__isub__", "PyNumberISubProtocol") .args(&["Other"]) .has_self(), - MethodProto::new("__ipow__", "pyo3::class::number::PyNumberIPowProtocol") + MethodProto::new("__imul__", "PyNumberIMulProtocol") .args(&["Other"]) .has_self(), - MethodProto::new( - "__ilshift__", - "pyo3::class::number::PyNumberILShiftProtocol", - ) - .args(&["Other"]) - .has_self(), - MethodProto::new( - "__irshift__", - "pyo3::class::number::PyNumberIRShiftProtocol", - ) - .args(&["Other"]) - .has_self(), - MethodProto::new("__iand__", "pyo3::class::number::PyNumberIAndProtocol") + MethodProto::new("__imatmul__", "PyNumberIMatmulProtocol") .args(&["Other"]) .has_self(), - MethodProto::new("__ixor__", "pyo3::class::number::PyNumberIXorProtocol") + MethodProto::new("__itruediv__", "PyNumberITruedivProtocol") .args(&["Other"]) .has_self(), - MethodProto::new("__ior__", "pyo3::class::number::PyNumberIOrProtocol") + MethodProto::new("__ifloordiv__", "PyNumberIFloordivProtocol") .args(&["Other"]) .has_self(), - MethodProto::new("__neg__", "pyo3::class::number::PyNumberNegProtocol").has_self(), - MethodProto::new("__pos__", "pyo3::class::number::PyNumberPosProtocol").has_self(), - MethodProto::new("__abs__", "pyo3::class::number::PyNumberAbsProtocol").has_self(), - MethodProto::new("__invert__", "pyo3::class::number::PyNumberInvertProtocol").has_self(), - MethodProto::new( - "__complex__", - "pyo3::class::number::PyNumberComplexProtocol", - ) - .has_self(), - MethodProto::new("__int__", "pyo3::class::number::PyNumberIntProtocol").has_self(), - MethodProto::new("__float__", "pyo3::class::number::PyNumberFloatProtocol").has_self(), - MethodProto::new("__index__", "pyo3::class::number::PyNumberIndexProtocol").has_self(), - MethodProto::new("__round__", "pyo3::class::number::PyNumberRoundProtocol") + MethodProto::new("__imod__", "PyNumberIModProtocol") + .args(&["Other"]) + .has_self(), + MethodProto::new("__ipow__", "PyNumberIPowProtocol") + .args(&["Other"]) + .has_self(), + MethodProto::new("__ilshift__", "PyNumberILShiftProtocol") + .args(&["Other"]) + .has_self(), + MethodProto::new("__irshift__", "PyNumberIRShiftProtocol") + .args(&["Other"]) + .has_self(), + MethodProto::new("__iand__", "PyNumberIAndProtocol") + .args(&["Other"]) + .has_self(), + MethodProto::new("__ixor__", "PyNumberIXorProtocol") + .args(&["Other"]) + .has_self(), + MethodProto::new("__ior__", "PyNumberIOrProtocol") + .args(&["Other"]) + .has_self(), + MethodProto::new("__neg__", "PyNumberNegProtocol").has_self(), + MethodProto::new("__pos__", "PyNumberPosProtocol").has_self(), + MethodProto::new("__abs__", "PyNumberAbsProtocol").has_self(), + MethodProto::new("__invert__", "PyNumberInvertProtocol").has_self(), + MethodProto::new("__complex__", "PyNumberComplexProtocol").has_self(), + MethodProto::new("__int__", "PyNumberIntProtocol").has_self(), + MethodProto::new("__float__", "PyNumberFloatProtocol").has_self(), + MethodProto::new("__index__", "PyNumberIndexProtocol").has_self(), + MethodProto::new("__round__", "PyNumberRoundProtocol") .args(&["NDigits"]) .has_self(), ], py_methods: &[ - PyMethod::coexist("__radd__", "pyo3::class::number::PyNumberRAddProtocolImpl"), - PyMethod::coexist("__rsub__", "pyo3::class::number::PyNumberRSubProtocolImpl"), - PyMethod::coexist("__rmul__", "pyo3::class::number::PyNumberRMulProtocolImpl"), - PyMethod::coexist( - "__rmatmul__", - "pyo3::class::number::PyNumberRMatmulProtocolImpl", - ), - PyMethod::coexist( - "__rtruediv__", - "pyo3::class::number::PyNumberRTruedivProtocolImpl", - ), - PyMethod::coexist( - "__rfloordiv__", - "pyo3::class::number::PyNumberRFloordivProtocolImpl", - ), - PyMethod::coexist("__rmod__", "pyo3::class::number::PyNumberRModProtocolImpl"), - PyMethod::coexist( - "__rdivmod__", - "pyo3::class::number::PyNumberRDivmodProtocolImpl", - ), - PyMethod::coexist("__rpow__", "pyo3::class::number::PyNumberRPowProtocolImpl"), - PyMethod::coexist( - "__rlshift__", - "pyo3::class::number::PyNumberRLShiftProtocolImpl", - ), - PyMethod::coexist( - "__rrshift__", - "pyo3::class::number::PyNumberRRShiftProtocolImpl", - ), - PyMethod::coexist("__rand__", "pyo3::class::number::PyNumberRAndProtocolImpl"), - PyMethod::coexist("__rxor__", "pyo3::class::number::PyNumberRXorProtocolImpl"), - PyMethod::coexist("__ror__", "pyo3::class::number::PyNumberROrProtocolImpl"), - PyMethod::new( - "__complex__", - "pyo3::class::number::PyNumberComplexProtocolImpl", - ), - PyMethod::new( - "__round__", - "pyo3::class::number::PyNumberRoundProtocolImpl", - ), + PyMethod::coexist("__radd__", "PyNumberRAddProtocolImpl"), + PyMethod::coexist("__rsub__", "PyNumberRSubProtocolImpl"), + PyMethod::coexist("__rmul__", "PyNumberRMulProtocolImpl"), + PyMethod::coexist("__rmatmul__", "PyNumberRMatmulProtocolImpl"), + PyMethod::coexist("__rtruediv__", "PyNumberRTruedivProtocolImpl"), + PyMethod::coexist("__rfloordiv__", "PyNumberRFloordivProtocolImpl"), + PyMethod::coexist("__rmod__", "PyNumberRModProtocolImpl"), + PyMethod::coexist("__rdivmod__", "PyNumberRDivmodProtocolImpl"), + PyMethod::coexist("__rpow__", "PyNumberRPowProtocolImpl"), + PyMethod::coexist("__rlshift__", "PyNumberRLShiftProtocolImpl"), + PyMethod::coexist("__rrshift__", "PyNumberRRShiftProtocolImpl"), + PyMethod::coexist("__rand__", "PyNumberRAndProtocolImpl"), + PyMethod::coexist("__rxor__", "PyNumberRXorProtocolImpl"), + PyMethod::coexist("__ror__", "PyNumberROrProtocolImpl"), + PyMethod::new("__complex__", "PyNumberComplexProtocolImpl"), + PyMethod::new("__round__", "PyNumberRoundProtocolImpl"), ], - slot_getters: &[ - SlotGetter::new(&["__add__", "__radd__"], "get_add_radd"), - SlotGetter::new(&["__add__"], "get_add"), - SlotGetter::new(&["__radd__"], "get_radd"), - SlotGetter::new(&["__sub__", "__rsub__"], "get_sub_rsub"), - SlotGetter::new(&["__sub__"], "get_sub"), - SlotGetter::new(&["__rsub__"], "get_rsub"), - SlotGetter::new(&["__mul__", "__rmul__"], "get_mul_rmul"), - SlotGetter::new(&["__mul__"], "get_mul"), - SlotGetter::new(&["__rmul__"], "get_rmul"), - SlotGetter::new(&["__mod__"], "get_mod"), - SlotGetter::new(&["__divmod__", "__rdivmod__"], "get_divmod_rdivmod"), - SlotGetter::new(&["__divmod__"], "get_divmod"), - SlotGetter::new(&["__rdivmod__"], "get_rdivmod"), - SlotGetter::new(&["__pow__", "__rpow__"], "get_pow_rpow"), - SlotGetter::new(&["__pow__"], "get_pow"), - SlotGetter::new(&["__rpow__"], "get_rpow"), - SlotGetter::new(&["__neg__"], "get_neg"), - SlotGetter::new(&["__pos__"], "get_pos"), - SlotGetter::new(&["__abs__"], "get_abs"), - SlotGetter::new(&["__invert__"], "get_invert"), - SlotGetter::new(&["__lshift__", "__rlshift__"], "get_lshift_rlshift"), - SlotGetter::new(&["__lshift__"], "get_lshift"), - SlotGetter::new(&["__rlshift__"], "get_rlshift"), - SlotGetter::new(&["__rshift__", "__rrshift__"], "get_rshift_rrshift"), - SlotGetter::new(&["__rshift__"], "get_rshift"), - SlotGetter::new(&["__rrshift__"], "get_rrshift"), - SlotGetter::new(&["__and__", "__rand__"], "get_and_rand"), - SlotGetter::new(&["__and__"], "get_and"), - SlotGetter::new(&["__rand__"], "get_rand"), - SlotGetter::new(&["__xor__", "__rxor__"], "get_xor_rxor"), - SlotGetter::new(&["__xor__"], "get_xor"), - SlotGetter::new(&["__rxor__"], "get_rxor"), - SlotGetter::new(&["__or__", "__ror__"], "get_or_ror"), - SlotGetter::new(&["__or__"], "get_or"), - SlotGetter::new(&["__ror__"], "get_ror"), - SlotGetter::new(&["__int__"], "get_int"), - SlotGetter::new(&["__float__"], "get_float"), - SlotGetter::new(&["__iadd__"], "get_iadd"), - SlotGetter::new(&["__isub__"], "get_isub"), - SlotGetter::new(&["__imul__"], "get_imul"), - SlotGetter::new(&["__imod__"], "get_imod"), - SlotGetter::new(&["__ipow__"], "get_ipow"), - SlotGetter::new(&["__ilshift__"], "get_ilshift"), - SlotGetter::new(&["__irshift__"], "get_irshift"), - SlotGetter::new(&["__iand__"], "get_iand"), - SlotGetter::new(&["__ixor__"], "get_ixor"), - SlotGetter::new(&["__ior__"], "get_ior"), - SlotGetter::new(&["__floordiv__", "__rfloordiv__"], "get_floordiv_rfloordiv"), - SlotGetter::new(&["__floordiv__"], "get_floordiv"), - SlotGetter::new(&["__rfloordiv__"], "get_rfloordiv"), - SlotGetter::new(&["__truediv__", "__rtruediv__"], "get_truediv_rtruediv"), - SlotGetter::new(&["__truediv__"], "get_truediv"), - SlotGetter::new(&["__rtruediv__"], "get_rtruediv"), - SlotGetter::new(&["__ifloordiv__"], "get_ifloordiv"), - SlotGetter::new(&["__itruediv__"], "get_itruediv"), - SlotGetter::new(&["__index__"], "get_index"), - SlotGetter::new(&["__matmul__", "__rmatmul__"], "get_matmul_rmatmul"), - SlotGetter::new(&["__matmul__"], "get_matmul"), - SlotGetter::new(&["__rmatmul__"], "get_rmatmul"), - SlotGetter::new(&["__imatmul__"], "get_imatmul"), + slot_defs: &[ + SlotDef::new(&["__add__", "__radd__"], "Py_nb_add", "add_radd"), + SlotDef::new(&["__add__"], "Py_nb_add", "add"), + SlotDef::new(&["__radd__"], "Py_nb_add", "radd"), + SlotDef::new(&["__sub__", "__rsub__"], "Py_nb_subtract", "sub_rsub"), + SlotDef::new(&["__sub__"], "Py_nb_subtract", "sub"), + SlotDef::new(&["__rsub__"], "Py_nb_subtract", "rsub"), + SlotDef::new(&["__mul__", "__rmul__"], "Py_nb_multiply", "mul_rmul"), + SlotDef::new(&["__mul__"], "Py_nb_multiply", "mul"), + SlotDef::new(&["__rmul__"], "Py_nb_multiply", "rmul"), + SlotDef::new(&["__mod__"], "Py_nb_remainder", "mod_"), + SlotDef::new( + &["__divmod__", "__rdivmod__"], + "Py_nb_divmod", + "divmod_rdivmod", + ), + SlotDef::new(&["__divmod__"], "Py_nb_divmod", "divmod"), + SlotDef::new(&["__rdivmod__"], "Py_nb_divmod", "rdivmod"), + SlotDef::new(&["__pow__", "__rpow__"], "Py_nb_power", "pow_rpow"), + SlotDef::new(&["__pow__"], "Py_nb_power", "pow"), + SlotDef::new(&["__rpow__"], "Py_nb_power", "rpow"), + SlotDef::new(&["__neg__"], "Py_nb_negative", "neg"), + SlotDef::new(&["__pos__"], "Py_nb_positive", "pos"), + SlotDef::new(&["__abs__"], "Py_nb_absolute", "abs"), + SlotDef::new(&["__invert__"], "Py_nb_invert", "invert"), + SlotDef::new( + &["__lshift__", "__rlshift__"], + "Py_nb_lshift", + "lshift_rlshift", + ), + SlotDef::new(&["__lshift__"], "Py_nb_lshift", "lshift"), + SlotDef::new(&["__rlshift__"], "Py_nb_lshift", "rlshift"), + SlotDef::new( + &["__rshift__", "__rrshift__"], + "Py_nb_rshift", + "rshift_rrshift", + ), + SlotDef::new(&["__rshift__"], "Py_nb_rshift", "rshift"), + SlotDef::new(&["__rrshift__"], "Py_nb_rshift", "rrshift"), + SlotDef::new(&["__and__", "__rand__"], "Py_nb_and", "and_rand"), + SlotDef::new(&["__and__"], "Py_nb_and", "and"), + SlotDef::new(&["__rand__"], "Py_nb_and", "rand"), + SlotDef::new(&["__xor__", "__rxor__"], "Py_nb_xor", "xor_rxor"), + SlotDef::new(&["__xor__"], "Py_nb_xor", "xor"), + SlotDef::new(&["__rxor__"], "Py_nb_xor", "rxor"), + SlotDef::new(&["__or__", "__ror__"], "Py_nb_or", "or_ror"), + SlotDef::new(&["__or__"], "Py_nb_or", "or"), + SlotDef::new(&["__ror__"], "Py_nb_or", "ror"), + SlotDef::new(&["__int__"], "Py_nb_int", "int"), + SlotDef::new(&["__float__"], "Py_nb_float", "float"), + SlotDef::new(&["__iadd__"], "Py_nb_inplace_add", "iadd"), + SlotDef::new(&["__isub__"], "Py_nb_inplace_subtract", "isub"), + SlotDef::new(&["__imul__"], "Py_nb_inplace_multiply", "imul"), + SlotDef::new(&["__imod__"], "Py_nb_inplace_remainder", "imod"), + SlotDef::new(&["__ipow__"], "Py_nb_inplace_power", "ipow"), + SlotDef::new(&["__ilshift__"], "Py_nb_inplace_lshift", "ilshift"), + SlotDef::new(&["__irshift__"], "Py_nb_inplace_rshift", "irshift"), + SlotDef::new(&["__iand__"], "Py_nb_inplace_and", "iand"), + SlotDef::new(&["__ixor__"], "Py_nb_inplace_xor", "ixor"), + SlotDef::new(&["__ior__"], "Py_nb_inplace_or", "ior"), + SlotDef::new( + &["__floordiv__", "__rfloordiv__"], + "Py_nb_floor_divide", + "floordiv_rfloordiv", + ), + SlotDef::new(&["__floordiv__"], "Py_nb_floor_divide", "floordiv"), + SlotDef::new(&["__rfloordiv__"], "Py_nb_floor_divide", "rfloordiv"), + SlotDef::new( + &["__truediv__", "__rtruediv__"], + "Py_nb_true_divide", + "truediv_rtruediv", + ), + SlotDef::new(&["__truediv__"], "Py_nb_true_divide", "truediv"), + SlotDef::new(&["__rtruediv__"], "Py_nb_true_divide", "rtruediv"), + SlotDef::new( + &["__ifloordiv__"], + "Py_nb_inplace_floor_divide", + "ifloordiv", + ), + SlotDef::new(&["__itruediv__"], "Py_nb_inplace_true_divide", "itruediv"), + SlotDef::new(&["__index__"], "Py_nb_index", "index"), + SlotDef::new( + &["__matmul__", "__rmatmul__"], + "Py_nb_matrix_multiply", + "matmul_rmatmul", + ), + SlotDef::new(&["__matmul__"], "Py_nb_matrix_multiply", "matmul"), + SlotDef::new(&["__rmatmul__"], "Py_nb_matrix_multiply", "rmatmul"), + SlotDef::new(&["__imatmul__"], "Py_nb_inplace_matrix_multiply", "imatmul"), ], }; diff --git a/pyo3-macros-backend/src/proto_method.rs b/pyo3-macros-backend/src/proto_method.rs index 66279706..e62fd983 100644 --- a/pyo3-macros-backend/src/proto_method.rs +++ b/pyo3-macros-backend/src/proto_method.rs @@ -43,9 +43,10 @@ impl MethodProto { pub(crate) fn impl_method_proto( cls: &syn::Type, sig: &mut syn::Signature, + module: &syn::Path, meth: &MethodProto, ) -> syn::Result { - let p: syn::Path = syn::parse_str(meth.proto).unwrap(); + let proto: syn::Path = syn::parse_str(meth.proto).unwrap(); let mut impl_types = Vec::new(); for (i, arg) in meth.args.iter().enumerate() { @@ -55,8 +56,8 @@ pub(crate) fn impl_method_proto( impl_types.push(quote! {type #arg_name = #arg_ty;}); - let type1 = syn::parse_quote! { arg: <#cls as #p<'p>>::#arg_name}; - let type2 = syn::parse_quote! { arg: Option<<#cls as #p<'p>>::#arg_name>}; + let type1 = syn::parse_quote! { arg: <#cls as #module::#proto<'p>>::#arg_name}; + let type2 = syn::parse_quote! { arg: Option<<#cls as #module::#proto<'p>>::#arg_name>}; modify_arg_ty(sig, idx, &type1, &type2)?; } @@ -74,14 +75,14 @@ pub(crate) fn impl_method_proto( } }; - sig.output = syn::parse_quote! { -> <#cls as #p<'p>>::Result }; + sig.output = syn::parse_quote! { -> <#cls as #module::#proto<'p>>::Result }; quote! { type Result = #ret_ty; } } else { proc_macro2::TokenStream::new() }; Ok(quote! { - impl<'p> #p<'p> for #cls { + impl<'p> #module::#proto<'p> for #cls { #(#impl_types)* #res_type_def } diff --git a/pyo3-macros-backend/src/pyclass.rs b/pyo3-macros-backend/src/pyclass.rs index 57669ad5..77f76eb4 100644 --- a/pyo3-macros-backend/src/pyclass.rs +++ b/pyo3-macros-backend/src/pyclass.rs @@ -254,34 +254,6 @@ fn impl_methods_inventory(cls: &syn::Ident) -> TokenStream { } } -/// Implement `HasProtoInventory` for the class for lazy protocol initialization. -fn impl_proto_inventory(cls: &syn::Ident) -> TokenStream { - // Try to build a unique type for better error messages - let name = format!("Pyo3ProtoInventoryFor{}", cls); - let inventory_cls = syn::Ident::new(&name, Span::call_site()); - - quote! { - #[doc(hidden)] - pub struct #inventory_cls { - def: pyo3::class::proto_methods::PyProtoMethodDef, - } - impl pyo3::class::proto_methods::PyProtoInventory for #inventory_cls { - fn new(def: pyo3::class::proto_methods::PyProtoMethodDef) -> Self { - Self { def } - } - fn get(&'static self) -> &'static pyo3::class::proto_methods::PyProtoMethodDef { - &self.def - } - } - - impl pyo3::class::proto_methods::HasProtoInventory for #cls { - type ProtoMethods = #inventory_cls; - } - - pyo3::inventory::collect!(#inventory_cls); - } -} - fn get_class_python_name<'a>(cls: &'a syn::Ident, attr: &'a PyClassArgs) -> &'a syn::Ident { attr.name.as_ref().unwrap_or(cls) } @@ -383,7 +355,6 @@ fn impl_class( }; let impl_inventory = impl_methods_inventory(&cls); - let impl_proto_inventory = impl_proto_inventory(&cls); let base = &attr.base; let flags = &attr.flags; @@ -472,7 +443,32 @@ fn impl_class( #impl_inventory - #impl_proto_inventory + impl pyo3::class::proto_methods::PyProtoMethods for #cls { + fn for_each_proto_slot(visitor: Visitor) { + // Implementation which uses dtolnay specialization to load all slots. + use pyo3::class::proto_methods::*; + let protocols = PyClassProtocols::<#cls>::new(); + protocols.object_protocol_slots() + .iter() + .chain(protocols.number_protocol_slots()) + .chain(protocols.iter_protocol_slots()) + .chain(protocols.gc_protocol_slots()) + .chain(protocols.descr_protocol_slots()) + .chain(protocols.mapping_protocol_slots()) + .chain(protocols.sequence_protocol_slots()) + .chain(protocols.async_protocol_slots()) + .chain(protocols.buffer_protocol_slots()) + .cloned() + .for_each(visitor); + } + + fn get_buffer() -> Option<&'static pyo3::class::proto_methods::PyBufferProcs> { + use pyo3::class::proto_methods::*; + let protocols = PyClassProtocols::<#cls>::new(); + protocols.buffer_procs() + } + } + #extra diff --git a/pyo3-macros-backend/src/pyproto.rs b/pyo3-macros-backend/src/pyproto.rs index e7e2bd64..686ea68d 100644 --- a/pyo3-macros-backend/src/pyproto.rs +++ b/pyo3-macros-backend/src/pyproto.rs @@ -62,12 +62,13 @@ fn impl_proto_impl( let mut trait_impls = TokenStream::new(); let mut py_methods = Vec::new(); let mut method_names = HashSet::new(); + let module = proto.module(); for iimpl in impls.iter_mut() { if let syn::ImplItem::Method(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); + impl_method_proto(ty, &mut met.sig, &module, m)?.to_tokens(&mut trait_impls); // Insert the method to the HashSet method_names.insert(met.sig.ident.to_string()); } @@ -107,7 +108,7 @@ fn impl_proto_impl( } } let normal_methods = submit_normal_methods(py_methods, ty); - let protocol_methods = submit_protocol_methods(method_names, ty, proto)?; + let protocol_methods = impl_proto_methods(method_names, ty, proto); Ok(quote! { #trait_impls #normal_methods @@ -129,49 +130,69 @@ fn submit_normal_methods(py_methods: Vec, ty: &syn::Type) -> TokenS } } -fn submit_protocol_methods( +fn impl_proto_methods( method_names: HashSet, ty: &syn::Type, proto: &defs::Proto, -) -> syn::Result { - if proto.extension_trait == "" { - return Ok(quote! {}); +) -> TokenStream { + if proto.slots_trait.is_empty() { + return TokenStream::default(); } - let ext_trait: syn::Path = syn::parse_str(proto.extension_trait)?; - let mut tokens = vec![]; + + let module = proto.module(); + let slots_trait = syn::Ident::new(proto.slots_trait, Span::call_site()); + let slots_trait_slots = syn::Ident::new(proto.slots_trait_slots, Span::call_site()); + + let mut maybe_buffer_methods = None; if proto.name == "Buffer" { - // For buffer, we construct `PyProtoMethods` from PyBufferProcs - tokens.push(quote! { - let mut proto_methods = pyo3::ffi::PyBufferProcs::default(); + // On Python 3.9 we have to use PyBufferProcs to set buffer slots. + // For now we emit this always for buffer methods, even on 3.9+. + // Maybe in the future we can access Py_3_9 here and define it. + maybe_buffer_methods = Some(quote! { + impl pyo3::class::proto_methods::PyBufferProtocolProcs<#ty> + for pyo3::class::proto_methods::PyClassProtocols<#ty> + { + fn buffer_procs( + self + ) -> Option<&'static pyo3::class::proto_methods::PyBufferProcs> { + static PROCS: pyo3::class::proto_methods::PyBufferProcs + = pyo3::class::proto_methods::PyBufferProcs { + bf_getbuffer: Some(pyo3::class::buffer::getbuffer::<#ty>), + bf_releasebuffer: Some(pyo3::class::buffer::releasebuffer::<#ty>), + }; + Some(&PROCS) + } + } }); - for getter in proto.slot_getters(method_names) { - let get = syn::Ident::new(getter, Span::call_site()); - let field = syn::Ident::new(&format!("bf_{}", &getter[4..]), Span::call_site()); - tokens.push(quote! { proto_methods.#field = Some(<#ty as #ext_trait>::#get()); }); - } - } else { - // For other protocols, we construct `PyProtoMethods` from Vec - tokens.push(quote! { let mut proto_methods = vec![]; }); - for getter in proto.slot_getters(method_names) { - let get = syn::Ident::new(getter, Span::call_site()); - tokens.push(quote! { - let slot = <#ty as #ext_trait>::#get(); - proto_methods.push(pyo3::ffi::PyType_Slot { slot: slot.0, pfunc: slot.1 as _ }); - }); - } - }; - if tokens.len() <= 1 { - return Ok(quote! {}); } - Ok(quote! { - pyo3::inventory::submit! { - #![crate = pyo3] { - type Inventory = - <#ty as pyo3::class::proto_methods::HasProtoInventory>::ProtoMethods; - ::new( - { #(#tokens)* proto_methods.into() } - ) + + let mut tokens = proto + .slot_defs(method_names) + .map(|def| { + let slot = syn::Ident::new(def.slot, Span::call_site()); + let slot_impl = syn::Ident::new(def.slot_impl, Span::call_site()); + quote! {{ + pyo3::ffi::PyType_Slot { + slot: pyo3::ffi::#slot, + pfunc: #module::#slot_impl::<#ty> as _ + } + }} + }) + .peekable(); + + if tokens.peek().is_none() { + return TokenStream::default(); + } + + quote! { + #maybe_buffer_methods + + impl pyo3::class::proto_methods::#slots_trait<#ty> + for pyo3::class::proto_methods::PyClassProtocols<#ty> + { + fn #slots_trait_slots(self) -> &'static [pyo3::ffi::PyType_Slot] { + &[#(#tokens),*] } } - }) + } } diff --git a/src/class/basic.rs b/src/class/basic.rs index 505946d5..d796343c 100644 --- a/src/class/basic.rs +++ b/src/class/basic.rs @@ -8,9 +8,8 @@ //! Parts of the documentation are copied from the respective methods from the //! [typeobj docs](https://docs.python.org/3/c-api/typeobj.html) -use super::proto_methods::TypedSlot; use crate::callback::{HashCallbackOutput, IntoPyCallbackOutput}; -use crate::{exceptions, ffi, FromPyObject, PyAny, PyCell, PyClass, PyObject, PyResult}; +use crate::{exceptions, ffi, FromPyObject, PyAny, PyCell, PyClass, PyObject}; use std::os::raw::c_int; /// Operators for the __richcmp__ method @@ -134,150 +133,73 @@ pub trait PyObjectRichcmpProtocol<'p>: PyObjectProtocol<'p> { type Result: IntoPyCallbackOutput; } -/// Extension trait for proc-macro backend. +py_unary_func!(str, PyObjectStrProtocol, T::__str__); +py_unary_func!(repr, PyObjectReprProtocol, T::__repr__); +py_unary_func!(hash, PyObjectHashProtocol, T::__hash__, ffi::Py_hash_t); + #[doc(hidden)] -pub trait PyBasicSlots { - fn get_str() -> TypedSlot - where - Self: for<'p> PyObjectStrProtocol<'p>, - { - TypedSlot( - ffi::Py_tp_str, - py_unary_func!(PyObjectStrProtocol, Self::__str__), - ) - } - - fn get_repr() -> TypedSlot - where - Self: for<'p> PyObjectReprProtocol<'p>, - { - TypedSlot( - ffi::Py_tp_repr, - py_unary_func!(PyObjectReprProtocol, Self::__repr__), - ) - } - - fn get_hash() -> TypedSlot - where - Self: for<'p> PyObjectHashProtocol<'p>, - { - TypedSlot( - ffi::Py_tp_hash, - py_unary_func!(PyObjectHashProtocol, Self::__hash__, ffi::Py_hash_t), - ) - } - - fn get_getattr() -> TypedSlot - where - Self: for<'p> PyObjectGetAttrProtocol<'p>, - { - 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).convert(py) - }) +pub unsafe extern "C" fn getattr( + 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); } - TypedSlot(ffi::Py_tp_getattro, wrap::) - } - fn get_richcmp() -> TypedSlot - where - Self: for<'p> PyObjectRichcmpProtocol<'p>, - { - 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(exceptions::PyValueError::new_err( - "tp_richcompare called with invalid comparison operator", - )), - } - } - 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 = extract_or_return_not_implemented!(py, arg); - let op = extract_op(op)?; - - slf.try_borrow()?.__richcmp__(arg, op).convert(py) - }) - } - TypedSlot(ffi::Py_tp_richcompare, wrap::) - } - - fn get_setattr() -> TypedSlot - where - Self: for<'p> PyObjectSetAttrProtocol<'p>, - { - TypedSlot( - ffi::Py_tp_setattro, - py_func_set!(PyObjectSetAttrProtocol, Self::__setattr__), - ) - } - - fn get_delattr() -> TypedSlot - where - Self: for<'p> PyObjectDelAttrProtocol<'p>, - { - TypedSlot( - ffi::Py_tp_setattro, - py_func_del!(PyObjectDelAttrProtocol, Self::__delattr__), - ) - } - - fn get_setdelattr() -> TypedSlot - where - Self: for<'p> PyObjectSetAttrProtocol<'p> + for<'p> PyObjectDelAttrProtocol<'p>, - { - TypedSlot( - ffi::Py_tp_setattro, - py_func_set_del!( - PyObjectSetAttrProtocol, - PyObjectDelAttrProtocol, - Self, - __setattr__, - __delattr__ - ), - ) - } - - fn get_bool() -> TypedSlot - where - Self: for<'p> PyObjectBoolProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_bool, - py_unary_func!(PyObjectBoolProtocol, Self::__bool__, c_int), - ) - } + let slf = py.from_borrowed_ptr::>(slf); + let arg = py.from_borrowed_ptr::(arg); + call_ref!(slf, __getattr__, arg).convert(py) + }) } -impl<'p, T> PyBasicSlots for T where T: PyObjectProtocol<'p> {} +#[doc(hidden)] +pub unsafe extern "C" fn richcmp( + 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 = extract_or_return_not_implemented!(py, arg); + let op = match op { + ffi::Py_LT => CompareOp::Lt, + ffi::Py_LE => CompareOp::Le, + ffi::Py_EQ => CompareOp::Eq, + ffi::Py_NE => CompareOp::Ne, + ffi::Py_GT => CompareOp::Gt, + ffi::Py_GE => CompareOp::Ge, + _ => { + return Err(exceptions::PyValueError::new_err( + "tp_richcompare called with invalid comparison operator", + )) + } + }; + + slf.try_borrow()?.__richcmp__(arg, op).convert(py) + }) +} + +py_func_set!(setattr, PyObjectSetAttrProtocol, T::__setattr__); +py_func_del!(delattr, PyObjectDelAttrProtocol, T::__delattr__); +py_func_set_del!( + setdelattr, + PyObjectSetAttrProtocol, + PyObjectDelAttrProtocol, + Self, + __setattr__, + __delattr__ +); +py_unary_func!(bool, PyObjectBoolProtocol, T::__bool__, c_int); diff --git a/src/class/buffer.rs b/src/class/buffer.rs index 89b0a880..7b4c8acf 100644 --- a/src/class/buffer.rs +++ b/src/class/buffer.rs @@ -11,22 +11,18 @@ use std::os::raw::c_int; /// Buffer protocol interface /// /// For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html) -/// c-api +/// c-api. #[allow(unused_variables)] pub trait PyBufferProtocol<'p>: PyClass { + // No default implementations so that implementors of this trait provide both methods. + fn bf_getbuffer(slf: PyRefMut, view: *mut ffi::Py_buffer, flags: c_int) -> Self::Result where - Self: PyBufferGetBufferProtocol<'p>, - { - unimplemented!() - } + Self: PyBufferGetBufferProtocol<'p>; fn bf_releasebuffer(slf: PyRefMut, view: *mut ffi::Py_buffer) -> Self::Result where - Self: PyBufferReleaseBufferProtocol<'p>, - { - unimplemented!() - } + Self: PyBufferReleaseBufferProtocol<'p>; } pub trait PyBufferGetBufferProtocol<'p>: PyBufferProtocol<'p> { @@ -37,46 +33,28 @@ pub trait PyBufferReleaseBufferProtocol<'p>: PyBufferProtocol<'p> { type Result: IntoPyCallbackOutput<()>; } -/// Extension trait for proc-macro backend. #[doc(hidden)] -pub trait PyBufferSlots { - fn get_getbuffer() -> ffi::getbufferproc - where - Self: for<'p> PyBufferGetBufferProtocol<'p>, - { - 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).convert(py) - }) - } - - wrap:: - } - - fn get_releasebuffer() -> ffi::releasebufferproc - where - Self: for<'p> PyBufferReleaseBufferProtocol<'p>, - { - 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).convert(py) - }) - } - - wrap:: - } +pub unsafe extern "C" fn getbuffer( + 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).convert(py) + }) } -impl<'p, T> PyBufferSlots for T where T: PyBufferProtocol<'p> {} +#[doc(hidden)] +pub unsafe extern "C" fn releasebuffer(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).convert(py) + }) +} diff --git a/src/class/descr.rs b/src/class/descr.rs index 036ff881..d489bb59 100644 --- a/src/class/descr.rs +++ b/src/class/descr.rs @@ -5,10 +5,9 @@ //! [Python information]( //! https://docs.python.org/3/reference/datamodel.html#implementing-descriptors) -use super::proto_methods::TypedSlot; use crate::callback::IntoPyCallbackOutput; use crate::types::PyAny; -use crate::{ffi, FromPyObject, PyClass, PyObject}; +use crate::{FromPyObject, PyClass, PyObject}; use std::os::raw::c_int; /// Descriptor interface @@ -71,28 +70,5 @@ pub trait PyDescrSetNameProtocol<'p>: PyDescrProtocol<'p> { type Result: IntoPyCallbackOutput<()>; } -/// Extension trait for our proc-macro backend. -#[doc(hidden)] -pub trait PyDescrSlots { - fn get_descr_get() -> TypedSlot - where - Self: for<'p> PyDescrGetProtocol<'p>, - { - TypedSlot( - ffi::Py_tp_descr_get, - py_ternarys_func!(PyDescrGetProtocol, Self::__get__), - ) - } - - fn get_descr_set() -> TypedSlot - where - Self: for<'p> PyDescrSetProtocol<'p>, - { - TypedSlot( - ffi::Py_tp_descr_set, - py_ternarys_func!(PyDescrSetProtocol, Self::__set__, c_int), - ) - } -} - -impl<'p, T> PyDescrSlots for T where T: PyDescrProtocol<'p> {} +py_ternarys_func!(descr_get, PyDescrGetProtocol, Self::__get__); +py_ternarys_func!(descr_set, PyDescrSetProtocol, Self::__set__, c_int); diff --git a/src/class/gc.rs b/src/class/gc.rs index 9d8db1e0..667d97ae 100644 --- a/src/class/gc.rs +++ b/src/class/gc.rs @@ -3,7 +3,6 @@ //! Python GC support //! -use super::proto_methods::TypedSlot; use crate::{ffi, AsPyPointer, PyCell, PyClass, Python}; use std::os::raw::{c_int, c_void}; @@ -19,64 +18,46 @@ pub trait PyGCProtocol<'p>: PyClass { pub trait PyGCTraverseProtocol<'p>: PyGCProtocol<'p> {} pub trait PyGCClearProtocol<'p>: PyGCProtocol<'p> {} -/// Extension trait for proc-macro backend. #[doc(hidden)] -pub trait PyGCSlots { - fn get_traverse() -> TypedSlot - where - Self: for<'p> PyGCTraverseProtocol<'p>, - { - unsafe extern "C" fn wrap( - 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); +pub unsafe extern "C" fn 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 - } + 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, } - - TypedSlot(ffi::Py_tp_traverse, wrap::) - } - - fn get_clear() -> TypedSlot - where - Self: for<'p> PyGCClearProtocol<'p>, - { - unsafe extern "C" fn wrap(slf: *mut ffi::PyObject) -> c_int - where - T: for<'p> PyGCClearProtocol<'p>, - { - let pool = crate::GILPool::new(); - let slf = pool.python().from_borrowed_ptr::>(slf); - - slf.borrow_mut().__clear__(); - 0 - } - - TypedSlot(ffi::Py_tp_clear, wrap::) + } else { + 0 } } -impl<'p, T> PyGCSlots for T where T: PyGCProtocol<'p> {} +#[doc(hidden)] +pub unsafe extern "C" fn clear(slf: *mut ffi::PyObject) -> c_int +where + T: for<'p> PyGCClearProtocol<'p>, +{ + let pool = crate::GILPool::new(); + let slf = pool.python().from_borrowed_ptr::>(slf); + + slf.borrow_mut().__clear__(); + 0 +} /// Object visitor for GC. #[derive(Clone)] diff --git a/src/class/iter.rs b/src/class/iter.rs index c08bef0a..161cec00 100644 --- a/src/class/iter.rs +++ b/src/class/iter.rs @@ -2,7 +2,6 @@ //! Python Iterator Interface. //! Trait and support implementation for implementing iterators -use super::proto_methods::TypedSlot; use crate::callback::IntoPyCallbackOutput; use crate::derive_utils::TryFromPyCell; use crate::err::PyResult; @@ -72,30 +71,8 @@ pub trait PyIterNextProtocol<'p>: PyIterProtocol<'p> { type Result: IntoPyCallbackOutput; } -/// Extension trait for proc-macro backend. -#[doc(hidden)] -pub trait PyIterSlots { - fn get_iter() -> TypedSlot - where - Self: for<'p> PyIterIterProtocol<'p>, - { - TypedSlot( - ffi::Py_tp_iter, - py_unarys_func!(PyIterIterProtocol, Self::__iter__), - ) - } - fn get_iternext() -> TypedSlot - where - Self: for<'p> PyIterNextProtocol<'p>, - { - TypedSlot( - ffi::Py_tp_iternext, - py_unarys_func!(PyIterNextProtocol, Self::__next__), - ) - } -} - -impl<'p, T> PyIterSlots for T where T: PyIterProtocol<'p> {} +py_unarys_func!(iter, PyIterIterProtocol, Self::__iter__); +py_unarys_func!(iternext, PyIterNextProtocol, Self::__next__); /// Output of `__next__` which can either `yield` the next value in the iteration, or /// `return` a value to raise `StopIteration` in Python. diff --git a/src/class/macros.rs b/src/class/macros.rs index 98c0a47d..5aa18273 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -1,8 +1,9 @@ // Copyright (c) 2017-present PyO3 Project and Contributors macro_rules! py_unary_func { - ($trait: ident, $class:ident :: $f:ident, $call:ident, $ret_type: ty) => {{ - unsafe extern "C" fn wrap(slf: *mut $crate::ffi::PyObject) -> $ret_type + ($name:ident, $trait:ident, $class:ident :: $f:ident, $call:ident, $ret_type: ty) => { + #[doc(hidden)] + pub unsafe extern "C" fn $name(slf: *mut $crate::ffi::PyObject) -> $ret_type where T: for<'p> $trait<'p>, { @@ -11,20 +12,28 @@ macro_rules! py_unary_func { $call!(slf, $f).convert(py) }) } - wrap::<$class> - }}; - // Use call_ref! by default - ($trait:ident, $class:ident :: $f:ident, $ret_type:ty) => { - py_unary_func!($trait, $class::$f, call_ref, $ret_type); }; - ($trait:ident, $class:ident :: $f:ident) => { - py_unary_func!($trait, $class::$f, call_ref, *mut $crate::ffi::PyObject); + // Use call_ref! by default + ($name:ident, $trait:ident, $class:ident :: $f:ident, $ret_type:ty) => { + py_unary_func!($name, $trait, $class::$f, call_ref, $ret_type); + }; + ($name:ident, $trait:ident, $class:ident :: $f:ident) => { + py_unary_func!( + $name, + $trait, + $class::$f, + call_ref, + *mut $crate::ffi::PyObject + ); }; } macro_rules! py_unarys_func { - ($trait:ident, $class:ident :: $f:ident) => {{ - unsafe extern "C" fn wrap(slf: *mut $crate::ffi::PyObject) -> *mut $crate::ffi::PyObject + ($name:ident, $trait:ident, $class:ident :: $f:ident) => { + #[doc(hidden)] + pub unsafe extern "C" fn $name( + slf: *mut $crate::ffi::PyObject, + ) -> *mut $crate::ffi::PyObject where T: for<'p> $trait<'p>, { @@ -37,20 +46,23 @@ macro_rules! py_unarys_func { T::$f(borrow).convert(py) }) } - wrap::<$class> - }}; + }; } macro_rules! py_len_func { - ($trait:ident, $class:ident :: $f:ident) => { - py_unary_func!($trait, $class::$f, $crate::ffi::Py_ssize_t) + ($name:ident, $trait:ident, $class:ident :: $f:ident) => { + py_unary_func!($name, $trait, $class::$f, $crate::ffi::Py_ssize_t); }; } macro_rules! py_binary_func { // Use call_ref! by default - ($trait:ident, $class:ident :: $f:ident, $return:ty, $call:ident) => {{ - unsafe extern "C" fn wrap(slf: *mut ffi::PyObject, arg: *mut ffi::PyObject) -> $return + ($name:ident, $trait:ident, $class:ident :: $f:ident, $return:ty, $call:ident) => { + #[doc(hidden)] + pub unsafe extern "C" fn $name( + slf: *mut ffi::PyObject, + arg: *mut ffi::PyObject, + ) -> $return where T: for<'p> $trait<'p>, { @@ -60,19 +72,19 @@ macro_rules! py_binary_func { $call!(slf, $f, arg).convert(py) }) } - wrap::<$class> - }}; - ($trait:ident, $class:ident :: $f:ident, $return:ty) => { - py_binary_func!($trait, $class::$f, $return, call_ref) }; - ($trait:ident, $class:ident :: $f:ident) => { - py_binary_func!($trait, $class::$f, *mut $crate::ffi::PyObject) + ($name:ident, $trait:ident, $class:ident :: $f:ident, $return:ty) => { + py_binary_func!($name, $trait, $class::$f, $return, call_ref); + }; + ($name:ident, $trait:ident, $class:ident :: $f:ident) => { + py_binary_func!($name, $trait, $class::$f, *mut $crate::ffi::PyObject); }; } macro_rules! py_binary_num_func { - ($trait:ident, $class:ident :: $f:ident) => {{ - unsafe extern "C" fn wrap( + ($name:ident, $trait:ident, $class:ident :: $f:ident) => { + #[doc(hidden)] + pub unsafe extern "C" fn $name( lhs: *mut ffi::PyObject, rhs: *mut ffi::PyObject, ) -> *mut $crate::ffi::PyObject @@ -85,13 +97,13 @@ macro_rules! py_binary_num_func { T::$f(lhs.extract()?, rhs).convert(py) }) } - wrap::<$class> - }}; + }; } macro_rules! py_binary_reversed_num_func { - ($trait:ident, $class:ident :: $f:ident) => {{ - unsafe extern "C" fn wrap( + ($name:ident, $trait:ident, $class:ident :: $f:ident) => { + #[doc(hidden)] + pub unsafe extern "C" fn $name( lhs: *mut ffi::PyObject, rhs: *mut ffi::PyObject, ) -> *mut $crate::ffi::PyObject @@ -105,13 +117,13 @@ macro_rules! py_binary_reversed_num_func { T::$f(&*slf.try_borrow()?, arg).convert(py) }) } - wrap::<$class> - }}; + }; } macro_rules! py_binary_fallback_num_func { - ($class:ident, $lop_trait: ident :: $lop: ident, $rop_trait: ident :: $rop: ident) => {{ - unsafe extern "C" fn wrap( + ($name:ident, $class:ident, $lop_trait: ident :: $lop: ident, $rop_trait: ident :: $rop: ident) => { + #[doc(hidden)] + pub unsafe extern "C" fn $name( lhs: *mut ffi::PyObject, rhs: *mut ffi::PyObject, ) -> *mut $crate::ffi::PyObject @@ -133,14 +145,14 @@ macro_rules! py_binary_fallback_num_func { } }) } - wrap::<$class> - }}; + }; } // NOTE(kngwyu): This macro is used only for inplace operations, so I used call_mut here. macro_rules! py_binary_self_func { - ($trait:ident, $class:ident :: $f:ident) => {{ - unsafe extern "C" fn wrap( + ($name:ident, $trait:ident, $class:ident :: $f:ident) => { + #[doc(hidden)] + pub unsafe extern "C" fn $name( slf: *mut ffi::PyObject, arg: *mut ffi::PyObject, ) -> *mut $crate::ffi::PyObject @@ -155,17 +167,17 @@ macro_rules! py_binary_self_func { Ok::<_, $crate::err::PyErr>(slf) }) } - wrap::<$class> - }}; + }; } macro_rules! py_ssizearg_func { // Use call_ref! by default - ($trait:ident, $class:ident :: $f:ident) => { - py_ssizearg_func!($trait, $class::$f, call_ref) + ($name:ident, $trait:ident, $class:ident :: $f:ident) => { + py_ssizearg_func!($name, $trait, $class::$f, call_ref); }; - ($trait:ident, $class:ident :: $f:ident, $call:ident) => {{ - unsafe extern "C" fn wrap( + ($name:ident, $trait:ident, $class:ident :: $f:ident, $call:ident) => { + #[doc(hidden)] + pub unsafe extern "C" fn $name( slf: *mut ffi::PyObject, arg: $crate::ffi::Py_ssize_t, ) -> *mut $crate::ffi::PyObject @@ -177,13 +189,13 @@ macro_rules! py_ssizearg_func { $call!(slf, $f; arg.into()).convert(py) }) } - wrap::<$class> - }}; + }; } macro_rules! py_ternarys_func { - ($trait:ident, $class:ident :: $f:ident, $return_type:ty) => {{ - unsafe extern "C" fn wrap( + ($name:ident, $trait:ident, $class:ident :: $f:ident, $return_type:ty) => { + #[doc(hidden)] + pub unsafe extern "C" fn $name( slf: *mut $crate::ffi::PyObject, arg1: *mut $crate::ffi::PyObject, arg2: *mut $crate::ffi::PyObject, @@ -206,17 +218,16 @@ macro_rules! py_ternarys_func { T::$f(slf, arg1, arg2).convert(py) }) } - - wrap::<$class> - }}; - ($trait:ident, $class:ident :: $f:ident) => { - py_ternarys_func!($trait, $class::$f, *mut $crate::ffi::PyObject); + }; + ($name:ident, $trait:ident, $class:ident :: $f:ident) => { + py_ternarys_func!($name, $trait, $class::$f, *mut $crate::ffi::PyObject); }; } macro_rules! py_func_set { - ($trait_name:ident, $class:ident :: $fn_set:ident) => {{ - unsafe extern "C" fn wrap( + ($name:ident, $trait_name:ident, $class:ident :: $fn_set:ident) => { + #[doc(hidden)] + pub unsafe extern "C" fn $name( slf: *mut $crate::ffi::PyObject, name: *mut $crate::ffi::PyObject, value: *mut $crate::ffi::PyObject, @@ -239,14 +250,13 @@ macro_rules! py_func_set { } }) } - - wrap::<$class> - }}; + }; } macro_rules! py_func_del { - ($trait_name:ident, $class:ident :: $fn_del:ident) => {{ - unsafe extern "C" fn wrap( + ($name:ident, $trait_name:ident, $class:ident :: $fn_del:ident) => { + #[doc(hidden)] + pub unsafe extern "C" fn $name( slf: *mut $crate::ffi::PyObject, name: *mut $crate::ffi::PyObject, value: *mut $crate::ffi::PyObject, @@ -268,14 +278,13 @@ macro_rules! py_func_del { } }) } - - wrap::<$class> - }}; + }; } macro_rules! py_func_set_del { - ($trait1:ident, $trait2:ident, $class:ident, $fn_set:ident, $fn_del:ident) => {{ - unsafe extern "C" fn wrap( + ($name:ident, $trait1:ident, $trait2:ident, $class:ident, $fn_set:ident, $fn_del:ident) => { + #[doc(hidden)] + pub unsafe extern "C" fn $name( slf: *mut $crate::ffi::PyObject, name: *mut $crate::ffi::PyObject, value: *mut $crate::ffi::PyObject, @@ -295,8 +304,7 @@ macro_rules! py_func_set_del { } }) } - wrap::<$class> - }}; + }; } macro_rules! extract_or_return_not_implemented { diff --git a/src/class/mapping.rs b/src/class/mapping.rs index df3cfe14..bad8e556 100644 --- a/src/class/mapping.rs +++ b/src/class/mapping.rs @@ -3,7 +3,6 @@ //! Python Mapping Interface //! Trait and support implementation for implementing mapping support -use super::proto_methods::TypedSlot; use crate::callback::IntoPyCallbackOutput; use crate::{exceptions, ffi, FromPyObject, PyClass, PyObject}; @@ -73,64 +72,15 @@ pub trait PyMappingReversedProtocol<'p>: PyMappingProtocol<'p> { type Result: IntoPyCallbackOutput; } -/// Extension trait for proc-macro backend. -#[doc(hidden)] -pub trait PyMappingSlots { - fn get_len() -> TypedSlot - where - Self: for<'p> PyMappingLenProtocol<'p>, - { - TypedSlot( - ffi::Py_mp_length, - py_len_func!(PyMappingLenProtocol, Self::__len__), - ) - } - - fn get_getitem() -> TypedSlot - where - Self: for<'p> PyMappingGetItemProtocol<'p>, - { - TypedSlot( - ffi::Py_mp_subscript, - py_binary_func!(PyMappingGetItemProtocol, Self::__getitem__), - ) - } - - fn get_setitem() -> TypedSlot - where - Self: for<'p> PyMappingSetItemProtocol<'p>, - { - TypedSlot( - ffi::Py_mp_ass_subscript, - py_func_set!(PyMappingSetItemProtocol, Self::__setitem__), - ) - } - - fn get_delitem() -> TypedSlot - where - Self: for<'p> PyMappingDelItemProtocol<'p>, - { - TypedSlot( - ffi::Py_mp_ass_subscript, - py_func_del!(PyMappingDelItemProtocol, Self::__delitem__), - ) - } - - fn get_setdelitem() -> TypedSlot - where - Self: for<'p> PyMappingSetItemProtocol<'p> + for<'p> PyMappingDelItemProtocol<'p>, - { - TypedSlot( - ffi::Py_mp_ass_subscript, - py_func_set_del!( - PyMappingSetItemProtocol, - PyMappingDelItemProtocol, - Self, - __setitem__, - __delitem__ - ), - ) - } -} - -impl<'p, T> PyMappingSlots for T where T: PyMappingProtocol<'p> {} +py_len_func!(len, PyMappingLenProtocol, Self::__len__); +py_binary_func!(getitem, PyMappingGetItemProtocol, Self::__getitem__); +py_func_set!(setitem, PyMappingSetItemProtocol, Self::__setitem__); +py_func_del!(delitem, PyMappingDelItemProtocol, Self::__delitem__); +py_func_set_del!( + setdelitem, + PyMappingSetItemProtocol, + PyMappingDelItemProtocol, + Self, + __setitem__, + __delitem__ +); diff --git a/src/class/mod.rs b/src/class/mod.rs index 5fc986cb..03cb40d4 100644 --- a/src/class/mod.rs +++ b/src/class/mod.rs @@ -16,6 +16,7 @@ pub mod mapping; #[doc(hidden)] pub mod methods; pub mod number; +#[doc(hidden)] pub mod proto_methods; pub mod pyasync; pub mod sequence; diff --git a/src/class/number.rs b/src/class/number.rs index cdcea6b0..36eae307 100644 --- a/src/class/number.rs +++ b/src/class/number.rs @@ -2,7 +2,6 @@ //! Python Number Interface //! Trait and support implementation for implementing number protocol -use super::proto_methods::TypedSlot; use crate::callback::IntoPyCallbackOutput; use crate::err::PyErr; use crate::{ffi, FromPyObject, PyClass, PyObject}; @@ -579,721 +578,202 @@ pub trait PyNumberIndexProtocol<'p>: PyNumberProtocol<'p> { type Result: IntoPyCallbackOutput; } -/// Extension trait for proc-macro backend. +py_binary_fallback_num_func!( + add_radd, + T, + PyNumberAddProtocol::__add__, + PyNumberRAddProtocol::__radd__ +); +py_binary_num_func!(add, PyNumberAddProtocol, T::__add__); +py_binary_reversed_num_func!(radd, PyNumberRAddProtocol, T::__radd__); +py_binary_fallback_num_func!( + sub_rsub, + T, + PyNumberSubProtocol::__sub__, + PyNumberRSubProtocol::__rsub__ +); +py_binary_num_func!(sub, PyNumberSubProtocol, T::__sub__); +py_binary_reversed_num_func!(rsub, PyNumberRSubProtocol, T::__rsub__); +py_binary_fallback_num_func!( + mul_rmul, + T, + PyNumberMulProtocol::__mul__, + PyNumberRMulProtocol::__rmul__ +); +py_binary_num_func!(mul, PyNumberMulProtocol, T::__mul__); +py_binary_reversed_num_func!(rmul, PyNumberRMulProtocol, T::__rmul__); +py_binary_num_func!(mod_, PyNumberModProtocol, T::__mod__); +py_binary_fallback_num_func!( + divmod_rdivmod, + T, + PyNumberDivmodProtocol::__divmod__, + PyNumberRDivmodProtocol::__rdivmod__ +); +py_binary_num_func!(divmod, PyNumberDivmodProtocol, T::__divmod__); +py_binary_reversed_num_func!(rdivmod, PyNumberRDivmodProtocol, T::__rdivmod__); + #[doc(hidden)] -pub trait PyNumberSlots { - fn get_add_radd() -> TypedSlot - where - Self: for<'p> PyNumberAddProtocol<'p> + for<'p> PyNumberRAddProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_add, - py_binary_fallback_num_func!( - Self, - PyNumberAddProtocol::__add__, - PyNumberRAddProtocol::__radd__ - ), - ) - } - - fn get_add() -> TypedSlot - where - Self: for<'p> PyNumberAddProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_add, - py_binary_num_func!(PyNumberAddProtocol, Self::__add__), - ) - } - - fn get_radd() -> TypedSlot - where - Self: for<'p> PyNumberRAddProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_add, - py_binary_reversed_num_func!(PyNumberRAddProtocol, Self::__radd__), - ) - } - - fn get_sub_rsub() -> TypedSlot - where - Self: for<'p> PyNumberSubProtocol<'p> + for<'p> PyNumberRSubProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_subtract, - py_binary_fallback_num_func!( - Self, - PyNumberSubProtocol::__sub__, - PyNumberRSubProtocol::__rsub__ - ), - ) - } - - fn get_sub() -> TypedSlot - where - Self: for<'p> PyNumberSubProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_subtract, - py_binary_num_func!(PyNumberSubProtocol, Self::__sub__), - ) - } - - fn get_rsub() -> TypedSlot - where - Self: for<'p> PyNumberRSubProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_subtract, - py_binary_reversed_num_func!(PyNumberRSubProtocol, Self::__rsub__), - ) - } - - fn get_mul_rmul() -> TypedSlot - where - Self: for<'p> PyNumberMulProtocol<'p> + for<'p> PyNumberRMulProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_multiply, - py_binary_fallback_num_func!( - Self, - PyNumberMulProtocol::__mul__, - PyNumberRMulProtocol::__rmul__ - ), - ) - } - - fn get_mul() -> TypedSlot - where - Self: for<'p> PyNumberMulProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_multiply, - py_binary_num_func!(PyNumberMulProtocol, Self::__mul__), - ) - } - - fn get_rmul() -> TypedSlot - where - Self: for<'p> PyNumberRMulProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_multiply, - py_binary_reversed_num_func!(PyNumberRMulProtocol, Self::__rmul__), - ) - } - - fn get_mod() -> TypedSlot - where - Self: for<'p> PyNumberModProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_remainder, - py_binary_num_func!(PyNumberModProtocol, Self::__mod__), - ) - } - - fn get_divmod_rdivmod() -> TypedSlot - where - Self: for<'p> PyNumberDivmodProtocol<'p> + for<'p> PyNumberRDivmodProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_divmod, - py_binary_fallback_num_func!( - Self, - PyNumberDivmodProtocol::__divmod__, - PyNumberRDivmodProtocol::__rdivmod__ - ), - ) - } - - fn get_divmod() -> TypedSlot - where - Self: for<'p> PyNumberDivmodProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_divmod, - py_binary_num_func!(PyNumberDivmodProtocol, Self::__divmod__), - ) - } - - fn get_rdivmod() -> TypedSlot - where - Self: for<'p> PyNumberRDivmodProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_divmod, - py_binary_reversed_num_func!(PyNumberRDivmodProtocol, Self::__rdivmod__), - ) - } - - fn get_pow_rpow() -> TypedSlot - where - Self: for<'p> PyNumberPowProtocol<'p> + for<'p> PyNumberRPowProtocol<'p>, - { - unsafe extern "C" fn wrap_pow_and_rpow( - lhs: *mut ffi::PyObject, - rhs: *mut ffi::PyObject, - modulo: *mut ffi::PyObject, - ) -> *mut ffi::PyObject - where - T: for<'p> PyNumberPowProtocol<'p> + for<'p> PyNumberRPowProtocol<'p>, - { - crate::callback_body!(py, { - let lhs = py.from_borrowed_ptr::(lhs); - let rhs = py.from_borrowed_ptr::(rhs); - let modulo = py.from_borrowed_ptr::(modulo); - // First, try __pow__ - match (lhs.extract(), rhs.extract(), modulo.extract()) { - (Ok(l), Ok(r), Ok(m)) => T::__pow__(l, r, m).convert(py), - _ => { - // Then try __rpow__ - let slf: &crate::PyCell = extract_or_return_not_implemented!(rhs); - let arg = extract_or_return_not_implemented!(lhs); - let modulo = extract_or_return_not_implemented!(modulo); - slf.try_borrow()?.__rpow__(arg, modulo).convert(py) - } - } - }) - } - - TypedSlot(ffi::Py_nb_power, wrap_pow_and_rpow::) - } - - fn get_pow() -> TypedSlot - where - Self: for<'p> PyNumberPowProtocol<'p>, - { - unsafe extern "C" fn wrap_pow( - lhs: *mut ffi::PyObject, - rhs: *mut ffi::PyObject, - modulo: *mut ffi::PyObject, - ) -> *mut ffi::PyObject - where - T: for<'p> PyNumberPowProtocol<'p>, - { - crate::callback_body!(py, { - let lhs = extract_or_return_not_implemented!(py, lhs); - let rhs = extract_or_return_not_implemented!(py, rhs); - let modulo = extract_or_return_not_implemented!(py, modulo); - T::__pow__(lhs, rhs, modulo).convert(py) - }) - } - - TypedSlot(ffi::Py_nb_power, wrap_pow::) - } - - fn get_rpow() -> TypedSlot - where - Self: for<'p> PyNumberRPowProtocol<'p>, - { - unsafe extern "C" fn wrap_rpow( - arg: *mut ffi::PyObject, - slf: *mut ffi::PyObject, - modulo: *mut ffi::PyObject, - ) -> *mut ffi::PyObject - where - T: for<'p> PyNumberRPowProtocol<'p>, - { - crate::callback_body!(py, { - let slf: &crate::PyCell = extract_or_return_not_implemented!(py, slf); - let arg = extract_or_return_not_implemented!(py, arg); - let modulo = extract_or_return_not_implemented!(py, modulo); +pub unsafe extern "C" fn pow_rpow( + lhs: *mut ffi::PyObject, + rhs: *mut ffi::PyObject, + modulo: *mut ffi::PyObject, +) -> *mut ffi::PyObject +where + T: for<'p> PyNumberPowProtocol<'p> + for<'p> PyNumberRPowProtocol<'p>, +{ + crate::callback_body!(py, { + let lhs = py.from_borrowed_ptr::(lhs); + let rhs = py.from_borrowed_ptr::(rhs); + let modulo = py.from_borrowed_ptr::(modulo); + // First, try __pow__ + match (lhs.extract(), rhs.extract(), modulo.extract()) { + (Ok(l), Ok(r), Ok(m)) => T::__pow__(l, r, m).convert(py), + _ => { + // Then try __rpow__ + let slf: &crate::PyCell = extract_or_return_not_implemented!(rhs); + let arg = extract_or_return_not_implemented!(lhs); + let modulo = extract_or_return_not_implemented!(modulo); slf.try_borrow()?.__rpow__(arg, modulo).convert(py) - }) + } } - - TypedSlot(ffi::Py_nb_power, wrap_rpow::) - } - - fn get_neg() -> TypedSlot - where - Self: for<'p> PyNumberNegProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_negative, - py_unary_func!(PyNumberNegProtocol, Self::__neg__), - ) - } - - fn get_pos() -> TypedSlot - where - Self: for<'p> PyNumberPosProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_positive, - py_unary_func!(PyNumberPosProtocol, Self::__pos__), - ) - } - - fn get_abs() -> TypedSlot - where - Self: for<'p> PyNumberAbsProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_absolute, - py_unary_func!(PyNumberAbsProtocol, Self::__abs__), - ) - } - - fn get_invert() -> TypedSlot - where - Self: for<'p> PyNumberInvertProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_invert, - py_unary_func!(PyNumberInvertProtocol, Self::__invert__), - ) - } - - fn get_lshift_rlshift() -> TypedSlot - where - Self: for<'p> PyNumberLShiftProtocol<'p> + for<'p> PyNumberRLShiftProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_lshift, - py_binary_fallback_num_func!( - Self, - PyNumberLShiftProtocol::__lshift__, - PyNumberRLShiftProtocol::__rlshift__ - ), - ) - } - - fn get_lshift() -> TypedSlot - where - Self: for<'p> PyNumberLShiftProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_lshift, - py_binary_num_func!(PyNumberLShiftProtocol, Self::__lshift__), - ) - } - - fn get_rlshift() -> TypedSlot - where - Self: for<'p> PyNumberRLShiftProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_lshift, - py_binary_reversed_num_func!(PyNumberRLShiftProtocol, Self::__rlshift__), - ) - } - - fn get_rshift_rrshift() -> TypedSlot - where - Self: for<'p> PyNumberRShiftProtocol<'p> + for<'p> PyNumberRRShiftProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_rshift, - py_binary_fallback_num_func!( - Self, - PyNumberRShiftProtocol::__rshift__, - PyNumberRRShiftProtocol::__rrshift__ - ), - ) - } - - fn get_rshift() -> TypedSlot - where - Self: for<'p> PyNumberRShiftProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_rshift, - py_binary_num_func!(PyNumberRShiftProtocol, Self::__rshift__), - ) - } - - fn get_rrshift() -> TypedSlot - where - Self: for<'p> PyNumberRRShiftProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_rshift, - py_binary_reversed_num_func!(PyNumberRRShiftProtocol, Self::__rrshift__), - ) - } - - fn get_and_rand() -> TypedSlot - where - Self: for<'p> PyNumberAndProtocol<'p> + for<'p> PyNumberRAndProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_and, - py_binary_fallback_num_func!( - Self, - PyNumberAndProtocol::__and__, - PyNumberRAndProtocol::__rand__ - ), - ) - } - - fn get_and() -> TypedSlot - where - Self: for<'p> PyNumberAndProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_and, - py_binary_num_func!(PyNumberAndProtocol, Self::__and__), - ) - } - - fn get_rand() -> TypedSlot - where - Self: for<'p> PyNumberRAndProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_and, - py_binary_reversed_num_func!(PyNumberRAndProtocol, Self::__rand__), - ) - } - - fn get_xor_rxor() -> TypedSlot - where - Self: for<'p> PyNumberXorProtocol<'p> + for<'p> PyNumberRXorProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_xor, - py_binary_fallback_num_func!( - Self, - PyNumberXorProtocol::__xor__, - PyNumberRXorProtocol::__rxor__ - ), - ) - } - - fn get_xor() -> TypedSlot - where - Self: for<'p> PyNumberXorProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_xor, - py_binary_num_func!(PyNumberXorProtocol, Self::__xor__), - ) - } - - fn get_rxor() -> TypedSlot - where - Self: for<'p> PyNumberRXorProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_xor, - py_binary_reversed_num_func!(PyNumberRXorProtocol, Self::__rxor__), - ) - } - - fn get_or_ror() -> TypedSlot - where - Self: for<'p> PyNumberOrProtocol<'p> + for<'p> PyNumberROrProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_or, - py_binary_fallback_num_func!( - Self, - PyNumberOrProtocol::__or__, - PyNumberROrProtocol::__ror__ - ), - ) - } - - fn get_or() -> TypedSlot - where - Self: for<'p> PyNumberOrProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_or, - py_binary_num_func!(PyNumberOrProtocol, Self::__or__), - ) - } - - fn get_ror() -> TypedSlot - where - Self: for<'p> PyNumberROrProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_or, - py_binary_reversed_num_func!(PyNumberROrProtocol, Self::__ror__), - ) - } - - fn get_int() -> TypedSlot - where - Self: for<'p> PyNumberIntProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_int, - py_unary_func!(PyNumberIntProtocol, Self::__int__), - ) - } - - fn get_float() -> TypedSlot - where - Self: for<'p> PyNumberFloatProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_float, - py_unary_func!(PyNumberFloatProtocol, Self::__float__), - ) - } - - fn get_iadd() -> TypedSlot - where - Self: for<'p> PyNumberIAddProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_inplace_add, - py_binary_self_func!(PyNumberIAddProtocol, Self::__iadd__), - ) - } - - fn get_isub() -> TypedSlot - where - Self: for<'p> PyNumberISubProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_inplace_subtract, - py_binary_self_func!(PyNumberISubProtocol, Self::__isub__), - ) - } - - fn get_imul() -> TypedSlot - where - Self: for<'p> PyNumberIMulProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_inplace_multiply, - py_binary_self_func!(PyNumberIMulProtocol, Self::__imul__), - ) - } - - fn get_imod() -> TypedSlot - where - Self: for<'p> PyNumberIModProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_inplace_remainder, - py_binary_self_func!(PyNumberIModProtocol, Self::__imod__), - ) - } - - fn get_ipow() -> TypedSlot - where - Self: for<'p> PyNumberIPowProtocol<'p>, - { - // NOTE: Somehow __ipow__ causes SIGSEGV in Python < 3.8 when we extract, - // so we ignore it. It's the same as what CPython does. - unsafe extern "C" fn wrap_ipow( - slf: *mut ffi::PyObject, - other: *mut ffi::PyObject, - _modulo: *mut ffi::PyObject, - ) -> *mut ffi::PyObject - where - T: for<'p> PyNumberIPowProtocol<'p>, - { - crate::callback_body!(py, { - let slf_cell = py.from_borrowed_ptr::>(slf); - let other = py.from_borrowed_ptr::(other); - call_operator_mut!(py, slf_cell, __ipow__, other).convert(py)?; - ffi::Py_INCREF(slf); - Ok::<_, PyErr>(slf) - }) - } - - TypedSlot(ffi::Py_nb_inplace_power, wrap_ipow::) - } - - fn get_ilshift() -> TypedSlot - where - Self: for<'p> PyNumberILShiftProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_inplace_lshift, - py_binary_self_func!(PyNumberILShiftProtocol, Self::__ilshift__), - ) - } - - fn get_irshift() -> TypedSlot - where - Self: for<'p> PyNumberIRShiftProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_inplace_rshift, - py_binary_self_func!(PyNumberIRShiftProtocol, Self::__irshift__), - ) - } - - fn get_iand() -> TypedSlot - where - Self: for<'p> PyNumberIAndProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_inplace_and, - py_binary_self_func!(PyNumberIAndProtocol, Self::__iand__), - ) - } - - fn get_ixor() -> TypedSlot - where - Self: for<'p> PyNumberIXorProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_inplace_xor, - py_binary_self_func!(PyNumberIXorProtocol, Self::__ixor__), - ) - } - - fn get_ior() -> TypedSlot - where - Self: for<'p> PyNumberIOrProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_inplace_or, - py_binary_self_func!(PyNumberIOrProtocol, Self::__ior__), - ) - } - - fn get_floordiv_rfloordiv() -> TypedSlot - where - Self: for<'p> PyNumberFloordivProtocol<'p> + for<'p> PyNumberRFloordivProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_floor_divide, - py_binary_fallback_num_func!( - Self, - PyNumberFloordivProtocol::__floordiv__, - PyNumberRFloordivProtocol::__rfloordiv__ - ), - ) - } - - fn get_floordiv() -> TypedSlot - where - Self: for<'p> PyNumberFloordivProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_floor_divide, - py_binary_num_func!(PyNumberFloordivProtocol, Self::__floordiv__), - ) - } - - fn get_rfloordiv() -> TypedSlot - where - Self: for<'p> PyNumberRFloordivProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_floor_divide, - py_binary_reversed_num_func!(PyNumberRFloordivProtocol, Self::__rfloordiv__), - ) - } - - fn get_truediv_rtruediv() -> TypedSlot - where - Self: for<'p> PyNumberTruedivProtocol<'p> + for<'p> PyNumberRTruedivProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_true_divide, - py_binary_fallback_num_func!( - Self, - PyNumberTruedivProtocol::__truediv__, - PyNumberRTruedivProtocol::__rtruediv__ - ), - ) - } - - fn get_truediv() -> TypedSlot - where - Self: for<'p> PyNumberTruedivProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_true_divide, - py_binary_num_func!(PyNumberTruedivProtocol, Self::__truediv__), - ) - } - - fn get_rtruediv() -> TypedSlot - where - Self: for<'p> PyNumberRTruedivProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_true_divide, - py_binary_reversed_num_func!(PyNumberRTruedivProtocol, Self::__rtruediv__), - ) - } - - fn get_ifloordiv() -> TypedSlot - where - Self: for<'p> PyNumberIFloordivProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_inplace_floor_divide, - py_binary_self_func!(PyNumberIFloordivProtocol, Self::__ifloordiv__), - ) - } - - fn get_itruediv() -> TypedSlot - where - Self: for<'p> PyNumberITruedivProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_inplace_true_divide, - py_binary_self_func!(PyNumberITruedivProtocol, Self::__itruediv__), - ) - } - - fn get_index() -> TypedSlot - where - Self: for<'p> PyNumberIndexProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_index, - py_unary_func!(PyNumberIndexProtocol, Self::__index__), - ) - } - - fn get_matmul_rmatmul() -> TypedSlot - where - Self: for<'p> PyNumberMatmulProtocol<'p> + for<'p> PyNumberRMatmulProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_matrix_multiply, - py_binary_fallback_num_func!( - Self, - PyNumberMatmulProtocol::__matmul__, - PyNumberRMatmulProtocol::__rmatmul__ - ), - ) - } - - fn get_matmul() -> TypedSlot - where - Self: for<'p> PyNumberMatmulProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_matrix_multiply, - py_binary_num_func!(PyNumberMatmulProtocol, Self::__matmul__), - ) - } - - fn get_rmatmul() -> TypedSlot - where - Self: for<'p> PyNumberRMatmulProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_matrix_multiply, - py_binary_reversed_num_func!(PyNumberRMatmulProtocol, Self::__rmatmul__), - ) - } - - fn get_imatmul() -> TypedSlot - where - Self: for<'p> PyNumberIMatmulProtocol<'p>, - { - TypedSlot( - ffi::Py_nb_inplace_matrix_multiply, - py_binary_self_func!(PyNumberIMatmulProtocol, Self::__imatmul__), - ) - } + }) } -impl<'p, T> PyNumberSlots for T where T: PyNumberProtocol<'p> {} +#[doc(hidden)] +pub unsafe extern "C" fn pow( + lhs: *mut ffi::PyObject, + rhs: *mut ffi::PyObject, + modulo: *mut ffi::PyObject, +) -> *mut ffi::PyObject +where + T: for<'p> PyNumberPowProtocol<'p>, +{ + crate::callback_body!(py, { + let lhs = extract_or_return_not_implemented!(py, lhs); + let rhs = extract_or_return_not_implemented!(py, rhs); + let modulo = extract_or_return_not_implemented!(py, modulo); + T::__pow__(lhs, rhs, modulo).convert(py) + }) +} + +#[doc(hidden)] +pub unsafe extern "C" fn rpow( + arg: *mut ffi::PyObject, + slf: *mut ffi::PyObject, + modulo: *mut ffi::PyObject, +) -> *mut ffi::PyObject +where + T: for<'p> PyNumberRPowProtocol<'p>, +{ + crate::callback_body!(py, { + let slf: &crate::PyCell = extract_or_return_not_implemented!(py, slf); + let arg = extract_or_return_not_implemented!(py, arg); + let modulo = extract_or_return_not_implemented!(py, modulo); + slf.try_borrow()?.__rpow__(arg, modulo).convert(py) + }) +} + +py_unary_func!(neg, PyNumberNegProtocol, T::__neg__); +py_unary_func!(pos, PyNumberPosProtocol, T::__pos__); +py_unary_func!(abs, PyNumberAbsProtocol, T::__abs__); +py_unary_func!(invert, PyNumberInvertProtocol, T::__invert__); +py_binary_fallback_num_func!( + lshift_rlshift, + T, + PyNumberLShiftProtocol::__lshift__, + PyNumberRLShiftProtocol::__rlshift__ +); +py_binary_num_func!(lshift, PyNumberLShiftProtocol, T::__lshift__); +py_binary_reversed_num_func!(rlshift, PyNumberRLShiftProtocol, T::__rlshift__); +py_binary_fallback_num_func!( + rshift_rrshift, + T, + PyNumberRShiftProtocol::__rshift__, + PyNumberRRShiftProtocol::__rrshift__ +); +py_binary_num_func!(rshift, PyNumberRShiftProtocol, T::__rshift__); +py_binary_reversed_num_func!(rrshift, PyNumberRRShiftProtocol, T::__rrshift__); +py_binary_fallback_num_func!( + and_rand, + T, + PyNumberAndProtocol::__and__, + PyNumberRAndProtocol::__rand__ +); +py_binary_num_func!(and, PyNumberAndProtocol, T::__and__); +py_binary_reversed_num_func!(rand, PyNumberRAndProtocol, T::__rand__); +py_binary_fallback_num_func!( + xor_rxor, + T, + PyNumberXorProtocol::__xor__, + PyNumberRXorProtocol::__rxor__ +); +py_binary_num_func!(xor, PyNumberXorProtocol, T::__xor__); +py_binary_reversed_num_func!(rxor, PyNumberRXorProtocol, T::__rxor__); +py_binary_fallback_num_func!( + or_ror, + T, + PyNumberOrProtocol::__or__, + PyNumberROrProtocol::__ror__ +); +py_binary_num_func!(or, PyNumberOrProtocol, T::__or__); +py_binary_reversed_num_func!(ror, PyNumberROrProtocol, T::__ror__); +py_unary_func!(int, PyNumberIntProtocol, T::__int__); +py_unary_func!(float, PyNumberFloatProtocol, T::__float__); +py_binary_self_func!(iadd, PyNumberIAddProtocol, T::__iadd__); +py_binary_self_func!(isub, PyNumberISubProtocol, T::__isub__); +py_binary_self_func!(imul, PyNumberIMulProtocol, T::__imul__); +py_binary_self_func!(imod, PyNumberIModProtocol, T::__imod__); + +#[doc(hidden)] +pub unsafe extern "C" fn ipow( + slf: *mut ffi::PyObject, + other: *mut ffi::PyObject, + _modulo: *mut ffi::PyObject, +) -> *mut ffi::PyObject +where + T: for<'p> PyNumberIPowProtocol<'p>, +{ + // NOTE: Somehow __ipow__ causes SIGSEGV in Python < 3.8 when we extract, + // so we ignore it. It's the same as what CPython does. + crate::callback_body!(py, { + let slf_cell = py.from_borrowed_ptr::>(slf); + let other = py.from_borrowed_ptr::(other); + call_operator_mut!(py, slf_cell, __ipow__, other).convert(py)?; + ffi::Py_INCREF(slf); + Ok::<_, PyErr>(slf) + }) +} + +py_binary_self_func!(ilshift, PyNumberILShiftProtocol, T::__ilshift__); +py_binary_self_func!(irshift, PyNumberIRShiftProtocol, T::__irshift__); +py_binary_self_func!(iand, PyNumberIAndProtocol, T::__iand__); +py_binary_self_func!(ixor, PyNumberIXorProtocol, T::__ixor__); +py_binary_self_func!(ior, PyNumberIOrProtocol, T::__ior__); +py_binary_fallback_num_func!( + floordiv_rfloordiv, + T, + PyNumberFloordivProtocol::__floordiv__, + PyNumberRFloordivProtocol::__rfloordiv__ +); +py_binary_num_func!(floordiv, PyNumberFloordivProtocol, T::__floordiv__); +py_binary_reversed_num_func!(rfloordiv, PyNumberRFloordivProtocol, T::__rfloordiv__); +py_binary_fallback_num_func!( + truediv_rtruediv, + T, + PyNumberTruedivProtocol::__truediv__, + PyNumberRTruedivProtocol::__rtruediv__ +); +py_binary_num_func!(truediv, PyNumberTruedivProtocol, T::__truediv__); +py_binary_reversed_num_func!(rtruediv, PyNumberRTruedivProtocol, T::__rtruediv__); +py_binary_self_func!(ifloordiv, PyNumberIFloordivProtocol, T::__ifloordiv__); +py_binary_self_func!(itruediv, PyNumberITruedivProtocol, T::__itruediv__); +py_unary_func!(index, PyNumberIndexProtocol, T::__index__); +py_binary_fallback_num_func!( + matmul_rmatmul, + T, + PyNumberMatmulProtocol::__matmul__, + PyNumberRMatmulProtocol::__rmatmul__ +); +py_binary_num_func!(matmul, PyNumberMatmulProtocol, T::__matmul__); +py_binary_reversed_num_func!(rmatmul, PyNumberRMatmulProtocol, T::__rmatmul__); +py_binary_self_func!(imatmul, PyNumberIMatmulProtocol, T::__imatmul__); diff --git a/src/class/proto_methods.rs b/src/class/proto_methods.rs index eff528f0..b314d948 100644 --- a/src/class/proto_methods.rs +++ b/src/class/proto_methods.rs @@ -1,78 +1,147 @@ use crate::ffi; -#[cfg(not(Py_LIMITED_API))] -use crate::ffi::PyBufferProcs; - -/// ABI3 doesn't have buffer APIs, so here we define the empty one. -#[cfg(Py_LIMITED_API)] -#[doc(hidden)] -#[derive(Clone)] -pub struct PyBufferProcs; +use std::marker::PhantomData; // Note(kngwyu): default implementations are for rust-numpy. Please don't remove them. pub trait PyProtoMethods { - fn get_type_slots() -> Vec { - vec![] - } - fn get_buffer() -> Option { + fn for_each_proto_slot(_visitor: Visitor) {} + fn get_buffer() -> Option<&'static PyBufferProcs> { None } } -/// Typed version of `ffi::PyType_Slot` -#[doc(hidden)] -pub struct TypedSlot(pub std::os::raw::c_int, pub T); +pub struct PyClassProtocols(PhantomData); -#[doc(hidden)] -pub enum PyProtoMethodDef { - Slots(Vec), - Buffer(PyBufferProcs), -} - -impl From> for PyProtoMethodDef { - fn from(slots: Vec) -> Self { - PyProtoMethodDef::Slots(slots) +impl PyClassProtocols { + pub fn new() -> Self { + Self(PhantomData) } } -impl From for PyProtoMethodDef { - fn from(buffer_procs: PyBufferProcs) -> Self { - PyProtoMethodDef::Buffer(buffer_procs) +impl Default for PyClassProtocols { + fn default() -> Self { + Self::new() } } -#[doc(hidden)] -#[cfg(feature = "macros")] -pub trait PyProtoInventory: inventory::Collect { - fn new(methods: PyProtoMethodDef) -> Self; - fn get(&'static self) -> &'static PyProtoMethodDef; -} - -#[doc(hidden)] -#[cfg(feature = "macros")] -pub trait HasProtoInventory { - type ProtoMethods: PyProtoInventory; -} - -#[cfg(feature = "macros")] -impl PyProtoMethods for T { - fn get_type_slots() -> Vec { - inventory::iter:: - .into_iter() - .filter_map(|def| match def.get() { - PyProtoMethodDef::Slots(slots) => Some(slots), - PyProtoMethodDef::Buffer(_) => None, - }) - .flatten() - .cloned() - .collect() - } - - fn get_buffer() -> Option { - inventory::iter:: - .into_iter() - .find_map(|def| match def.get() { - PyProtoMethodDef::Slots(_) => None, - PyProtoMethodDef::Buffer(buf) => Some(buf.clone()), - }) +impl Clone for PyClassProtocols { + fn clone(&self) -> Self { + Self::new() + } +} + +impl Copy for PyClassProtocols {} + +// All traits describing slots, as well as the fallback implementations for unimplemented protos +// +// Protos which are implented use dtolnay specialization to implement for PyClassProtocols. +// +// See https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md + +pub trait PyObjectProtocolSlots { + fn object_protocol_slots(self) -> &'static [ffi::PyType_Slot]; +} + +impl PyObjectProtocolSlots for &'_ PyClassProtocols { + fn object_protocol_slots(self) -> &'static [ffi::PyType_Slot] { + &[] + } +} + +pub trait PyDescrProtocolSlots { + fn descr_protocol_slots(self) -> &'static [ffi::PyType_Slot]; +} + +impl PyDescrProtocolSlots for &'_ PyClassProtocols { + fn descr_protocol_slots(self) -> &'static [ffi::PyType_Slot] { + &[] + } +} + +pub trait PyGCProtocolSlots { + fn gc_protocol_slots(self) -> &'static [ffi::PyType_Slot]; +} + +impl PyGCProtocolSlots for &'_ PyClassProtocols { + fn gc_protocol_slots(self) -> &'static [ffi::PyType_Slot] { + &[] + } +} + +pub trait PyIterProtocolSlots { + fn iter_protocol_slots(self) -> &'static [ffi::PyType_Slot]; +} + +impl PyIterProtocolSlots for &'_ PyClassProtocols { + fn iter_protocol_slots(self) -> &'static [ffi::PyType_Slot] { + &[] + } +} + +pub trait PyMappingProtocolSlots { + fn mapping_protocol_slots(self) -> &'static [ffi::PyType_Slot]; +} + +impl PyMappingProtocolSlots for &'_ PyClassProtocols { + fn mapping_protocol_slots(self) -> &'static [ffi::PyType_Slot] { + &[] + } +} + +pub trait PyNumberProtocolSlots { + fn number_protocol_slots(self) -> &'static [ffi::PyType_Slot]; +} + +impl PyNumberProtocolSlots for &'_ PyClassProtocols { + fn number_protocol_slots(self) -> &'static [ffi::PyType_Slot] { + &[] + } +} + +pub trait PyAsyncProtocolSlots { + fn async_protocol_slots(self) -> &'static [ffi::PyType_Slot]; +} + +impl PyAsyncProtocolSlots for &'_ PyClassProtocols { + fn async_protocol_slots(self) -> &'static [ffi::PyType_Slot] { + &[] + } +} + +pub trait PySequenceProtocolSlots { + fn sequence_protocol_slots(self) -> &'static [ffi::PyType_Slot]; +} + +impl PySequenceProtocolSlots for &'_ PyClassProtocols { + fn sequence_protocol_slots(self) -> &'static [ffi::PyType_Slot] { + &[] + } +} + +pub trait PyBufferProtocolSlots { + fn buffer_protocol_slots(self) -> &'static [ffi::PyType_Slot]; +} + +impl PyBufferProtocolSlots for &'_ PyClassProtocols { + fn buffer_protocol_slots(self) -> &'static [ffi::PyType_Slot] { + &[] + } +} + +// On Python < 3.9 setting the buffer protocol using slots doesn't work, so these procs are used +// on those versions to set the slots manually (on the limited API). + +#[cfg(not(Py_LIMITED_API))] +pub use ffi::PyBufferProcs; + +#[cfg(Py_LIMITED_API)] +pub struct PyBufferProcs; + +pub trait PyBufferProtocolProcs { + fn buffer_procs(self) -> Option<&'static PyBufferProcs>; +} + +impl PyBufferProtocolProcs for &'_ PyClassProtocols { + fn buffer_procs(self) -> Option<&'static PyBufferProcs> { + None } } diff --git a/src/class/pyasync.rs b/src/class/pyasync.rs index 226656fd..81efa837 100644 --- a/src/class/pyasync.rs +++ b/src/class/pyasync.rs @@ -8,7 +8,6 @@ //! [PEP-0492](https://www.python.org/dev/peps/pep-0492/) //! -use super::proto_methods::TypedSlot; use crate::callback::IntoPyCallbackOutput; use crate::derive_utils::TryFromPyCell; use crate::err::PyResult; @@ -86,41 +85,9 @@ pub trait PyAsyncAexitProtocol<'p>: PyAsyncProtocol<'p> { type Result: IntoPyCallbackOutput; } -/// Extension trait for proc-macro backend. -#[doc(hidden)] -pub trait PyAsyncSlots { - fn get_await() -> TypedSlot - where - Self: for<'p> PyAsyncAwaitProtocol<'p>, - { - TypedSlot( - ffi::Py_am_await, - py_unarys_func!(PyAsyncAwaitProtocol, Self::__await__), - ) - } - - fn get_aiter() -> TypedSlot - where - Self: for<'p> PyAsyncAiterProtocol<'p>, - { - TypedSlot( - ffi::Py_am_aiter, - py_unarys_func!(PyAsyncAiterProtocol, Self::__aiter__), - ) - } - - fn get_anext() -> TypedSlot - where - Self: for<'p> PyAsyncAnextProtocol<'p>, - { - TypedSlot( - ffi::Py_am_anext, - py_unarys_func!(PyAsyncAnextProtocol, Self::__anext__), - ) - } -} - -impl<'p, T> PyAsyncSlots for T where T: PyAsyncProtocol<'p> {} +py_unarys_func!(await_, PyAsyncAwaitProtocol, Self::__await__); +py_unarys_func!(aiter, PyAsyncAiterProtocol, Self::__aiter__); +py_unarys_func!(anext, PyAsyncAnextProtocol, Self::__anext__); /// Output of `__anext__`. pub enum IterANextOutput { diff --git a/src/class/sequence.rs b/src/class/sequence.rs index 7ac3ffd9..129a188a 100644 --- a/src/class/sequence.rs +++ b/src/class/sequence.rs @@ -3,7 +3,6 @@ //! Python Sequence Interface //! Trait and support implementation for implementing sequence -use super::proto_methods::TypedSlot; use crate::callback::IntoPyCallbackOutput; use crate::conversion::{FromPyObject, IntoPy}; use crate::err::PyErr; @@ -129,177 +128,99 @@ pub trait PySequenceInplaceRepeatProtocol<'p>: type Result: IntoPyCallbackOutput; } -/// Extension trait for proc-macro backend. +py_len_func!(len, PySequenceLenProtocol, Self::__len__); +py_binary_func!(concat, PySequenceConcatProtocol, Self::__concat__); +py_ssizearg_func!(repeat, PySequenceRepeatProtocol, Self::__repeat__); +py_ssizearg_func!(getitem, PySequenceGetItemProtocol, Self::__getitem__); + #[doc(hidden)] -pub trait PySequenceSlots { - fn get_len() -> TypedSlot - where - Self: for<'p> PySequenceLenProtocol<'p>, - { - TypedSlot( - ffi::Py_sq_length, - py_len_func!(PySequenceLenProtocol, Self::__len__), - ) - } +pub unsafe extern "C" fn setitem( + 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); - fn get_concat() -> TypedSlot - where - Self: for<'p> PySequenceConcatProtocol<'p>, - { - TypedSlot( - ffi::Py_sq_concat, - py_binary_func!(PySequenceConcatProtocol, Self::__concat__), - ) - } - - fn get_repeat() -> TypedSlot - where - Self: for<'p> PySequenceRepeatProtocol<'p>, - { - TypedSlot( - ffi::Py_sq_repeat, - py_ssizearg_func!(PySequenceRepeatProtocol, Self::__repeat__), - ) - } - - fn get_getitem() -> TypedSlot - where - Self: for<'p> PySequenceGetItemProtocol<'p>, - { - TypedSlot( - ffi::Py_sq_item, - py_ssizearg_func!(PySequenceGetItemProtocol, Self::__getitem__), - ) - } - - fn get_setitem() -> TypedSlot - where - Self: 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(exceptions::PyNotImplementedError::new_err(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()?; - crate::callback::convert(py, slf.__setitem__(key.into(), value)) - }) + if value.is_null() { + return Err(exceptions::PyNotImplementedError::new_err(format!( + "Item deletion is not supported by {:?}", + stringify!(T) + ))); } - TypedSlot(ffi::Py_sq_ass_item, wrap::) - } - - fn get_delitem() -> TypedSlot - where - Self: 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() { - crate::callback::convert(py, slf.borrow_mut().__delitem__(key.into())) - } else { - Err(PyErr::new::(format!( - "Item assignment not supported by {:?}", - stringify!(T) - ))) - } - }) - } - - TypedSlot(ffi::Py_sq_ass_item, wrap::) - } - - fn get_setdelitem() -> TypedSlot - where - Self: for<'p> PySequenceDelItemProtocol<'p> + 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> + 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()).convert(py) - } else { - let value = py.from_borrowed_ptr::(value); - let mut slf_ = slf.try_borrow_mut()?; - let value = value.extract()?; - slf_.__setitem__(key.into(), value).convert(py) - } - }) - } - - TypedSlot(ffi::Py_sq_ass_item, wrap::) - } - - fn get_contains() -> TypedSlot - where - Self: for<'p> PySequenceContainsProtocol<'p>, - { - TypedSlot( - ffi::Py_sq_contains, - py_binary_func!(PySequenceContainsProtocol, Self::__contains__, c_int), - ) - } - - fn get_inplace_concat() -> TypedSlot - where - Self: for<'p> PySequenceInplaceConcatProtocol<'p>, - { - TypedSlot( - ffi::Py_sq_inplace_concat, - py_binary_func!( - PySequenceInplaceConcatProtocol, - Self::__inplace_concat__, - *mut ffi::PyObject, - call_mut - ), - ) - } - - fn get_inplace_repeat() -> TypedSlot - where - Self: for<'p> PySequenceInplaceRepeatProtocol<'p>, - { - TypedSlot( - ffi::Py_sq_inplace_repeat, - py_ssizearg_func!( - PySequenceInplaceRepeatProtocol, - Self::__inplace_repeat__, - call_mut - ), - ) - } + let mut slf = slf.try_borrow_mut()?; + let value = py.from_borrowed_ptr::(value); + let value = value.extract()?; + crate::callback::convert(py, slf.__setitem__(key.into(), value)) + }) } -impl<'p, T> PySequenceSlots for T where T: PySequenceProtocol<'p> {} +#[doc(hidden)] +pub unsafe extern "C" fn delitem( + 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() { + crate::callback::convert(py, slf.borrow_mut().__delitem__(key.into())) + } else { + Err(PyErr::new::(format!( + "Item assignment not supported by {:?}", + stringify!(T) + ))) + } + }) +} + +#[doc(hidden)] +pub unsafe extern "C" fn setdelitem( + 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()).convert(py) + } else { + let value = py.from_borrowed_ptr::(value); + let mut slf_ = slf.try_borrow_mut()?; + let value = value.extract()?; + slf_.__setitem__(key.into(), value).convert(py) + } + }) +} + +py_binary_func!( + contains, + PySequenceContainsProtocol, + Self::__contains__, + c_int +); +py_binary_func!( + inplace_concat, + PySequenceInplaceConcatProtocol, + Self::__inplace_concat__, + *mut ffi::PyObject, + call_mut +); +py_ssizearg_func!( + inplace_repeat, + PySequenceInplaceRepeatProtocol, + Self::__inplace_repeat__, + call_mut +); diff --git a/src/ffi/typeslots.rs b/src/ffi/typeslots.rs index d214ba08..6356e480 100644 --- a/src/ffi/typeslots.rs +++ b/src/ffi/typeslots.rs @@ -1,5 +1,9 @@ use std::os::raw::c_int; +#[cfg(not(Py_LIMITED_API))] +pub const Py_bf_getbuffer: c_int = 1; +#[cfg(not(Py_LIMITED_API))] +pub const Py_bf_releasebuffer: c_int = 2; pub const Py_mp_ass_subscript: c_int = 3; pub const Py_mp_length: c_int = 4; pub const Py_mp_subscript: c_int = 5; diff --git a/src/lib.rs b/src/lib.rs index b421391f..88eb3a8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -339,16 +339,15 @@ macro_rules! py_run_impl { use $crate::ToPyObject; let d = [$((stringify!($val), $val.to_object($py)),)+].into_py_dict($py); - $py.run($code, None, Some(d)) - .map_err(|e| { - e.print($py); - // So when this c api function the last line called printed the error to stderr, - // the output is only written into a buffer which is never flushed because we - // panic before flushing. This is where this hack comes into place - $py.run("import sys; sys.stderr.flush()", None, None) - .unwrap(); - }) - .expect($code) + if let Err(e) = $py.run($code, None, Some(d)) { + e.print($py); + // So when this c api function the last line called printed the error to stderr, + // the output is only written into a buffer which is never flushed because we + // panic before flushing. This is where this hack comes into place + $py.run("import sys; sys.stderr.flush()", None, None) + .unwrap(); + panic!($code.to_string()) + } }}; } diff --git a/src/pyclass.rs b/src/pyclass.rs index f927eb11..67f5ba6e 100644 --- a/src/pyclass.rs +++ b/src/pyclass.rs @@ -191,11 +191,11 @@ where // protocol methods let mut has_gc_methods = false; - for slot in T::get_type_slots() { + T::for_each_proto_slot(|slot| { has_gc_methods |= slot.slot == ffi::Py_tp_clear; has_gc_methods |= slot.slot == ffi::Py_tp_traverse; slots.0.push(slot); - } + }); slots.push(0, ptr::null_mut()); let mut spec = ffi::PyType_Spec { @@ -238,6 +238,9 @@ fn tp_init_additional(type_object: *mut ffi::PyTypeObject) { } } + // Setting buffer protocols via slots doesn't work until Python 3.9, so on older versions we + // must manually fixup the type object. + #[cfg(not(Py_3_9))] if let Some(buffer) = T::get_buffer() { unsafe { (*(*type_object).tp_as_buffer).bf_getbuffer = buffer.bf_getbuffer;