py_class!: add __contains__ and __reversed__
This commit is contained in:
parent
5886720c90
commit
9eafaebfa2
|
@ -310,6 +310,17 @@ TODO: implement support for `__cmp__`, `__lt__`, `__le__`, `__gt__`, `__ge__`, `
|
||||||
|
|
||||||
Called by Python `del self[key]`.
|
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
|
## Other Special Methods
|
||||||
|
|
||||||
* `def __bool__(&self) -> PyResult<bool>`
|
* `def __bool__(&self) -> PyResult<bool>`
|
||||||
|
|
|
@ -519,6 +519,9 @@ def operator(special_name, slot,
|
||||||
if res_type == '()':
|
if res_type == '()':
|
||||||
res_conv = '$crate::py_class::slots::UnitCallbackConverter'
|
res_conv = '$crate::py_class::slots::UnitCallbackConverter'
|
||||||
res_ffi_type = '$crate::_detail::libc::c_int'
|
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':
|
elif res_type == 'PyObject':
|
||||||
res_conv = '$crate::_detail::PyObjectCallbackConverter'
|
res_conv = '$crate::_detail::PyObjectCallbackConverter'
|
||||||
else:
|
else:
|
||||||
|
@ -582,8 +585,7 @@ special_names = {
|
||||||
res_ffi_type='$crate::Py_hash_t'),
|
res_ffi_type='$crate::Py_hash_t'),
|
||||||
'__nonzero__': error('__nonzero__ is not supported by py_class!; use the Python 3 spelling __bool__ instead.'),
|
'__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',
|
'__bool__': operator('nb_nonzero' if PY2 else 'nb_bool',
|
||||||
res_conv='$crate::py_class::slots::BoolConverter',
|
res_type='bool'),
|
||||||
res_ffi_type='$crate::_detail::libc::c_int'),
|
|
||||||
# Customizing attribute access
|
# Customizing attribute access
|
||||||
'__getattr__': unimplemented(),
|
'__getattr__': unimplemented(),
|
||||||
'__getattribute__': unimplemented(),
|
'__getattribute__': unimplemented(),
|
||||||
|
@ -627,8 +629,10 @@ special_names = {
|
||||||
'__iter__': operator('tp_iter'),
|
'__iter__': operator('tp_iter'),
|
||||||
'__next__': operator('tp_iternext',
|
'__next__': operator('tp_iternext',
|
||||||
res_conv='$crate::py_class::slots::IterNextResultConverter'),
|
res_conv='$crate::py_class::slots::IterNextResultConverter'),
|
||||||
'__reversed__': unimplemented(),
|
'__reversed__': normal_method(),
|
||||||
'__contains__': unimplemented(),
|
'__contains__': operator('sq_contains',
|
||||||
|
args=[Argument('item', '&PyObject')],
|
||||||
|
res_type='bool'),
|
||||||
|
|
||||||
# Emulating numeric types
|
# Emulating numeric types
|
||||||
'__add__': unimplemented(),
|
'__add__': unimplemented(),
|
||||||
|
|
|
@ -414,9 +414,35 @@ macro_rules! py_class_impl {
|
||||||
{ { def __complex__ $($tail:tt)* } $( $stuff:tt )* } => {
|
{ { def __complex__ $($tail:tt)* } $( $stuff:tt )* } => {
|
||||||
py_error! { "__complex__ is not supported by py_class! yet." }
|
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 )* } => {
|
{ { 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 )* } => {
|
{ { def __del__ $($tail:tt)* } $( $stuff:tt )* } => {
|
||||||
|
@ -895,10 +921,6 @@ macro_rules! py_class_impl {
|
||||||
py_error! { "Invalid signature for operator __repr__" }
|
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 )* } => {
|
{ { def __rfloordiv__ $($tail:tt)* } $( $stuff:tt )* } => {
|
||||||
py_error! { "__rfloordiv__ is not supported by py_class! yet." }
|
py_error! { "__rfloordiv__ is not supported by py_class! yet." }
|
||||||
};
|
};
|
||||||
|
|
|
@ -414,9 +414,35 @@ macro_rules! py_class_impl {
|
||||||
{ { def __complex__ $($tail:tt)* } $( $stuff:tt )* } => {
|
{ { def __complex__ $($tail:tt)* } $( $stuff:tt )* } => {
|
||||||
py_error! { "__complex__ is not supported by py_class! yet." }
|
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 )* } => {
|
{ { 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 )* } => {
|
{ { def __del__ $($tail:tt)* } $( $stuff:tt )* } => {
|
||||||
|
@ -895,10 +921,6 @@ macro_rules! py_class_impl {
|
||||||
py_error! { "Invalid signature for operator __repr__" }
|
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 )* } => {
|
{ { def __rfloordiv__ $($tail:tt)* } $( $stuff:tt )* } => {
|
||||||
py_error! { "__rfloordiv__ is not supported by py_class! yet." }
|
py_error! { "__rfloordiv__ is not supported by py_class! yet." }
|
||||||
};
|
};
|
||||||
|
|
|
@ -540,3 +540,34 @@ fn setdelitem() {
|
||||||
assert_eq!(c.val(py).get(), None);
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue