Merge pull request #1328 from davidhewitt/pyproto-no-inventory

pyproto: remove inventory from implementation
This commit is contained in:
David Hewitt 2020-12-22 15:29:20 +00:00 committed by GitHub
commit 33b3da3337
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1162 additions and 1954 deletions

View file

@ -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: FnMut(pyo3::ffi::PyType_Slot)>(visitor: Visitor) {
// Implementation which uses dtolnay specialization to load all slots.
use pyo3::class::proto_methods::*;
let protocols = PyClassProtocols::<MyClass>::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::<MyClass>::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<MyClass>;

File diff suppressed because it is too large Load diff

View file

@ -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<TokenStream> {
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
}

View file

@ -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: FnMut(pyo3::ffi::PyType_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

View file

@ -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<TokenStream>, ty: &syn::Type) -> TokenS
}
}
fn submit_protocol_methods(
fn impl_proto_methods(
method_names: HashSet<String>,
ty: &syn::Type,
proto: &defs::Proto,
) -> syn::Result<TokenStream> {
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<ffi::PyType_Slot>
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;
<Inventory as pyo3::class::proto_methods::PyProtoInventory>::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),*]
}
}
})
}
}

View file

@ -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<PyObject>;
}
/// 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<ffi::reprfunc>
where
Self: for<'p> PyObjectStrProtocol<'p>,
{
TypedSlot(
ffi::Py_tp_str,
py_unary_func!(PyObjectStrProtocol, Self::__str__),
)
}
fn get_repr() -> TypedSlot<ffi::reprfunc>
where
Self: for<'p> PyObjectReprProtocol<'p>,
{
TypedSlot(
ffi::Py_tp_repr,
py_unary_func!(PyObjectReprProtocol, Self::__repr__),
)
}
fn get_hash() -> TypedSlot<ffi::hashfunc>
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<ffi::getattrofunc>
where
Self: for<'p> PyObjectGetAttrProtocol<'p>,
{
unsafe extern "C" fn wrap<T>(
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::<PyCell<T>>(slf);
let arg = py.from_borrowed_ptr::<PyAny>(arg);
call_ref!(slf, __getattr__, arg).convert(py)
})
pub unsafe extern "C" fn getattr<T>(
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::<Self>)
}
fn get_richcmp() -> TypedSlot<ffi::richcmpfunc>
where
Self: for<'p> PyObjectRichcmpProtocol<'p>,
{
fn extract_op(op: c_int) -> PyResult<CompareOp> {
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<T>(
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::<crate::PyCell<T>>(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::<Self>)
}
fn get_setattr() -> TypedSlot<ffi::setattrofunc>
where
Self: for<'p> PyObjectSetAttrProtocol<'p>,
{
TypedSlot(
ffi::Py_tp_setattro,
py_func_set!(PyObjectSetAttrProtocol, Self::__setattr__),
)
}
fn get_delattr() -> TypedSlot<ffi::setattrofunc>
where
Self: for<'p> PyObjectDelAttrProtocol<'p>,
{
TypedSlot(
ffi::Py_tp_setattro,
py_func_del!(PyObjectDelAttrProtocol, Self::__delattr__),
)
}
fn get_setdelattr() -> TypedSlot<ffi::setattrofunc>
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<ffi::inquiry>
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::<PyCell<T>>(slf);
let arg = py.from_borrowed_ptr::<PyAny>(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<T>(
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::<crate::PyCell<T>>(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);

View file

@ -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<Self>, view: *mut ffi::Py_buffer, flags: c_int) -> Self::Result
where
Self: PyBufferGetBufferProtocol<'p>,
{
unimplemented!()
}
Self: PyBufferGetBufferProtocol<'p>;
fn bf_releasebuffer(slf: PyRefMut<Self>, 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<T>(
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::<PyCell<T>>(slf);
T::bf_getbuffer(slf.try_borrow_mut()?, arg1, arg2).convert(py)
})
}
wrap::<Self>
}
fn get_releasebuffer() -> ffi::releasebufferproc
where
Self: for<'p> PyBufferReleaseBufferProtocol<'p>,
{
unsafe extern "C" fn wrap<T>(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::<crate::PyCell<T>>(slf);
T::bf_releasebuffer(slf.try_borrow_mut()?, arg1).convert(py)
})
}
wrap::<Self>
}
pub unsafe extern "C" fn getbuffer<T>(
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::<PyCell<T>>(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<T>(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::<crate::PyCell<T>>(slf);
T::bf_releasebuffer(slf.try_borrow_mut()?, arg1).convert(py)
})
}

View file

@ -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<ffi::descrgetfunc>
where
Self: for<'p> PyDescrGetProtocol<'p>,
{
TypedSlot(
ffi::Py_tp_descr_get,
py_ternarys_func!(PyDescrGetProtocol, Self::__get__),
)
}
fn get_descr_set() -> TypedSlot<ffi::descrsetfunc>
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);

View file

@ -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<ffi::traverseproc>
where
Self: for<'p> PyGCTraverseProtocol<'p>,
{
unsafe extern "C" fn wrap<T>(
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::<PyCell<T>>(slf);
pub unsafe extern "C" fn traverse<T>(
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::<PyCell<T>>(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::<Self>)
}
fn get_clear() -> TypedSlot<ffi::inquiry>
where
Self: for<'p> PyGCClearProtocol<'p>,
{
unsafe extern "C" fn wrap<T>(slf: *mut ffi::PyObject) -> c_int
where
T: for<'p> PyGCClearProtocol<'p>,
{
let pool = crate::GILPool::new();
let slf = pool.python().from_borrowed_ptr::<PyCell<T>>(slf);
slf.borrow_mut().__clear__();
0
}
TypedSlot(ffi::Py_tp_clear, wrap::<Self>)
} else {
0
}
}
impl<'p, T> PyGCSlots for T where T: PyGCProtocol<'p> {}
#[doc(hidden)]
pub unsafe extern "C" fn clear<T>(slf: *mut ffi::PyObject) -> c_int
where
T: for<'p> PyGCClearProtocol<'p>,
{
let pool = crate::GILPool::new();
let slf = pool.python().from_borrowed_ptr::<PyCell<T>>(slf);
slf.borrow_mut().__clear__();
0
}
/// Object visitor for GC.
#[derive(Clone)]

View file

@ -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<PyIterNextOutput>;
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
pub trait PyIterSlots {
fn get_iter() -> TypedSlot<ffi::getiterfunc>
where
Self: for<'p> PyIterIterProtocol<'p>,
{
TypedSlot(
ffi::Py_tp_iter,
py_unarys_func!(PyIterIterProtocol, Self::__iter__),
)
}
fn get_iternext() -> TypedSlot<ffi::iternextfunc>
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.

View file

@ -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<T>(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<T>(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<T>(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<T>(
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<T>(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<T>(
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<T>(
($name:ident, $trait:ident, $class:ident :: $f:ident) => {
#[doc(hidden)]
pub unsafe extern "C" fn $name<T>(
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<T>(
($name:ident, $trait:ident, $class:ident :: $f:ident) => {
#[doc(hidden)]
pub unsafe extern "C" fn $name<T>(
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<T>(
($name:ident, $class:ident, $lop_trait: ident :: $lop: ident, $rop_trait: ident :: $rop: ident) => {
#[doc(hidden)]
pub unsafe extern "C" fn $name<T>(
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<T>(
($name:ident, $trait:ident, $class:ident :: $f:ident) => {
#[doc(hidden)]
pub unsafe extern "C" fn $name<T>(
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<T>(
($name:ident, $trait:ident, $class:ident :: $f:ident, $call:ident) => {
#[doc(hidden)]
pub unsafe extern "C" fn $name<T>(
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<T>(
($name:ident, $trait:ident, $class:ident :: $f:ident, $return_type:ty) => {
#[doc(hidden)]
pub unsafe extern "C" fn $name<T>(
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<T>(
($name:ident, $trait_name:ident, $class:ident :: $fn_set:ident) => {
#[doc(hidden)]
pub unsafe extern "C" fn $name<T>(
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<T>(
($name:ident, $trait_name:ident, $class:ident :: $fn_del:ident) => {
#[doc(hidden)]
pub unsafe extern "C" fn $name<T>(
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<T>(
($name:ident, $trait1:ident, $trait2:ident, $class:ident, $fn_set:ident, $fn_del:ident) => {
#[doc(hidden)]
pub unsafe extern "C" fn $name<T>(
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 {

View file

@ -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<PyObject>;
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
pub trait PyMappingSlots {
fn get_len() -> TypedSlot<ffi::lenfunc>
where
Self: for<'p> PyMappingLenProtocol<'p>,
{
TypedSlot(
ffi::Py_mp_length,
py_len_func!(PyMappingLenProtocol, Self::__len__),
)
}
fn get_getitem() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyMappingGetItemProtocol<'p>,
{
TypedSlot(
ffi::Py_mp_subscript,
py_binary_func!(PyMappingGetItemProtocol, Self::__getitem__),
)
}
fn get_setitem() -> TypedSlot<ffi::objobjargproc>
where
Self: for<'p> PyMappingSetItemProtocol<'p>,
{
TypedSlot(
ffi::Py_mp_ass_subscript,
py_func_set!(PyMappingSetItemProtocol, Self::__setitem__),
)
}
fn get_delitem() -> TypedSlot<ffi::objobjargproc>
where
Self: for<'p> PyMappingDelItemProtocol<'p>,
{
TypedSlot(
ffi::Py_mp_ass_subscript,
py_func_del!(PyMappingDelItemProtocol, Self::__delitem__),
)
}
fn get_setdelitem() -> TypedSlot<ffi::objobjargproc>
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__
);

View file

@ -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;

View file

@ -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<PyObject>;
}
/// 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<ffi::binaryfunc>
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<ffi::binaryfunc>
where
Self: for<'p> PyNumberAddProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_add,
py_binary_num_func!(PyNumberAddProtocol, Self::__add__),
)
}
fn get_radd() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberRAddProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_add,
py_binary_reversed_num_func!(PyNumberRAddProtocol, Self::__radd__),
)
}
fn get_sub_rsub() -> TypedSlot<ffi::binaryfunc>
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<ffi::binaryfunc>
where
Self: for<'p> PyNumberSubProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_subtract,
py_binary_num_func!(PyNumberSubProtocol, Self::__sub__),
)
}
fn get_rsub() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberRSubProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_subtract,
py_binary_reversed_num_func!(PyNumberRSubProtocol, Self::__rsub__),
)
}
fn get_mul_rmul() -> TypedSlot<ffi::binaryfunc>
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<ffi::binaryfunc>
where
Self: for<'p> PyNumberMulProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_multiply,
py_binary_num_func!(PyNumberMulProtocol, Self::__mul__),
)
}
fn get_rmul() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberRMulProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_multiply,
py_binary_reversed_num_func!(PyNumberRMulProtocol, Self::__rmul__),
)
}
fn get_mod() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberModProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_remainder,
py_binary_num_func!(PyNumberModProtocol, Self::__mod__),
)
}
fn get_divmod_rdivmod() -> TypedSlot<ffi::binaryfunc>
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<ffi::binaryfunc>
where
Self: for<'p> PyNumberDivmodProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_divmod,
py_binary_num_func!(PyNumberDivmodProtocol, Self::__divmod__),
)
}
fn get_rdivmod() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberRDivmodProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_divmod,
py_binary_reversed_num_func!(PyNumberRDivmodProtocol, Self::__rdivmod__),
)
}
fn get_pow_rpow() -> TypedSlot<ffi::ternaryfunc>
where
Self: for<'p> PyNumberPowProtocol<'p> + for<'p> PyNumberRPowProtocol<'p>,
{
unsafe extern "C" fn wrap_pow_and_rpow<T>(
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::<crate::PyAny>(lhs);
let rhs = py.from_borrowed_ptr::<crate::PyAny>(rhs);
let modulo = py.from_borrowed_ptr::<crate::PyAny>(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<T> = 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::<Self>)
}
fn get_pow() -> TypedSlot<ffi::ternaryfunc>
where
Self: for<'p> PyNumberPowProtocol<'p>,
{
unsafe extern "C" fn wrap_pow<T>(
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::<Self>)
}
fn get_rpow() -> TypedSlot<ffi::ternaryfunc>
where
Self: for<'p> PyNumberRPowProtocol<'p>,
{
unsafe extern "C" fn wrap_rpow<T>(
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<T> = 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<T>(
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::<crate::PyAny>(lhs);
let rhs = py.from_borrowed_ptr::<crate::PyAny>(rhs);
let modulo = py.from_borrowed_ptr::<crate::PyAny>(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<T> = 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::<Self>)
}
fn get_neg() -> TypedSlot<ffi::unaryfunc>
where
Self: for<'p> PyNumberNegProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_negative,
py_unary_func!(PyNumberNegProtocol, Self::__neg__),
)
}
fn get_pos() -> TypedSlot<ffi::unaryfunc>
where
Self: for<'p> PyNumberPosProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_positive,
py_unary_func!(PyNumberPosProtocol, Self::__pos__),
)
}
fn get_abs() -> TypedSlot<ffi::unaryfunc>
where
Self: for<'p> PyNumberAbsProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_absolute,
py_unary_func!(PyNumberAbsProtocol, Self::__abs__),
)
}
fn get_invert() -> TypedSlot<ffi::unaryfunc>
where
Self: for<'p> PyNumberInvertProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_invert,
py_unary_func!(PyNumberInvertProtocol, Self::__invert__),
)
}
fn get_lshift_rlshift() -> TypedSlot<ffi::binaryfunc>
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<ffi::binaryfunc>
where
Self: for<'p> PyNumberLShiftProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_lshift,
py_binary_num_func!(PyNumberLShiftProtocol, Self::__lshift__),
)
}
fn get_rlshift() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberRLShiftProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_lshift,
py_binary_reversed_num_func!(PyNumberRLShiftProtocol, Self::__rlshift__),
)
}
fn get_rshift_rrshift() -> TypedSlot<ffi::binaryfunc>
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<ffi::binaryfunc>
where
Self: for<'p> PyNumberRShiftProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_rshift,
py_binary_num_func!(PyNumberRShiftProtocol, Self::__rshift__),
)
}
fn get_rrshift() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberRRShiftProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_rshift,
py_binary_reversed_num_func!(PyNumberRRShiftProtocol, Self::__rrshift__),
)
}
fn get_and_rand() -> TypedSlot<ffi::binaryfunc>
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<ffi::binaryfunc>
where
Self: for<'p> PyNumberAndProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_and,
py_binary_num_func!(PyNumberAndProtocol, Self::__and__),
)
}
fn get_rand() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberRAndProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_and,
py_binary_reversed_num_func!(PyNumberRAndProtocol, Self::__rand__),
)
}
fn get_xor_rxor() -> TypedSlot<ffi::binaryfunc>
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<ffi::binaryfunc>
where
Self: for<'p> PyNumberXorProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_xor,
py_binary_num_func!(PyNumberXorProtocol, Self::__xor__),
)
}
fn get_rxor() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberRXorProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_xor,
py_binary_reversed_num_func!(PyNumberRXorProtocol, Self::__rxor__),
)
}
fn get_or_ror() -> TypedSlot<ffi::binaryfunc>
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<ffi::binaryfunc>
where
Self: for<'p> PyNumberOrProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_or,
py_binary_num_func!(PyNumberOrProtocol, Self::__or__),
)
}
fn get_ror() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberROrProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_or,
py_binary_reversed_num_func!(PyNumberROrProtocol, Self::__ror__),
)
}
fn get_int() -> TypedSlot<ffi::unaryfunc>
where
Self: for<'p> PyNumberIntProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_int,
py_unary_func!(PyNumberIntProtocol, Self::__int__),
)
}
fn get_float() -> TypedSlot<ffi::unaryfunc>
where
Self: for<'p> PyNumberFloatProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_float,
py_unary_func!(PyNumberFloatProtocol, Self::__float__),
)
}
fn get_iadd() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberIAddProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_inplace_add,
py_binary_self_func!(PyNumberIAddProtocol, Self::__iadd__),
)
}
fn get_isub() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberISubProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_inplace_subtract,
py_binary_self_func!(PyNumberISubProtocol, Self::__isub__),
)
}
fn get_imul() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberIMulProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_inplace_multiply,
py_binary_self_func!(PyNumberIMulProtocol, Self::__imul__),
)
}
fn get_imod() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberIModProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_inplace_remainder,
py_binary_self_func!(PyNumberIModProtocol, Self::__imod__),
)
}
fn get_ipow() -> TypedSlot<ffi::ternaryfunc>
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<T>(
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::<crate::PyCell<T>>(slf);
let other = py.from_borrowed_ptr::<crate::PyAny>(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::<Self>)
}
fn get_ilshift() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberILShiftProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_inplace_lshift,
py_binary_self_func!(PyNumberILShiftProtocol, Self::__ilshift__),
)
}
fn get_irshift() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberIRShiftProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_inplace_rshift,
py_binary_self_func!(PyNumberIRShiftProtocol, Self::__irshift__),
)
}
fn get_iand() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberIAndProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_inplace_and,
py_binary_self_func!(PyNumberIAndProtocol, Self::__iand__),
)
}
fn get_ixor() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberIXorProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_inplace_xor,
py_binary_self_func!(PyNumberIXorProtocol, Self::__ixor__),
)
}
fn get_ior() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberIOrProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_inplace_or,
py_binary_self_func!(PyNumberIOrProtocol, Self::__ior__),
)
}
fn get_floordiv_rfloordiv() -> TypedSlot<ffi::binaryfunc>
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<ffi::binaryfunc>
where
Self: for<'p> PyNumberFloordivProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_floor_divide,
py_binary_num_func!(PyNumberFloordivProtocol, Self::__floordiv__),
)
}
fn get_rfloordiv() -> TypedSlot<ffi::binaryfunc>
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<ffi::binaryfunc>
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<ffi::binaryfunc>
where
Self: for<'p> PyNumberTruedivProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_true_divide,
py_binary_num_func!(PyNumberTruedivProtocol, Self::__truediv__),
)
}
fn get_rtruediv() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberRTruedivProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_true_divide,
py_binary_reversed_num_func!(PyNumberRTruedivProtocol, Self::__rtruediv__),
)
}
fn get_ifloordiv() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberIFloordivProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_inplace_floor_divide,
py_binary_self_func!(PyNumberIFloordivProtocol, Self::__ifloordiv__),
)
}
fn get_itruediv() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberITruedivProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_inplace_true_divide,
py_binary_self_func!(PyNumberITruedivProtocol, Self::__itruediv__),
)
}
fn get_index() -> TypedSlot<ffi::unaryfunc>
where
Self: for<'p> PyNumberIndexProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_index,
py_unary_func!(PyNumberIndexProtocol, Self::__index__),
)
}
fn get_matmul_rmatmul() -> TypedSlot<ffi::binaryfunc>
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<ffi::binaryfunc>
where
Self: for<'p> PyNumberMatmulProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_matrix_multiply,
py_binary_num_func!(PyNumberMatmulProtocol, Self::__matmul__),
)
}
fn get_rmatmul() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PyNumberRMatmulProtocol<'p>,
{
TypedSlot(
ffi::Py_nb_matrix_multiply,
py_binary_reversed_num_func!(PyNumberRMatmulProtocol, Self::__rmatmul__),
)
}
fn get_imatmul() -> TypedSlot<ffi::binaryfunc>
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<T>(
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<T>(
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<T> = 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<T>(
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::<crate::PyCell<T>>(slf);
let other = py.from_borrowed_ptr::<crate::PyAny>(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__);

View file

@ -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<ffi::PyType_Slot> {
vec![]
}
fn get_buffer() -> Option<PyBufferProcs> {
fn for_each_proto_slot<Visitor: FnMut(ffi::PyType_Slot)>(_visitor: Visitor) {}
fn get_buffer() -> Option<&'static PyBufferProcs> {
None
}
}
/// Typed version of `ffi::PyType_Slot`
#[doc(hidden)]
pub struct TypedSlot<T: Sized>(pub std::os::raw::c_int, pub T);
pub struct PyClassProtocols<T>(PhantomData<T>);
#[doc(hidden)]
pub enum PyProtoMethodDef {
Slots(Vec<ffi::PyType_Slot>),
Buffer(PyBufferProcs),
}
impl From<Vec<ffi::PyType_Slot>> for PyProtoMethodDef {
fn from(slots: Vec<ffi::PyType_Slot>) -> Self {
PyProtoMethodDef::Slots(slots)
impl<T> PyClassProtocols<T> {
pub fn new() -> Self {
Self(PhantomData)
}
}
impl From<PyBufferProcs> for PyProtoMethodDef {
fn from(buffer_procs: PyBufferProcs) -> Self {
PyProtoMethodDef::Buffer(buffer_procs)
impl<T> Default for PyClassProtocols<T> {
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<T: HasProtoInventory> PyProtoMethods for T {
fn get_type_slots() -> Vec<ffi::PyType_Slot> {
inventory::iter::<T::ProtoMethods>
.into_iter()
.filter_map(|def| match def.get() {
PyProtoMethodDef::Slots(slots) => Some(slots),
PyProtoMethodDef::Buffer(_) => None,
})
.flatten()
.cloned()
.collect()
}
fn get_buffer() -> Option<PyBufferProcs> {
inventory::iter::<T::ProtoMethods>
.into_iter()
.find_map(|def| match def.get() {
PyProtoMethodDef::Slots(_) => None,
PyProtoMethodDef::Buffer(buf) => Some(buf.clone()),
})
impl<T> Clone for PyClassProtocols<T> {
fn clone(&self) -> Self {
Self::new()
}
}
impl<T> Copy for PyClassProtocols<T> {}
// All traits describing slots, as well as the fallback implementations for unimplemented protos
//
// Protos which are implented use dtolnay specialization to implement for PyClassProtocols<T>.
//
// See https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md
pub trait PyObjectProtocolSlots<T> {
fn object_protocol_slots(self) -> &'static [ffi::PyType_Slot];
}
impl<T> PyObjectProtocolSlots<T> for &'_ PyClassProtocols<T> {
fn object_protocol_slots(self) -> &'static [ffi::PyType_Slot] {
&[]
}
}
pub trait PyDescrProtocolSlots<T> {
fn descr_protocol_slots(self) -> &'static [ffi::PyType_Slot];
}
impl<T> PyDescrProtocolSlots<T> for &'_ PyClassProtocols<T> {
fn descr_protocol_slots(self) -> &'static [ffi::PyType_Slot] {
&[]
}
}
pub trait PyGCProtocolSlots<T> {
fn gc_protocol_slots(self) -> &'static [ffi::PyType_Slot];
}
impl<T> PyGCProtocolSlots<T> for &'_ PyClassProtocols<T> {
fn gc_protocol_slots(self) -> &'static [ffi::PyType_Slot] {
&[]
}
}
pub trait PyIterProtocolSlots<T> {
fn iter_protocol_slots(self) -> &'static [ffi::PyType_Slot];
}
impl<T> PyIterProtocolSlots<T> for &'_ PyClassProtocols<T> {
fn iter_protocol_slots(self) -> &'static [ffi::PyType_Slot] {
&[]
}
}
pub trait PyMappingProtocolSlots<T> {
fn mapping_protocol_slots(self) -> &'static [ffi::PyType_Slot];
}
impl<T> PyMappingProtocolSlots<T> for &'_ PyClassProtocols<T> {
fn mapping_protocol_slots(self) -> &'static [ffi::PyType_Slot] {
&[]
}
}
pub trait PyNumberProtocolSlots<T> {
fn number_protocol_slots(self) -> &'static [ffi::PyType_Slot];
}
impl<T> PyNumberProtocolSlots<T> for &'_ PyClassProtocols<T> {
fn number_protocol_slots(self) -> &'static [ffi::PyType_Slot] {
&[]
}
}
pub trait PyAsyncProtocolSlots<T> {
fn async_protocol_slots(self) -> &'static [ffi::PyType_Slot];
}
impl<T> PyAsyncProtocolSlots<T> for &'_ PyClassProtocols<T> {
fn async_protocol_slots(self) -> &'static [ffi::PyType_Slot] {
&[]
}
}
pub trait PySequenceProtocolSlots<T> {
fn sequence_protocol_slots(self) -> &'static [ffi::PyType_Slot];
}
impl<T> PySequenceProtocolSlots<T> for &'_ PyClassProtocols<T> {
fn sequence_protocol_slots(self) -> &'static [ffi::PyType_Slot] {
&[]
}
}
pub trait PyBufferProtocolSlots<T> {
fn buffer_protocol_slots(self) -> &'static [ffi::PyType_Slot];
}
impl<T> PyBufferProtocolSlots<T> for &'_ PyClassProtocols<T> {
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<T> {
fn buffer_procs(self) -> Option<&'static PyBufferProcs>;
}
impl<T> PyBufferProtocolProcs<T> for &'_ PyClassProtocols<T> {
fn buffer_procs(self) -> Option<&'static PyBufferProcs> {
None
}
}

View file

@ -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<PyObject>;
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
pub trait PyAsyncSlots {
fn get_await() -> TypedSlot<ffi::unaryfunc>
where
Self: for<'p> PyAsyncAwaitProtocol<'p>,
{
TypedSlot(
ffi::Py_am_await,
py_unarys_func!(PyAsyncAwaitProtocol, Self::__await__),
)
}
fn get_aiter() -> TypedSlot<ffi::unaryfunc>
where
Self: for<'p> PyAsyncAiterProtocol<'p>,
{
TypedSlot(
ffi::Py_am_aiter,
py_unarys_func!(PyAsyncAiterProtocol, Self::__aiter__),
)
}
fn get_anext() -> TypedSlot<ffi::unaryfunc>
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<T, U> {

View file

@ -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<Self>;
}
/// 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<ffi::lenfunc>
where
Self: for<'p> PySequenceLenProtocol<'p>,
{
TypedSlot(
ffi::Py_sq_length,
py_len_func!(PySequenceLenProtocol, Self::__len__),
)
}
pub unsafe extern "C" fn setitem<T>(
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::<PyCell<T>>(slf);
fn get_concat() -> TypedSlot<ffi::binaryfunc>
where
Self: for<'p> PySequenceConcatProtocol<'p>,
{
TypedSlot(
ffi::Py_sq_concat,
py_binary_func!(PySequenceConcatProtocol, Self::__concat__),
)
}
fn get_repeat() -> TypedSlot<ffi::ssizeargfunc>
where
Self: for<'p> PySequenceRepeatProtocol<'p>,
{
TypedSlot(
ffi::Py_sq_repeat,
py_ssizearg_func!(PySequenceRepeatProtocol, Self::__repeat__),
)
}
fn get_getitem() -> TypedSlot<ffi::ssizeargfunc>
where
Self: for<'p> PySequenceGetItemProtocol<'p>,
{
TypedSlot(
ffi::Py_sq_item,
py_ssizearg_func!(PySequenceGetItemProtocol, Self::__getitem__),
)
}
fn get_setitem() -> TypedSlot<ffi::ssizeobjargproc>
where
Self: for<'p> PySequenceSetItemProtocol<'p>,
{
unsafe extern "C" fn wrap<T>(
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::<PyCell<T>>(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::<PyAny>(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::<Self>)
}
fn get_delitem() -> TypedSlot<ffi::ssizeobjargproc>
where
Self: for<'p> PySequenceDelItemProtocol<'p>,
{
unsafe extern "C" fn wrap<T>(
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::<PyCell<T>>(slf);
if value.is_null() {
crate::callback::convert(py, slf.borrow_mut().__delitem__(key.into()))
} else {
Err(PyErr::new::<exceptions::PyNotImplementedError, _>(format!(
"Item assignment not supported by {:?}",
stringify!(T)
)))
}
})
}
TypedSlot(ffi::Py_sq_ass_item, wrap::<Self>)
}
fn get_setdelitem() -> TypedSlot<ffi::ssizeobjargproc>
where
Self: for<'p> PySequenceDelItemProtocol<'p> + for<'p> PySequenceSetItemProtocol<'p>,
{
unsafe extern "C" fn wrap<T>(
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::<PyCell<T>>(slf);
if value.is_null() {
call_mut!(slf, __delitem__; key.into()).convert(py)
} else {
let value = py.from_borrowed_ptr::<PyAny>(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::<Self>)
}
fn get_contains() -> TypedSlot<ffi::objobjproc>
where
Self: for<'p> PySequenceContainsProtocol<'p>,
{
TypedSlot(
ffi::Py_sq_contains,
py_binary_func!(PySequenceContainsProtocol, Self::__contains__, c_int),
)
}
fn get_inplace_concat() -> TypedSlot<ffi::binaryfunc>
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<ffi::ssizeargfunc>
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::<PyAny>(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<T>(
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::<PyCell<T>>(slf);
if value.is_null() {
crate::callback::convert(py, slf.borrow_mut().__delitem__(key.into()))
} else {
Err(PyErr::new::<exceptions::PyNotImplementedError, _>(format!(
"Item assignment not supported by {:?}",
stringify!(T)
)))
}
})
}
#[doc(hidden)]
pub unsafe extern "C" fn setdelitem<T>(
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::<PyCell<T>>(slf);
if value.is_null() {
call_mut!(slf, __delitem__; key.into()).convert(py)
} else {
let value = py.from_borrowed_ptr::<PyAny>(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
);

View file

@ -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;

View file

@ -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())
}
}};
}

View file

@ -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<T: PyClass>(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;