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)
//! 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::os::raw::c_int;
@ -185,12 +188,7 @@ where
type_object.tp_hash = Self::tp_hash();
type_object.tp_getattro = Self::tp_getattro();
type_object.tp_richcompare = Self::tp_richcompare();
type_object.tp_setattro = if let Some(df) = Self::tp_delattro() {
Some(df)
} else {
Self::tp_setattro()
};
type_object.tp_setattro = tp_setattro_impl::tp_setattro::<Self>();
}
fn nb_bool_fn() -> Option<ffi::inquiry> {
Self::nb_bool()
@ -202,7 +200,9 @@ trait PyObjectGetAttrProtocolImpl {
None
}
}
impl<'p, T> PyObjectGetAttrProtocolImpl for T where T: PyObjectProtocol<'p> {}
impl<T> PyObjectGetAttrProtocolImpl for T
where
T: for<'p> PyObjectGetAttrProtocol<'p>,
@ -217,48 +217,87 @@ where
}
}
trait PyObjectSetAttrProtocolImpl {
fn tp_setattro() -> Option<ffi::setattrofunc> {
None
}
}
/// An object may support setting attributes (by implementing PyObjectSetAttrProtocol)
/// and may support deleting attributes (by implementing PyObjectDelAttrProtocol)
/// 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> {}
impl<T> PyObjectSetAttrProtocolImpl for T
where
T: for<'p> PyObjectSetAttrProtocol<'p>,
{
fn tp_setattro() -> Option<ffi::setattrofunc> {
py_func_set!(PyObjectSetAttrProtocol, T::__setattr__)
/// setattrofunc PyTypeObject.tp_setattro
///
/// An optional pointer to the function for setting and deleting attributes.
///
/// The signature is the same as for PyObject_SetAttr(), but setting v to NULL to delete an
/// attribute must be supported. It is usually convenient to set this field to
/// PyObject_GenericSetAttr(), which implements the normal way of setting object attributes.
pub(super) fn tp_setattro<'p, T: PyObjectProtocol<'p>>() -> Option<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 {
fn tp_delattro() -> Option<ffi::setattrofunc> {
None
trait SetAttr {
fn set_attr() -> Option<ffi::setattrofunc> {
None
}
}
}
impl<'p, T> PyObjectDelAttrProtocolImpl for T where T: PyObjectProtocol<'p> {}
impl<T> PyObjectDelAttrProtocolImpl for T
where
T: for<'p> PyObjectDelAttrProtocol<'p>,
{
default fn tp_delattro() -> Option<ffi::setattrofunc> {
py_func_del!(PyObjectDelAttrProtocol, T::__delattr__)
impl<'p, T: PyObjectProtocol<'p>> SetAttr for T {}
impl<T> SetAttr for T
where
T: for<'p> PyObjectSetAttrProtocol<'p>,
{
fn set_attr() -> Option<ffi::setattrofunc> {
py_func_set!(PyObjectSetAttrProtocol, T, __setattr__)
}
}
}
impl<T> PyObjectDelAttrProtocolImpl for T
where
T: for<'p> PyObjectSetAttrProtocol<'p> + for<'p> PyObjectDelAttrProtocol<'p>,
{
fn tp_delattro() -> Option<ffi::setattrofunc> {
py_func_set_del!(
PyObjectSetAttrProtocol,
PyObjectDelAttrProtocol,
T,
__setattr__,
__delattr__
)
trait DelAttr {
fn del_attr() -> Option<ffi::setattrofunc> {
None
}
}
impl<'p, T> DelAttr for T where T: PyObjectProtocol<'p> {}
impl<T> DelAttr for T
where
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)]
macro_rules! py_func_set {
($trait:ident, $class:ident :: $f:ident) => {{
unsafe extern "C" fn wrap<T>(
($trait_name:ident, $generic:ident, $fn_set:ident) => {{
unsafe extern "C" fn wrap<$generic>(
slf: *mut $crate::ffi::PyObject,
name: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
) -> $crate::libc::c_int
where
T: for<'p> $trait<'p>,
T: for<'p> $trait_name<'p>,
{
use $crate::ObjectProtocol;
let _pool = $crate::GILPool::new();
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() {
let e = $crate::PyErr::new::<exc::NotImplementedError, _>(format!(
result = Err($crate::PyErr::new::<exc::NotImplementedError, _>(format!(
"Subscript deletion not supported by {:?}",
stringify!(T)
));
e.restore(py);
-1
stringify!($generic)
)));
} else {
let name = py.mut_from_borrowed_ptr::<$crate::PyObjectRef>(name);
let value = py.from_borrowed_ptr::<$crate::PyObjectRef>(value);
let result = match name.extract() {
result = match name.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()),
};
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
}
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
}
}
Some(wrap::<T>)
Some(wrap::<$generic>)
}};
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_func_del {
($trait:ident, $class:ident :: $f:ident) => {{
#[allow(unused_mut)]
unsafe extern "C" fn wrap<T>(
($trait_name:ident, $generic:ident, $fn_del:ident) => {{
unsafe extern "C" fn wrap<$generic>(
slf: *mut $crate::ffi::PyObject,
name: *mut $crate::ffi::PyObject,
value: *mut $crate::ffi::PyObject,
) -> $crate::libc::c_int
where
T: for<'p> $trait<'p>,
T: for<'p> $trait_name<'p>,
{
use $crate::ObjectProtocol;
let _pool = $crate::GILPool::new();
let py = $crate::Python::assume_gil_acquired();
let result;
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 result = match name.extract() {
Ok(name) => slf.$f(name).into(),
result = match name.extract() {
Ok(name) => slf.$fn_del(name).into(),
Err(e) => Err(e.into()),
};
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
}
} else {
let e = PyErr::new::<exc::NotImplementedError, _>(format!(
result = Err(PyErr::new::<exc::NotImplementedError, _>(format!(
"Subscript assignment not supported by {:?}",
stringify!(T)
));
e.restore(py);
-1
stringify!($generic)
)));
}
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 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 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]
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]
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 slf = py.mut_from_borrowed_ptr::<T>(slf);
let result;
if value.is_null() {
let result = slf.__delitem__(key as isize).into();
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
}
result = slf.__delitem__(key as isize).into();
} else {
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(),
Err(e) => Err(e),
};
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
}
match result {
Ok(_) => 0,
Err(e) => {
e.restore(py);
-1
}
}
}