Renew PyProtoMethods for new ABI3-based type construction

This commit is contained in:
kngwyu 2020-10-17 16:59:56 +09:00
parent f74b64993e
commit eb8ff1581b
11 changed files with 888 additions and 909 deletions

View File

@ -133,94 +133,109 @@ pub trait PyObjectRichcmpProtocol<'p>: PyObjectProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
/// All FFI functions for basic protocols.
#[derive(Default)]
pub struct PyObjectMethods {
pub tp_str: Option<ffi::reprfunc>,
pub tp_repr: Option<ffi::reprfunc>,
pub tp_hash: Option<ffi::hashfunc>,
pub tp_getattro: Option<ffi::getattrofunc>,
pub tp_richcompare: Option<ffi::richcmpfunc>,
pub tp_setattro: Option<ffi::setattrofunc>,
pub nb_bool: Option<ffi::inquiry>,
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
impl PyObjectMethods {
// Set functions used by `#[pyproto]`.
pub fn set_str<T>(&mut self)
pub trait PyBasicSlots {
fn get_str() -> ffi::PyType_Slot
where
T: for<'p> PyObjectStrProtocol<'p>,
Self: for<'p> PyObjectStrProtocol<'p>,
{
self.tp_str = py_unary_func!(PyObjectStrProtocol, T::__str__);
}
pub fn set_repr<T>(&mut self)
where
T: for<'p> PyObjectReprProtocol<'p>,
{
self.tp_repr = py_unary_func!(PyObjectReprProtocol, T::__repr__);
}
pub fn set_hash<T>(&mut self)
where
T: for<'p> PyObjectHashProtocol<'p>,
{
self.tp_hash = py_unary_func!(PyObjectHashProtocol, T::__hash__, ffi::Py_hash_t);
}
pub fn set_getattr<T>(&mut self)
where
T: for<'p> PyObjectGetAttrProtocol<'p>,
{
self.tp_getattro = tp_getattro::<T>();
}
pub fn set_richcompare<T>(&mut self)
where
T: for<'p> PyObjectRichcmpProtocol<'p>,
{
self.tp_richcompare = tp_richcompare::<T>();
}
pub fn set_setattr<T>(&mut self)
where
T: for<'p> PyObjectSetAttrProtocol<'p>,
{
self.tp_setattro = py_func_set!(PyObjectSetAttrProtocol, T, __setattr__);
}
pub fn set_delattr<T>(&mut self)
where
T: for<'p> PyObjectDelAttrProtocol<'p>,
{
self.tp_setattro = py_func_del!(PyObjectDelAttrProtocol, T, __delattr__);
}
pub fn set_setdelattr<T>(&mut self)
where
T: for<'p> PyObjectSetAttrProtocol<'p> + for<'p> PyObjectDelAttrProtocol<'p>,
{
self.tp_setattro = py_func_set_del!(
PyObjectSetAttrProtocol,
PyObjectDelAttrProtocol,
T,
__setattr__,
__delattr__
)
}
pub fn set_bool<T>(&mut self)
where
T: for<'p> PyObjectBoolProtocol<'p>,
{
self.nb_bool = py_unary_func!(PyObjectBoolProtocol, T::__bool__, c_int);
ffi::PyType_Slot {
slot: ffi::Py_tp_str,
pfunc: py_unary_func!(PyObjectStrProtocol, Self::__str__) as _,
}
}
pub(crate) fn update_slots(&self, slots: &mut crate::pyclass::TypeSlots) {
slots.maybe_push(ffi::Py_tp_str, self.tp_str.map(|v| v as _));
slots.maybe_push(ffi::Py_tp_repr, self.tp_repr.map(|v| v as _));
slots.maybe_push(ffi::Py_tp_hash, self.tp_hash.map(|v| v as _));
slots.maybe_push(ffi::Py_tp_getattro, self.tp_getattro.map(|v| v as _));
slots.maybe_push(ffi::Py_tp_richcompare, self.tp_richcompare.map(|v| v as _));
slots.maybe_push(ffi::Py_tp_setattro, self.tp_setattro.map(|v| v as _));
slots.maybe_push(ffi::Py_nb_bool, self.nb_bool.map(|v| v as _));
fn get_repr() -> ffi::PyType_Slot
where
Self: for<'p> PyObjectReprProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_tp_repr,
pfunc: py_unary_func!(PyObjectReprProtocol, Self::__repr__) as _,
}
}
fn get_hash() -> ffi::PyType_Slot
where
Self: for<'p> PyObjectHashProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_tp_hash,
pfunc: py_unary_func!(PyObjectHashProtocol, Self::__hash__, ffi::Py_hash_t) as _,
}
}
fn get_getattr() -> ffi::PyType_Slot
where
Self: for<'p> PyObjectGetAttrProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_tp_getattro,
pfunc: tp_getattro::<Self>() as _,
}
}
fn get_richcompare() -> ffi::PyType_Slot
where
Self: for<'p> PyObjectRichcmpProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_tp_getattro,
pfunc: tp_richcompare::<Self>() as _,
}
}
fn get_setattr() -> ffi::PyType_Slot
where
Self: for<'p> PyObjectSetAttrProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_tp_setattro,
pfunc: py_func_set!(PyObjectSetAttrProtocol, Self::__setattr__) as _,
}
}
fn get_delattr() -> ffi::PyType_Slot
where
Self: for<'p> PyObjectDelAttrProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_tp_setattro,
pfunc: py_func_del!(PyObjectDelAttrProtocol, Self::__delattr__) as _,
}
}
fn get_setdelattr() -> ffi::PyType_Slot
where
Self: for<'p> PyObjectSetAttrProtocol<'p> + for<'p> PyObjectDelAttrProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_tp_setattro,
pfunc: py_func_set_del!(
PyObjectSetAttrProtocol,
PyObjectDelAttrProtocol,
Self,
__setattr__,
__delattr__
) as _,
}
}
fn get_bool() -> ffi::PyType_Slot
where
Self: for<'p> PyObjectBoolProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_nb_bool,
pfunc: py_unary_func!(PyObjectBoolProtocol, Self::__bool__, c_int) as _,
}
}
}
fn tp_getattro<T>() -> Option<ffi::binaryfunc>
impl<'p, T> PyBasicSlots for T where T: PyObjectProtocol<'p> {}
fn tp_getattro<T>() -> ffi::binaryfunc
where
T: for<'p> PyObjectGetAttrProtocol<'p>,
{
@ -247,10 +262,10 @@ where
call_ref!(slf, __getattr__, arg).convert(py)
})
}
Some(wrap::<T>)
wrap::<T>
}
fn tp_richcompare<T>() -> Option<ffi::richcmpfunc>
fn tp_richcompare<T>() -> ffi::richcmpfunc
where
T: for<'p> PyObjectRichcmpProtocol<'p>,
{
@ -283,5 +298,5 @@ where
slf.try_borrow()?.__richcmp__(arg, op).convert(py)
})
}
Some(wrap::<T>)
wrap::<T>
}

View File

@ -8,7 +8,6 @@
use crate::callback::IntoPyCallbackOutput;
use crate::types::PyAny;
use crate::{ffi, FromPyObject, PyClass, PyObject};
use std::os::raw::c_int;
/// Descriptor interface
#[allow(unused_variables)]
@ -70,29 +69,28 @@ pub trait PyDescrSetNameProtocol<'p>: PyDescrProtocol<'p> {
type Result: IntoPyCallbackOutput<()>;
}
/// All FFI functions for description protocols.
#[derive(Default)]
pub struct PyDescrMethods {
pub tp_descr_get: Option<ffi::descrgetfunc>,
pub tp_descr_set: Option<ffi::descrsetfunc>,
/// Extension trait for our proc-macro backend.
#[doc(hidden)]
pub trait PyDescrSlots {
fn get_descr_get() -> ffi::PyType_Slot
where
Self: for<'p> PyDescrGetProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_tp_descr_get,
pfunc: py_ternarys_func!(PyDescrGetProtocol, Self::__get__) as _,
}
}
fn get_descr_set() -> ffi::PyType_Slot
where
Self: for<'p> PyDescrSetProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_tp_descr_set,
pfunc: py_ternarys_func!(PyDescrSetProtocol, Self::__set__) as _,
}
}
}
#[doc(hidden)]
impl PyDescrMethods {
pub fn set_descr_get<T>(&mut self)
where
T: for<'p> PyDescrGetProtocol<'p>,
{
self.tp_descr_get = py_ternarys_func!(PyDescrGetProtocol, T::__get__);
}
pub fn set_descr_set<T>(&mut self)
where
T: for<'p> PyDescrSetProtocol<'p>,
{
self.tp_descr_set = py_ternarys_func!(PyDescrSetProtocol, T::__set__, c_int);
}
pub(crate) fn update_slots(&self, slots: &mut crate::pyclass::TypeSlots) {
slots.maybe_push(ffi::Py_tp_descr_get, self.tp_descr_get.map(|v| v as _));
slots.maybe_push(ffi::Py_tp_descr_set, self.tp_descr_set.map(|v| v as _));
}
}
impl<'p, T> PyDescrSlots for T where T: PyDescrProtocol<'p> {}

View File

@ -18,35 +18,31 @@ pub trait PyGCProtocol<'p>: PyClass {
pub trait PyGCTraverseProtocol<'p>: PyGCProtocol<'p> {}
pub trait PyGCClearProtocol<'p>: PyGCProtocol<'p> {}
/// All FFI functions for gc protocols.
#[derive(Default)]
pub struct PyGCMethods {
pub tp_traverse: Option<ffi::traverseproc>,
pub tp_clear: Option<ffi::inquiry>,
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
impl PyGCMethods {
pub(crate) fn update_slots(&self, slots: &mut crate::pyclass::TypeSlots) {
slots.maybe_push(ffi::Py_tp_traverse, self.tp_traverse.map(|v| v as _));
slots.maybe_push(ffi::Py_tp_clear, self.tp_clear.map(|v| v as _));
}
pub fn set_traverse<T>(&mut self)
pub trait PyGCSlots {
fn get_traverse() -> ffi::PyType_Slot
where
T: for<'p> PyGCTraverseProtocol<'p>,
Self: for<'p> PyGCTraverseProtocol<'p>,
{
self.tp_traverse = tp_traverse::<T>();
ffi::PyType_Slot {
slot: ffi::Py_tp_traverse,
pfunc: tp_traverse::<Self>() as _,
}
}
pub fn set_clear<T>(&mut self)
fn get_clear() -> ffi::PyType_Slot
where
T: for<'p> PyGCClearProtocol<'p>,
Self: for<'p> PyGCClearProtocol<'p>,
{
self.tp_clear = tp_clear::<T>();
ffi::PyType_Slot {
slot: ffi::Py_tp_clear,
pfunc: tp_clear::<Self>() as _,
}
}
}
impl<'p, T> PyGCSlots for T where T: PyGCProtocol<'p> {}
/// Object visitor for GC.
#[derive(Clone)]
pub struct PyVisit<'p> {
@ -73,7 +69,7 @@ impl<'p> PyVisit<'p> {
}
}
fn tp_traverse<T>() -> Option<ffi::traverseproc>
fn tp_traverse<T>() -> ffi::traverseproc
where
T: for<'p> PyGCTraverseProtocol<'p>,
{
@ -105,10 +101,10 @@ where
}
}
Some(tp_traverse::<T>)
tp_traverse::<T>
}
fn tp_clear<T>() -> Option<ffi::inquiry>
fn tp_clear<T>() -> ffi::inquiry
where
T: for<'p> PyGCClearProtocol<'p>,
{
@ -123,5 +119,5 @@ where
slf.borrow_mut().__clear__();
0
}
Some(tp_clear::<T>)
tp_clear::<T>
}

View File

@ -71,31 +71,30 @@ pub trait PyIterNextProtocol<'p>: PyIterProtocol<'p> {
type Result: IntoPyCallbackOutput<PyIterNextOutput>;
}
#[derive(Default)]
pub struct PyIterMethods {
pub tp_iter: Option<ffi::getiterfunc>,
pub tp_iternext: Option<ffi::iternextfunc>,
/// Extension trait for proc-macro backend.
#[doc(hidden)]
pub trait PyIterSlots {
fn get_iter() -> ffi::PyType_Slot
where
Self: for<'p> PyIterIterProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_tp_iter,
pfunc: py_unarys_func!(PyIterIterProtocol, Self::__iter__) as _,
}
}
fn get_iternext() -> ffi::PyType_Slot
where
Self: for<'p> PyIterNextProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_tp_iternext,
pfunc: py_unarys_func!(PyIterNextProtocol, Self::__next__) as _,
}
}
}
#[doc(hidden)]
impl PyIterMethods {
pub fn set_iter<T>(&mut self)
where
T: for<'p> PyIterIterProtocol<'p>,
{
self.tp_iter = py_unarys_func!(PyIterIterProtocol, T::__iter__);
}
pub fn set_iternext<T>(&mut self)
where
T: for<'p> PyIterNextProtocol<'p>,
{
self.tp_iternext = py_unarys_func!(PyIterNextProtocol, T::__next__);
}
pub(crate) fn update_slots(&self, slots: &mut crate::pyclass::TypeSlots) {
slots.maybe_push(ffi::Py_tp_iter, self.tp_iter.map(|v| v as _));
slots.maybe_push(ffi::Py_tp_iternext, self.tp_iternext.map(|v| v as _));
}
}
impl<'p, T> PyIterSlots for T where T: PyIterProtocol<'p> {}
/// Output of `__next__` which can either `yield` the next value in the iteration, or
/// `return` a value to raise `StopIteration` in Python.

View File

@ -11,7 +11,7 @@ macro_rules! py_unary_func {
$call!(slf, $f).convert(py)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
// Use call_ref! by default
($trait:ident, $class:ident :: $f:ident, $ret_type:ty) => {
@ -34,10 +34,10 @@ macro_rules! py_unarys_func {
<T::Receiver as $crate::derive_utils::TryFromPyCell<_>>::try_from_pycell(slf)
.map_err(|e| e.into())?;
$class::$f(borrow).convert(py)
T::$f(borrow).convert(py)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
}
@ -60,7 +60,7 @@ macro_rules! py_binary_func {
$call!(slf, $f, arg).convert(py)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
($trait:ident, $class:ident :: $f:ident, $return:ty) => {
py_binary_func!($trait, $class::$f, $return, call_ref)
@ -82,10 +82,10 @@ macro_rules! py_binary_num_func {
$crate::callback_body!(py, {
let lhs = py.from_borrowed_ptr::<$crate::PyAny>(lhs);
let rhs = extract_or_return_not_implemented!(py, rhs);
$class::$f(lhs.extract()?, rhs).convert(py)
T::$f(lhs.extract()?, rhs).convert(py)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
}
@ -102,10 +102,10 @@ macro_rules! py_binary_reversed_num_func {
// Swap lhs <-> rhs
let slf: &$crate::PyCell<T> = extract_or_return_not_implemented!(py, rhs);
let arg = extract_or_return_not_implemented!(py, lhs);
$class::$f(&*slf.try_borrow()?, arg).convert(py)
T::$f(&*slf.try_borrow()?, arg).convert(py)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
}
@ -123,17 +123,17 @@ macro_rules! py_binary_fallback_num_func {
let rhs = py.from_borrowed_ptr::<$crate::PyAny>(rhs);
// First, try the left hand method (e.g., __add__)
match (lhs.extract(), rhs.extract()) {
(Ok(l), Ok(r)) => $class::$lop(l, r).convert(py),
(Ok(l), Ok(r)) => T::$lop(l, r).convert(py),
_ => {
// Next, try the right hand method (e.g., __radd__)
let slf: &$crate::PyCell<T> = extract_or_return_not_implemented!(rhs);
let arg = extract_or_return_not_implemented!(lhs);
$class::$rop(&*slf.try_borrow()?, arg).convert(py)
T::$rop(&*slf.try_borrow()?, arg).convert(py)
}
}
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
}
@ -155,7 +155,7 @@ macro_rules! py_binary_self_func {
Ok::<_, $crate::err::PyErr>(slf)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
}
@ -177,7 +177,7 @@ macro_rules! py_ssizearg_func {
$call!(slf, $f; arg.into()).convert(py)
})
}
Some(wrap::<$class>)
wrap::<$class>
}};
}
@ -203,11 +203,11 @@ macro_rules! py_ternarys_func {
.from_borrowed_ptr::<$crate::types::PyAny>(arg2)
.extract()?;
$class::$f(slf, arg1, arg2).convert(py)
T::$f(slf, arg1, arg2).convert(py)
})
}
Some(wrap::<T>)
wrap::<$class>
}};
($trait:ident, $class:ident :: $f:ident) => {
py_ternarys_func!($trait, $class::$f, *mut $crate::ffi::PyObject);
@ -215,8 +215,8 @@ macro_rules! py_ternarys_func {
}
macro_rules! py_func_set {
($trait_name:ident, $generic:ident, $fn_set:ident) => {{
unsafe extern "C" fn wrap<$generic>(
($trait_name:ident, $class:ident :: $fn_set:ident) => {{
unsafe extern "C" fn wrap<T>(
slf: *mut $crate::ffi::PyObject,
name: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
@ -225,12 +225,12 @@ macro_rules! py_func_set {
T: for<'p> $trait_name<'p>,
{
$crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
if value.is_null() {
Err($crate::exceptions::PyNotImplementedError::new_err(format!(
"Subscript deletion not supported by {:?}",
stringify!($generic)
stringify!($class)
)))
} else {
let name = py.from_borrowed_ptr::<$crate::PyAny>(name);
@ -240,23 +240,23 @@ macro_rules! py_func_set {
})
}
Some(wrap::<$generic>)
wrap::<$class>
}};
}
macro_rules! py_func_del {
($trait_name:ident, $generic:ident, $fn_del:ident) => {{
unsafe extern "C" fn wrap<U>(
($trait_name:ident, $class:ident :: $fn_del:ident) => {{
unsafe extern "C" fn wrap<T>(
slf: *mut $crate::ffi::PyObject,
name: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
) -> libc::c_int
where
U: for<'p> $trait_name<'p>,
T: for<'p> $trait_name<'p>,
{
$crate::callback_body!(py, {
if value.is_null() {
let slf = py.from_borrowed_ptr::<$crate::PyCell<U>>(slf);
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let name = py
.from_borrowed_ptr::<$crate::types::PyAny>(name)
.extract()?;
@ -269,13 +269,13 @@ macro_rules! py_func_del {
})
}
Some(wrap::<$generic>)
wrap::<$class>
}};
}
macro_rules! py_func_set_del {
($trait1:ident, $trait2:ident, $generic:ident, $fn_set:ident, $fn_del:ident) => {{
unsafe extern "C" fn wrap<$generic>(
($trait1:ident, $trait2:ident, $class:ident, $fn_set:ident, $fn_del:ident) => {{
unsafe extern "C" fn wrap<T>(
slf: *mut $crate::ffi::PyObject,
name: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
@ -284,7 +284,7 @@ macro_rules! py_func_set_del {
T: for<'p> $trait1<'p> + for<'p> $trait2<'p>,
{
$crate::callback_body!(py, {
let slf = py.from_borrowed_ptr::<$crate::PyCell<$generic>>(slf);
let slf = py.from_borrowed_ptr::<$crate::PyCell<T>>(slf);
let name = py.from_borrowed_ptr::<$crate::PyAny>(name);
if value.is_null() {
@ -295,7 +295,7 @@ macro_rules! py_func_set_del {
}
})
}
Some(wrap::<$generic>)
wrap::<$class>
}};
}

View File

@ -6,17 +6,6 @@
use crate::callback::IntoPyCallbackOutput;
use crate::{exceptions, ffi, FromPyObject, PyClass, PyObject};
#[cfg(Py_LIMITED_API)]
#[derive(Clone, Default)]
pub struct PyMappingMethods {
pub mp_length: Option<ffi::lenfunc>,
pub mp_subscript: Option<ffi::binaryfunc>,
pub mp_ass_subscript: Option<ffi::objobjargproc>,
}
#[cfg(not(Py_LIMITED_API))]
pub use ffi::PyMappingMethods;
/// Mapping interface
#[allow(unused_variables)]
pub trait PyMappingProtocol<'p>: PyClass {
@ -83,50 +72,64 @@ pub trait PyMappingReversedProtocol<'p>: PyMappingProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
impl PyMappingMethods {
pub fn set_length<T>(&mut self)
pub trait PyMappingSlots {
fn get_length() -> ffi::PyType_Slot
where
T: for<'p> PyMappingLenProtocol<'p>,
Self: for<'p> PyMappingLenProtocol<'p>,
{
self.mp_length = py_len_func!(PyMappingLenProtocol, T::__len__);
ffi::PyType_Slot {
slot: ffi::Py_mp_length,
pfunc: py_len_func!(PyMappingLenProtocol, Self::__len__) as _,
}
}
pub fn set_getitem<T>(&mut self)
fn get_getitem() -> ffi::PyType_Slot
where
T: for<'p> PyMappingGetItemProtocol<'p>,
Self: for<'p> PyMappingGetItemProtocol<'p>,
{
self.mp_subscript = py_binary_func!(PyMappingGetItemProtocol, T::__getitem__);
ffi::PyType_Slot {
slot: ffi::Py_mp_subscript,
pfunc: py_binary_func!(PyMappingGetItemProtocol, Self::__getitem__) as _,
}
}
pub fn set_setitem<T>(&mut self)
fn get_setitem() -> ffi::PyType_Slot
where
T: for<'p> PyMappingSetItemProtocol<'p>,
Self: for<'p> PyMappingSetItemProtocol<'p>,
{
self.mp_ass_subscript = py_func_set!(PyMappingSetItemProtocol, T, __setitem__);
ffi::PyType_Slot {
slot: ffi::Py_mp_ass_subscript,
pfunc: py_func_set!(PyMappingSetItemProtocol, Self::__setitem__) as _,
}
}
pub fn set_delitem<T>(&mut self)
fn get_delitem() -> ffi::PyType_Slot
where
T: for<'p> PyMappingDelItemProtocol<'p>,
Self: for<'p> PyMappingDelItemProtocol<'p>,
{
self.mp_ass_subscript = py_func_del!(PyMappingDelItemProtocol, T, __delitem__);
ffi::PyType_Slot {
slot: ffi::Py_mp_ass_subscript,
pfunc: py_func_del!(PyMappingDelItemProtocol, Self::__delitem__) as _,
}
}
pub fn set_setdelitem<T>(&mut self)
fn get_setdelitem() -> ffi::PyType_Slot
where
T: for<'p> PyMappingSetItemProtocol<'p> + for<'p> PyMappingDelItemProtocol<'p>,
Self: for<'p> PyMappingSetItemProtocol<'p> + for<'p> PyMappingDelItemProtocol<'p>,
{
self.mp_ass_subscript = py_func_set_del!(
PyMappingSetItemProtocol,
PyMappingDelItemProtocol,
T,
__setitem__,
__delitem__
);
}
pub(crate) fn update_slots(&self, slots: &mut crate::pyclass::TypeSlots) {
slots.maybe_push(ffi::Py_mp_length, self.mp_length.map(|v| v as _));
slots.maybe_push(ffi::Py_mp_subscript, self.mp_subscript.map(|v| v as _));
slots.maybe_push(
ffi::Py_mp_ass_subscript,
self.mp_ass_subscript.map(|v| v as _),
);
ffi::PyType_Slot {
slot: ffi::Py_mp_ass_subscript,
pfunc: py_func_set_del!(
PyMappingSetItemProtocol,
PyMappingDelItemProtocol,
Self,
__setitem__,
__delitem__
) as _,
}
}
}
impl<'p, T> PyMappingSlots for T where T: PyMappingProtocol<'p> {}

File diff suppressed because it is too large Load Diff

View File

@ -1,160 +1,55 @@
#[cfg(not(Py_LIMITED_API))]
use crate::class::buffer::PyBufferProcs;
use crate::class::{
basic::PyObjectMethods, descr::PyDescrMethods, gc::PyGCMethods, iter::PyIterMethods,
mapping::PyMappingMethods, number::PyNumberMethods, pyasync::PyAsyncMethods,
sequence::PySequenceMethods,
};
use std::{
ptr::{self, NonNull},
sync::atomic::{AtomicPtr, Ordering},
};
use crate::ffi;
/// Defines all method tables we need for object protocols.
// Note(kngwyu): default implementations are for rust-numpy. Please don't remove them.
pub trait PyProtoMethods {
fn async_methods() -> Option<NonNull<PyAsyncMethods>> {
None
fn get_type_slots() -> Vec<ffi::PyType_Slot> {
vec![]
}
fn basic_methods() -> Option<NonNull<PyObjectMethods>> {
None
}
#[cfg(not(Py_LIMITED_API))]
fn buffer_methods() -> Option<NonNull<PyBufferProcs>> {
None
}
fn descr_methods() -> Option<NonNull<PyDescrMethods>> {
None
}
fn gc_methods() -> Option<NonNull<PyGCMethods>> {
None
}
fn mapping_methods() -> Option<NonNull<PyMappingMethods>> {
None
}
fn number_methods() -> Option<NonNull<PyNumberMethods>> {
None
}
fn iter_methods() -> Option<NonNull<PyIterMethods>> {
None
}
fn sequence_methods() -> Option<NonNull<PySequenceMethods>> {
fn get_buffer() -> Option<PyBufferProcs> {
None
}
}
/// Indicates that a type has a protocol registry. Implemented by `#[pyclass]`.
#[doc(hidden)]
pub trait HasProtoRegistry: Sized + 'static {
fn registry() -> &'static PyProtoRegistry;
pub enum PyProtoMethodDef {
Slots(Vec<ffi::PyType_Slot>),
Buffer(PyBufferProcs),
}
impl<T: HasProtoRegistry> PyProtoMethods for T {
fn async_methods() -> Option<NonNull<PyAsyncMethods>> {
NonNull::new(Self::registry().async_methods.load(Ordering::Relaxed))
}
fn basic_methods() -> Option<NonNull<PyObjectMethods>> {
NonNull::new(Self::registry().basic_methods.load(Ordering::Relaxed))
}
#[cfg(not(Py_LIMITED_API))]
fn buffer_methods() -> Option<NonNull<PyBufferProcs>> {
NonNull::new(Self::registry().buffer_methods.load(Ordering::Relaxed))
}
fn descr_methods() -> Option<NonNull<PyDescrMethods>> {
NonNull::new(Self::registry().descr_methods.load(Ordering::Relaxed))
}
fn gc_methods() -> Option<NonNull<PyGCMethods>> {
NonNull::new(Self::registry().gc_methods.load(Ordering::Relaxed))
}
fn mapping_methods() -> Option<NonNull<PyMappingMethods>> {
NonNull::new(Self::registry().mapping_methods.load(Ordering::Relaxed))
}
fn number_methods() -> Option<NonNull<PyNumberMethods>> {
NonNull::new(Self::registry().number_methods.load(Ordering::Relaxed))
}
fn iter_methods() -> Option<NonNull<PyIterMethods>> {
NonNull::new(Self::registry().iter_methods.load(Ordering::Relaxed))
}
fn sequence_methods() -> Option<NonNull<PySequenceMethods>> {
NonNull::new(Self::registry().sequence_methods.load(Ordering::Relaxed))
}
}
/// Stores all method protocols.
/// Used in the proc-macro code as a static variable.
#[doc(hidden)]
pub struct PyProtoRegistry {
/// Async protocols.
async_methods: AtomicPtr<PyAsyncMethods>,
/// Basic protocols.
basic_methods: AtomicPtr<PyObjectMethods>,
/// Buffer protocols.
#[cfg(not(Py_LIMITED_API))]
buffer_methods: AtomicPtr<PyBufferProcs>,
/// Descr pProtocols.
descr_methods: AtomicPtr<PyDescrMethods>,
/// GC protocols.
gc_methods: AtomicPtr<PyGCMethods>,
/// Mapping protocols.
mapping_methods: AtomicPtr<PyMappingMethods>,
/// Number protocols.
number_methods: AtomicPtr<PyNumberMethods>,
/// Iterator protocols.
iter_methods: AtomicPtr<PyIterMethods>,
/// Sequence protocols.
sequence_methods: AtomicPtr<PySequenceMethods>,
#[cfg(feature = "macros")]
pub trait PyProtoMethodsInventory: inventory::Collect {
fn new(methods: PyProtoMethodDef) -> Self;
fn get(&'static self) -> &'static PyProtoMethodDef;
}
impl PyProtoRegistry {
pub const fn new() -> Self {
PyProtoRegistry {
async_methods: AtomicPtr::new(ptr::null_mut()),
basic_methods: AtomicPtr::new(ptr::null_mut()),
#[cfg(not(Py_LIMITED_API))]
buffer_methods: AtomicPtr::new(ptr::null_mut()),
descr_methods: AtomicPtr::new(ptr::null_mut()),
gc_methods: AtomicPtr::new(ptr::null_mut()),
mapping_methods: AtomicPtr::new(ptr::null_mut()),
number_methods: AtomicPtr::new(ptr::null_mut()),
iter_methods: AtomicPtr::new(ptr::null_mut()),
sequence_methods: AtomicPtr::new(ptr::null_mut()),
}
#[doc(hidden)]
#[cfg(feature = "macros")]
pub trait HasProtoMethodsInventory {
type ProtoMethods: PyProtoMethodsInventory;
}
#[cfg(feature = "macros")]
impl<T: HasProtoMethodsInventory> 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()
}
pub fn set_async_methods(&self, methods: PyAsyncMethods) {
self.async_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_basic_methods(&self, methods: PyObjectMethods) {
self.basic_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
#[cfg(not(Py_LIMITED_API))]
pub fn set_buffer_methods(&self, methods: PyBufferProcs) {
self.buffer_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_descr_methods(&self, methods: PyDescrMethods) {
self.descr_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_gc_methods(&self, methods: PyGCMethods) {
self.gc_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_mapping_methods(&self, methods: PyMappingMethods) {
self.mapping_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_number_methods(&self, methods: PyNumberMethods) {
self.number_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_iter_methods(&self, methods: PyIterMethods) {
self.iter_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
}
pub fn set_sequence_methods(&self, methods: PySequenceMethods) {
self.sequence_methods
.store(Box::into_raw(Box::new(methods)), Ordering::Relaxed)
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()),
})
}
}

View File

@ -96,34 +96,43 @@ pub trait PyAsyncAexitProtocol<'p>: PyAsyncProtocol<'p> {
type Result: IntoPyCallbackOutput<PyObject>;
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
impl PyAsyncMethods {
pub fn set_await<T>(&mut self)
pub trait PyAsyncSlots {
fn get_await() -> ffi::PyType_Slot
where
T: for<'p> PyAsyncAwaitProtocol<'p>,
Self: for<'p> PyAsyncAwaitProtocol<'p>,
{
self.am_await = py_unarys_func!(PyAsyncAwaitProtocol, T::__await__);
}
pub fn set_aiter<T>(&mut self)
where
T: for<'p> PyAsyncAiterProtocol<'p>,
{
self.am_aiter = py_unarys_func!(PyAsyncAiterProtocol, T::__aiter__);
}
pub fn set_anext<T>(&mut self)
where
T: for<'p> PyAsyncAnextProtocol<'p>,
{
self.am_anext = am_anext::<T>();
ffi::PyType_Slot {
slot: ffi::Py_am_await,
pfunc: py_unarys_func!(PyAsyncAwaitProtocol, Self::__await__) as _,
}
}
pub(crate) fn update_slots(&self, slots: &mut crate::pyclass::TypeSlots) {
slots.maybe_push(ffi::Py_am_await, self.am_await.map(|v| v as _));
slots.maybe_push(ffi::Py_am_aiter, self.am_aiter.map(|v| v as _));
slots.maybe_push(ffi::Py_am_anext, self.am_anext.map(|v| v as _));
fn get_aiter() -> ffi::PyType_Slot
where
Self: for<'p> PyAsyncAiterProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_am_await,
pfunc: py_unarys_func!(PyAsyncAiterProtocol, Self::__aiter__) as _,
}
}
fn get_anext() -> ffi::PyType_Slot
where
Self: for<'p> PyAsyncAnextProtocol<'p>,
{
ffi::PyType_Slot {
slot: ffi::Py_am_anext,
pfunc: py_unarys_func!(PyAsyncAnextProtocol, Self::__anext__) as _,
}
}
}
impl<'p, T> PyAsyncSlots for T where T: PyAsyncProtocol<'p> {}
/// Output of `__anext__`.
pub enum IterANextOutput<T, U> {
Yield(T),
Return(U),
@ -166,11 +175,3 @@ where
}
}
}
#[inline]
fn am_anext<T>() -> Option<ffi::unaryfunc>
where
T: for<'p> PyAsyncAnextProtocol<'p>,
{
py_unarys_func!(PyAsyncAnextProtocol, T::__anext__)
}

View File

@ -167,103 +167,119 @@ pub trait PySequenceInplaceRepeatProtocol<'p>:
type Result: IntoPyCallbackOutput<Self>;
}
/// Extension trait for proc-macro backend.
#[doc(hidden)]
impl PySequenceMethods {
pub fn set_len<T>(&mut self)
pub trait PySequenceSlots {
fn get_len() -> ffi::PyType_Slot
where
T: for<'p> PySequenceLenProtocol<'p>,
Self: for<'p> PySequenceLenProtocol<'p>,
{
self.sq_length = py_len_func!(PySequenceLenProtocol, T::__len__);
ffi::PyType_Slot {
slot: ffi::Py_sq_length,
pfunc: py_len_func!(PySequenceLenProtocol, Self::__len__) as _,
}
}
pub fn set_concat<T>(&mut self)
fn get_concat() -> ffi::PyType_Slot
where
T: for<'p> PySequenceConcatProtocol<'p>,
Self: for<'p> PySequenceConcatProtocol<'p>,
{
self.sq_concat = py_binary_func!(PySequenceConcatProtocol, T::__concat__);
ffi::PyType_Slot {
slot: ffi::Py_sq_length,
pfunc: py_binary_func!(PySequenceConcatProtocol, Self::__concat__) as _,
}
}
pub fn set_repeat<T>(&mut self)
fn get_repeat() -> ffi::PyType_Slot
where
T: for<'p> PySequenceRepeatProtocol<'p>,
Self: for<'p> PySequenceRepeatProtocol<'p>,
{
self.sq_repeat = py_ssizearg_func!(PySequenceRepeatProtocol, T::__repeat__);
ffi::PyType_Slot {
slot: ffi::Py_sq_length,
pfunc: py_ssizearg_func!(PySequenceRepeatProtocol, Self::__repeat__) as _,
}
}
pub fn set_getitem<T>(&mut self)
fn get_getitem() -> ffi::PyType_Slot
where
T: for<'p> PySequenceGetItemProtocol<'p>,
Self: for<'p> PySequenceGetItemProtocol<'p>,
{
self.sq_item = py_ssizearg_func!(PySequenceGetItemProtocol, T::__getitem__);
ffi::PyType_Slot {
slot: ffi::Py_sq_length,
pfunc: py_ssizearg_func!(PySequenceGetItemProtocol, Self::__getitem__) as _,
}
}
pub fn set_setitem<T>(&mut self)
fn get_setitem() -> ffi::PyType_Slot
where
T: for<'p> PySequenceSetItemProtocol<'p>,
Self: for<'p> PySequenceSetItemProtocol<'p>,
{
self.sq_ass_item = sq_ass_item_impl::set_item::<T>();
ffi::PyType_Slot {
slot: ffi::Py_sq_length,
pfunc: sq_ass_item_impl::set_item::<Self>() as _,
}
}
pub fn set_delitem<T>(&mut self)
fn get_delitem() -> ffi::PyType_Slot
where
T: for<'p> PySequenceDelItemProtocol<'p>,
Self: for<'p> PySequenceDelItemProtocol<'p>,
{
self.sq_ass_item = sq_ass_item_impl::del_item::<T>();
ffi::PyType_Slot {
slot: ffi::Py_sq_length,
pfunc: sq_ass_item_impl::del_item::<Self>() as _,
}
}
pub fn set_setdelitem<T>(&mut self)
fn get_setdelitem() -> ffi::PyType_Slot
where
T: for<'p> PySequenceDelItemProtocol<'p> + for<'p> PySequenceSetItemProtocol<'p>,
Self: for<'p> PySequenceDelItemProtocol<'p> + for<'p> PySequenceSetItemProtocol<'p>,
{
self.sq_ass_item = sq_ass_item_impl::set_del_item::<T>();
ffi::PyType_Slot {
slot: ffi::Py_sq_length,
pfunc: sq_ass_item_impl::set_del_item::<Self>() as _,
}
}
pub fn set_contains<T>(&mut self)
fn get_contains() -> ffi::PyType_Slot
where
T: for<'p> PySequenceContainsProtocol<'p>,
Self: for<'p> PySequenceContainsProtocol<'p>,
{
self.sq_contains = py_binary_func!(PySequenceContainsProtocol, T::__contains__, c_int);
ffi::PyType_Slot {
slot: ffi::Py_sq_length,
pfunc: py_binary_func!(PySequenceContainsProtocol, Self::__contains__, c_int) as _,
}
}
pub fn set_inplace_concat<T>(&mut self)
fn get_inplace_concat() -> ffi::PyType_Slot
where
T: for<'p> PySequenceInplaceConcatProtocol<'p>,
Self: for<'p> PySequenceInplaceConcatProtocol<'p>,
{
self.sq_inplace_concat = py_binary_func!(
PySequenceInplaceConcatProtocol,
T::__inplace_concat__,
*mut ffi::PyObject,
call_mut
)
ffi::PyType_Slot {
slot: ffi::Py_sq_length,
pfunc: py_binary_func!(
PySequenceInplaceConcatProtocol,
Self::__inplace_concat__,
*mut ffi::PyObject,
call_mut
) as _,
}
}
pub fn set_inplace_repeat<T>(&mut self)
fn get_inplace_repeat() -> ffi::PyType_Slot
where
T: for<'p> PySequenceInplaceRepeatProtocol<'p>,
Self: for<'p> PySequenceInplaceRepeatProtocol<'p>,
{
self.sq_inplace_repeat = py_ssizearg_func!(
PySequenceInplaceRepeatProtocol,
T::__inplace_repeat__,
call_mut
)
}
pub(crate) fn update_slots(&self, slots: &mut crate::pyclass::TypeSlots) {
slots.maybe_push(ffi::Py_sq_length, self.sq_length.map(|v| v as _));
slots.maybe_push(ffi::Py_sq_concat, self.sq_concat.map(|v| v as _));
slots.maybe_push(ffi::Py_sq_repeat, self.sq_repeat.map(|v| v as _));
slots.maybe_push(ffi::Py_sq_item, self.sq_item.map(|v| v as _));
slots.maybe_push(ffi::Py_sq_ass_item, self.sq_ass_item.map(|v| v as _));
slots.maybe_push(ffi::Py_sq_contains, self.sq_contains.map(|v| v as _));
slots.maybe_push(
ffi::Py_sq_inplace_concat,
self.sq_inplace_concat.map(|v| v as _),
);
slots.maybe_push(
ffi::Py_sq_inplace_repeat,
self.sq_inplace_repeat.map(|v| v as _),
);
ffi::PyType_Slot {
slot: ffi::Py_sq_length,
pfunc: py_ssizearg_func!(
PySequenceInplaceRepeatProtocol,
Self::__inplace_repeat__,
call_mut
) as _,
}
}
}
impl<'p, T> PySequenceSlots for T where T: PySequenceProtocol<'p> {}
/// It can be possible to delete and set items (PySequenceSetItemProtocol and
/// PySequenceDelItemProtocol implemented), only to delete (PySequenceDelItemProtocol implemented)
/// or no deleting or setting is possible
mod sq_ass_item_impl {
use super::*;
pub(super) fn set_item<T>() -> Option<ffi::ssizeobjargproc>
pub(super) fn set_item<T>() -> ffi::ssizeobjargproc
where
T: for<'p> PySequenceSetItemProtocol<'p>,
{
@ -291,10 +307,10 @@ mod sq_ass_item_impl {
crate::callback::convert(py, slf.__setitem__(key.into(), value))
})
}
Some(wrap::<T>)
wrap::<T>
}
pub(super) fn del_item<T>() -> Option<ffi::ssizeobjargproc>
pub(super) fn del_item<T>() -> ffi::ssizeobjargproc
where
T: for<'p> PySequenceDelItemProtocol<'p>,
{
@ -319,10 +335,10 @@ mod sq_ass_item_impl {
}
})
}
Some(wrap::<T>)
wrap::<T>
}
pub(super) fn set_del_item<T>() -> Option<ffi::ssizeobjargproc>
pub(super) fn set_del_item<T>() -> ffi::ssizeobjargproc
where
T: for<'p> PySequenceSetItemProtocol<'p> + for<'p> PySequenceDelItemProtocol<'p>,
{
@ -347,6 +363,6 @@ mod sq_ass_item_impl {
}
})
}
Some(wrap::<T>)
wrap::<T>
}
}

View File

@ -147,8 +147,8 @@ impl TypeSlots {
self.0.push(ffi::PyType_Slot { slot, pfunc });
}
pub(crate) fn maybe_push(&mut self, slot: c_int, value: Option<*mut c_void>) {
if let Some(v) = value {
self.push(slot, v);
if let Some(pfunc) = value {
self.push(slot, pfunc);
}
}
}
@ -189,6 +189,7 @@ where
let (new, call, methods) = py_class_method_defs::<T>();
slots.maybe_push(ffi::Py_tp_new, new.map(|v| v as _));
slots.maybe_push(ffi::Py_tp_call, call.map(|v| v as _));
// normal methods
if !methods.is_empty() {
slots.push(ffi::Py_tp_methods, into_raw(methods));
@ -200,37 +201,12 @@ where
slots.push(ffi::Py_tp_getset, into_raw(props));
}
// basic methods
if let Some(basic) = T::basic_methods() {
unsafe { basic.as_ref() }.update_slots(&mut slots);
}
// number methods
if let Some(number) = T::number_methods() {
unsafe { number.as_ref() }.update_slots(&mut slots);
}
// iterator methods
if let Some(iter) = T::iter_methods() {
unsafe { iter.as_ref() }.update_slots(&mut slots);
}
// mapping methods
if let Some(mapping) = T::mapping_methods() {
unsafe { mapping.as_ref() }.update_slots(&mut slots);
}
// sequence methods
if let Some(sequence) = T::sequence_methods() {
unsafe { sequence.as_ref() }.update_slots(&mut slots);
}
// descriptor protocol
if let Some(descr) = T::descr_methods() {
unsafe { descr.as_ref() }.update_slots(&mut slots);
}
// async methods
if let Some(asnc) = T::async_methods() {
unsafe { asnc.as_ref() }.update_slots(&mut slots);
}
// gc methods
if let Some(gc) = T::gc_methods() {
unsafe { gc.as_ref() }.update_slots(&mut slots);
// protocol methods
let mut has_gc_methods = false;
for slot in T::get_type_slots() {
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());
@ -238,7 +214,7 @@ where
name: get_type_name::<T>(module_name)?,
basicsize: std::mem::size_of::<T::Layout>() as c_int,
itemsize: 0,
flags: py_class_flags::<T>(),
flags: py_class_flags::<T>(has_gc_methods),
slots: slots.0.as_mut_slice().as_mut_ptr(),
};
@ -274,10 +250,10 @@ fn tp_init_additional<T: PyClass>(type_object: *mut ffi::PyTypeObject) {
}
}
if let Some(buffer) = T::buffer_methods() {
if let Some(buffer) = T::get_buffer() {
unsafe {
(*(*type_object).tp_as_buffer).bf_getbuffer = buffer.as_ref().bf_getbuffer;
(*(*type_object).tp_as_buffer).bf_releasebuffer = buffer.as_ref().bf_releasebuffer;
(*(*type_object).tp_as_buffer).bf_getbuffer = buffer.bf_getbuffer;
(*(*type_object).tp_as_buffer).bf_releasebuffer = buffer.bf_releasebuffer;
}
}
// __dict__ support
@ -297,8 +273,8 @@ fn tp_init_additional<T: PyClass>(type_object: *mut ffi::PyTypeObject) {
#[cfg(Py_LIMITED_API)]
fn tp_init_additional<T: PyClass>(_type_object: *mut ffi::PyTypeObject) {}
fn py_class_flags<T: PyClass + PyTypeInfo>() -> c_uint {
let mut flags = if T::gc_methods().is_some() || T::FLAGS & type_flags::GC != 0 {
fn py_class_flags<T: PyClass + PyTypeInfo>(has_gc_methods: bool) -> c_uint {
let mut flags = if has_gc_methods || T::FLAGS & type_flags::GC != 0 {
ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HAVE_GC
} else {
ffi::Py_TPFLAGS_DEFAULT