py_class!: add __contains__ and __reversed__

This commit is contained in:
Daniel Grunwald 2016-05-07 23:19:26 +02:00
parent 5886720c90
commit 9eafaebfa2
5 changed files with 104 additions and 14 deletions

View file

@ -310,6 +310,17 @@ TODO: implement support for `__cmp__`, `__lt__`, `__le__`, `__gt__`, `__ge__`, `
Called by Python `del self[key]`.
* `def __reversed__(&self) -> PyResult<impl ToPyObject>`
Called by the `reversed()` built-in.
It should return a new iterator object that iterates over all the objects in the container in reverse order.
* `def __contains__(&self, item: impl ExtractPyObject) -> PyResult<bool>`
Called by Python `item in self`.
For mapping types, this should consider the keys of the mapping rather than the values
or the key-item pairs.
## Other Special Methods
* `def __bool__(&self) -> PyResult<bool>`

View file

@ -519,6 +519,9 @@ def operator(special_name, slot,
if res_type == '()':
res_conv = '$crate::py_class::slots::UnitCallbackConverter'
res_ffi_type = '$crate::_detail::libc::c_int'
elif res_type == 'bool':
res_conv = '$crate::py_class::slots::BoolConverter'
res_ffi_type = '$crate::_detail::libc::c_int'
elif res_type == 'PyObject':
res_conv = '$crate::_detail::PyObjectCallbackConverter'
else:
@ -582,8 +585,7 @@ special_names = {
res_ffi_type='$crate::Py_hash_t'),
'__nonzero__': error('__nonzero__ is not supported by py_class!; use the Python 3 spelling __bool__ instead.'),
'__bool__': operator('nb_nonzero' if PY2 else 'nb_bool',
res_conv='$crate::py_class::slots::BoolConverter',
res_ffi_type='$crate::_detail::libc::c_int'),
res_type='bool'),
# Customizing attribute access
'__getattr__': unimplemented(),
'__getattribute__': unimplemented(),
@ -627,8 +629,10 @@ special_names = {
'__iter__': operator('tp_iter'),
'__next__': operator('tp_iternext',
res_conv='$crate::py_class::slots::IterNextResultConverter'),
'__reversed__': unimplemented(),
'__contains__': unimplemented(),
'__reversed__': normal_method(),
'__contains__': operator('sq_contains',
args=[Argument('item', '&PyObject')],
res_type='bool'),
# Emulating numeric types
'__add__': unimplemented(),

View file

@ -414,9 +414,35 @@ macro_rules! py_class_impl {
{ { def __complex__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__complex__ is not supported by py_class! yet." }
};
{ { def __contains__(&$slf:ident, $item:ident : $item_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
$class:ident $py:ident $info:tt
/* slots: */ {
$type_slots:tt $as_number:tt
/* as_sequence */ [ $( $sq_slot_name:ident : $sq_slot_value:expr, )* ]
$as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots $as_number
/* as_sequence */ [
$( $sq_slot_name : $sq_slot_value, )*
sq_contains: py_class_binary_slot!($class::__contains__, $item_type, $crate::_detail::libc::c_int, $crate::py_class::slots::BoolConverter),
]
$as_mapping $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __contains__(&$slf,) $res_type; { $($body)* } [{ $item : $item_type = {} }] }
}
$members
}};
{ { def __contains__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__contains__ is not supported by py_class! yet." }
py_error! { "Invalid signature for operator __contains__" }
};
{ { def __del__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -895,10 +921,6 @@ macro_rules! py_class_impl {
py_error! { "Invalid signature for operator __repr__" }
};
{ { def __reversed__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__reversed__ is not supported by py_class! yet." }
};
{ { def __rfloordiv__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rfloordiv__ is not supported by py_class! yet." }
};

View file

@ -414,9 +414,35 @@ macro_rules! py_class_impl {
{ { def __complex__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__complex__ is not supported by py_class! yet." }
};
{ { def __contains__(&$slf:ident, $item:ident : $item_type:ty) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
$class:ident $py:ident $info:tt
/* slots: */ {
$type_slots:tt $as_number:tt
/* as_sequence */ [ $( $sq_slot_name:ident : $sq_slot_value:expr, )* ]
$as_mapping:tt $setdelitem:tt
}
{ $( $imp:item )* }
$members:tt
} => { py_class_impl! {
{ $($tail)* }
$class $py $info
/* slots: */ {
$type_slots $as_number
/* as_sequence */ [
$( $sq_slot_name : $sq_slot_value, )*
sq_contains: py_class_binary_slot!($class::__contains__, $item_type, $crate::_detail::libc::c_int, $crate::py_class::slots::BoolConverter),
]
$as_mapping $setdelitem
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __contains__(&$slf,) $res_type; { $($body)* } [{ $item : $item_type = {} }] }
}
$members
}};
{ { def __contains__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__contains__ is not supported by py_class! yet." }
py_error! { "Invalid signature for operator __contains__" }
};
{ { def __del__ $($tail:tt)* } $( $stuff:tt )* } => {
@ -895,10 +921,6 @@ macro_rules! py_class_impl {
py_error! { "Invalid signature for operator __repr__" }
};
{ { def __reversed__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__reversed__ is not supported by py_class! yet." }
};
{ { def __rfloordiv__ $($tail:tt)* } $( $stuff:tt )* } => {
py_error! { "__rfloordiv__ is not supported by py_class! yet." }
};

View file

@ -540,3 +540,34 @@ fn setdelitem() {
assert_eq!(c.val(py).get(), None);
}
py_class!(class Reversed |py| {
def __reversed__(&self) -> PyResult<&'static str> {
Ok("I am reversed")
}
});
#[test]
fn reversed() {
let gil = Python::acquire_gil();
let py = gil.python();
let c = Reversed::create_instance(py).unwrap();
py_run!(py, c, "assert reversed(c) == 'I am reversed'");
}
py_class!(class Contains |py| {
def __contains__(&self, item: i32) -> PyResult<bool> {
Ok(item >= 0)
}
});
#[test]
fn contains() {
let gil = Python::acquire_gil();
let py = gil.python();
let c = Contains::create_instance(py).unwrap();
py_run!(py, c, "assert 1 in c");
py_run!(py, c, "assert -1 not in c");
}