Rewrite the set attr / del attr function to avoid specialization

This commit is contained in:
konstin 2018-08-26 18:40:50 +02:00
parent 7c0379b13a
commit 6d9ee7fc38
4 changed files with 131 additions and 102 deletions

View file

@ -4,6 +4,9 @@
//! //!
//! Check [python c-api information](https://docs.python.org/3/reference/datamodel.html#basic-customization) //! Check [python c-api information](https://docs.python.org/3/reference/datamodel.html#basic-customization)
//! for more information. //! for more information.
//!
//! Parts of the documentation are copied from the respective methods from the
//! [typeobj docs](https://docs.python.org/3/c-api/typeobj.html)
use std; use std;
use std::os::raw::c_int; use std::os::raw::c_int;
@ -185,12 +188,7 @@ where
type_object.tp_hash = Self::tp_hash(); type_object.tp_hash = Self::tp_hash();
type_object.tp_getattro = Self::tp_getattro(); type_object.tp_getattro = Self::tp_getattro();
type_object.tp_richcompare = Self::tp_richcompare(); type_object.tp_richcompare = Self::tp_richcompare();
type_object.tp_setattro = tp_setattro_impl::tp_setattro::<Self>();
type_object.tp_setattro = if let Some(df) = Self::tp_delattro() {
Some(df)
} else {
Self::tp_setattro()
};
} }
fn nb_bool_fn() -> Option<ffi::inquiry> { fn nb_bool_fn() -> Option<ffi::inquiry> {
Self::nb_bool() Self::nb_bool()
@ -202,7 +200,9 @@ trait PyObjectGetAttrProtocolImpl {
None None
} }
} }
impl<'p, T> PyObjectGetAttrProtocolImpl for T where T: PyObjectProtocol<'p> {} impl<'p, T> PyObjectGetAttrProtocolImpl for T where T: PyObjectProtocol<'p> {}
impl<T> PyObjectGetAttrProtocolImpl for T impl<T> PyObjectGetAttrProtocolImpl for T
where where
T: for<'p> PyObjectGetAttrProtocol<'p>, T: for<'p> PyObjectGetAttrProtocol<'p>,
@ -217,48 +217,87 @@ where
} }
} }
trait PyObjectSetAttrProtocolImpl { /// An object may support setting attributes (by implementing PyObjectSetAttrProtocol)
fn tp_setattro() -> Option<ffi::setattrofunc> { /// and may support deleting attributes (by implementing PyObjectDelAttrProtocol)
None /// and we need to generate a single extern c function that supports only setting, only deleting
} /// or both, and return None in case none of the two is supported.
} mod tp_setattro_impl {
use super::*;
impl<'p, T> PyObjectSetAttrProtocolImpl for T where T: PyObjectProtocol<'p> {} /// setattrofunc PyTypeObject.tp_setattro
impl<T> PyObjectSetAttrProtocolImpl for T ///
where /// An optional pointer to the function for setting and deleting attributes.
T: for<'p> PyObjectSetAttrProtocol<'p>, ///
{ /// The signature is the same as for PyObject_SetAttr(), but setting v to NULL to delete an
fn tp_setattro() -> Option<ffi::setattrofunc> { /// attribute must be supported. It is usually convenient to set this field to
py_func_set!(PyObjectSetAttrProtocol, T::__setattr__) /// PyObject_GenericSetAttr(), which implements the normal way of setting object attributes.
pub(super) fn tp_setattro<'p, T: PyObjectProtocol<'p>>() -> Option<ffi::setattrofunc> {
if let Some(set_del) = T::set_del_attr() {
Some(set_del)
} else if let Some(set) = T::set_attr() {
Some(set)
} else if let Some(del) = T::del_attr() {
Some(del)
} else {
None
}
} }
}
trait PyObjectDelAttrProtocolImpl { trait SetAttr {
fn tp_delattro() -> Option<ffi::setattrofunc> { fn set_attr() -> Option<ffi::setattrofunc> {
None None
}
} }
}
impl<'p, T> PyObjectDelAttrProtocolImpl for T where T: PyObjectProtocol<'p> {} impl<'p, T: PyObjectProtocol<'p>> SetAttr for T {}
impl<T> PyObjectDelAttrProtocolImpl for T
where impl<T> SetAttr for T
T: for<'p> PyObjectDelAttrProtocol<'p>, where
{ T: for<'p> PyObjectSetAttrProtocol<'p>,
default fn tp_delattro() -> Option<ffi::setattrofunc> { {
py_func_del!(PyObjectDelAttrProtocol, T::__delattr__) fn set_attr() -> Option<ffi::setattrofunc> {
py_func_set!(PyObjectSetAttrProtocol, T, __setattr__)
}
} }
}
impl<T> PyObjectDelAttrProtocolImpl for T trait DelAttr {
where fn del_attr() -> Option<ffi::setattrofunc> {
T: for<'p> PyObjectSetAttrProtocol<'p> + for<'p> PyObjectDelAttrProtocol<'p>, None
{ }
fn tp_delattro() -> Option<ffi::setattrofunc> { }
py_func_set_del!(
PyObjectSetAttrProtocol, impl<'p, T> DelAttr for T where T: PyObjectProtocol<'p> {}
PyObjectDelAttrProtocol,
T, impl<T> DelAttr for T
__setattr__, where
__delattr__ T: for<'p> PyObjectDelAttrProtocol<'p>,
) {
fn del_attr() -> Option<ffi::setattrofunc> {
py_func_del!(PyObjectDelAttrProtocol, T, __delattr__)
}
}
trait SetDelAttr {
fn set_del_attr() -> Option<ffi::setattrofunc> {
None
}
}
impl<'p, T> SetDelAttr for T where T: PyObjectProtocol<'p> {}
impl<T> SetDelAttr for T
where
T: for<'p> PyObjectSetAttrProtocol<'p> + for<'p> PyObjectDelAttrProtocol<'p>,
{
fn set_del_attr() -> Option<ffi::setattrofunc> {
py_func_set_del!(
PyObjectSetAttrProtocol,
PyObjectDelAttrProtocol,
T,
__setattr__,
__delattr__
)
}
} }
} }

View file

@ -310,99 +310,95 @@ macro_rules! py_ternary_self_func {
}}; }};
} }
#[macro_export]
#[doc(hidden)] #[doc(hidden)]
macro_rules! py_func_set { macro_rules! py_func_set {
($trait:ident, $class:ident :: $f:ident) => {{ ($trait_name:ident, $generic:ident, $fn_set:ident) => {{
unsafe extern "C" fn wrap<T>( unsafe extern "C" fn wrap<$generic>(
slf: *mut $crate::ffi::PyObject, slf: *mut $crate::ffi::PyObject,
name: *mut $crate::ffi::PyObject, name: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject, value: *mut $crate::ffi::PyObject,
) -> $crate::libc::c_int ) -> $crate::libc::c_int
where where
T: for<'p> $trait<'p>, T: for<'p> $trait_name<'p>,
{ {
use $crate::ObjectProtocol; use $crate::ObjectProtocol;
let _pool = $crate::GILPool::new(); let _pool = $crate::GILPool::new();
let py = $crate::Python::assume_gil_acquired(); let py = $crate::Python::assume_gil_acquired();
let slf = py.mut_from_borrowed_ptr::<T>(slf); let slf = py.mut_from_borrowed_ptr::<$generic>(slf);
let result;
if value.is_null() { if value.is_null() {
let e = $crate::PyErr::new::<exc::NotImplementedError, _>(format!( result = Err($crate::PyErr::new::<exc::NotImplementedError, _>(format!(
"Subscript deletion not supported by {:?}", "Subscript deletion not supported by {:?}",
stringify!(T) stringify!($generic)
)); )));
e.restore(py);
-1
} else { } else {
let name = py.mut_from_borrowed_ptr::<$crate::PyObjectRef>(name); let name = py.mut_from_borrowed_ptr::<$crate::PyObjectRef>(name);
let value = py.from_borrowed_ptr::<$crate::PyObjectRef>(value); let value = py.from_borrowed_ptr::<$crate::PyObjectRef>(value);
let result = match name.extract() { result = match name.extract() {
Ok(name) => match value.extract() { Ok(name) => match value.extract() {
Ok(value) => slf.$f(name, value).into(), Ok(value) => slf.$fn_set(name, value).into(),
Err(e) => Err(e.into()), Err(e) => Err(e.into()),
}, },
Err(e) => Err(e.into()), Err(e) => Err(e.into()),
}; };
match result { }
Ok(_) => 0, match result {
Err(e) => { Ok(_) => 0,
e.restore(py); Err(e) => {
-1 e.restore(py);
} -1
} }
} }
} }
Some(wrap::<T>) Some(wrap::<$generic>)
}}; }};
} }
#[macro_export]
#[doc(hidden)] #[doc(hidden)]
macro_rules! py_func_del { macro_rules! py_func_del {
($trait:ident, $class:ident :: $f:ident) => {{ ($trait_name:ident, $generic:ident, $fn_del:ident) => {{
#[allow(unused_mut)] unsafe extern "C" fn wrap<$generic>(
unsafe extern "C" fn wrap<T>(
slf: *mut $crate::ffi::PyObject, slf: *mut $crate::ffi::PyObject,
name: *mut $crate::ffi::PyObject, name: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject, value: *mut $crate::ffi::PyObject,
) -> $crate::libc::c_int ) -> $crate::libc::c_int
where where
T: for<'p> $trait<'p>, T: for<'p> $trait_name<'p>,
{ {
use $crate::ObjectProtocol; use $crate::ObjectProtocol;
let _pool = $crate::GILPool::new(); let _pool = $crate::GILPool::new();
let py = $crate::Python::assume_gil_acquired(); let py = $crate::Python::assume_gil_acquired();
let result;
if value.is_null() { if value.is_null() {
let slf = py.mut_from_borrowed_ptr::<T>(slf); let slf = py.mut_from_borrowed_ptr::<$generic>(slf);
let name = py.from_borrowed_ptr::<$crate::PyObjectRef>(name); let name = py.from_borrowed_ptr::<$crate::PyObjectRef>(name);
let result = match name.extract() { result = match name.extract() {
Ok(name) => slf.$f(name).into(), Ok(name) => slf.$fn_del(name).into(),
Err(e) => Err(e.into()), Err(e) => Err(e.into()),
}; };
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
}
} else { } else {
let e = PyErr::new::<exc::NotImplementedError, _>(format!( result = Err(PyErr::new::<exc::NotImplementedError, _>(format!(
"Subscript assignment not supported by {:?}", "Subscript assignment not supported by {:?}",
stringify!(T) stringify!($generic)
)); )));
e.restore(py); }
-1
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
} }
} }
Some(wrap::<T>) Some(wrap::<$generic>)
}}; }};
} }
@ -421,7 +417,7 @@ macro_rules! py_func_set_del {
let _pool = $crate::GILPool::new(); let _pool = $crate::GILPool::new();
let py = $crate::Python::assume_gil_acquired(); let py = $crate::Python::assume_gil_acquired();
let slf = py.mut_from_borrowed_ptr::<T>(slf); let slf = py.mut_from_borrowed_ptr::<$generic>(slf);
let name = py.from_borrowed_ptr::<$crate::PyObjectRef>(name); let name = py.from_borrowed_ptr::<$crate::PyObjectRef>(name);
let result; let result;
@ -448,6 +444,6 @@ macro_rules! py_func_set_del {
} }
} }
} }
Some(wrap::<T>) Some(wrap::<$generic>)
}}; }};
} }

View file

@ -208,7 +208,7 @@ where
{ {
#[inline] #[inline]
fn mp_ass_subscript() -> Option<ffi::objobjargproc> { fn mp_ass_subscript() -> Option<ffi::objobjargproc> {
py_func_set!(PyMappingSetItemProtocol, T::__setitem__) py_func_set!(PyMappingSetItemProtocol, T, __setitem__)
} }
} }
@ -226,7 +226,7 @@ where
{ {
#[inline] #[inline]
default fn mp_del_subscript() -> Option<ffi::objobjargproc> { default fn mp_del_subscript() -> Option<ffi::objobjargproc> {
py_func_del!(PyMappingDelItemProtocol, T::__delitem__) py_func_del!(PyMappingDelItemProtocol, T, __delitem__)
} }
} }

View file

@ -330,27 +330,21 @@ where
let py = Python::assume_gil_acquired(); let py = Python::assume_gil_acquired();
let slf = py.mut_from_borrowed_ptr::<T>(slf); let slf = py.mut_from_borrowed_ptr::<T>(slf);
let result;
if value.is_null() { if value.is_null() {
let result = slf.__delitem__(key as isize).into(); result = slf.__delitem__(key as isize).into();
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
}
} else { } else {
let value = py.from_borrowed_ptr::<PyObjectRef>(value); let value = py.from_borrowed_ptr::<PyObjectRef>(value);
let result = match value.extract() { result = match value.extract() {
Ok(value) => slf.__setitem__(key as isize, value).into(), Ok(value) => slf.__setitem__(key as isize, value).into(),
Err(e) => Err(e), Err(e) => Err(e),
}; };
match result { }
Ok(_) => 0, match result {
Err(e) => { Ok(_) => 0,
e.restore(py); Err(e) => {
-1 e.restore(py);
} -1
} }
} }
} }