py_class!: add `__hash__` slot
This commit is contained in:
parent
933e0ed11d
commit
c06838a38e
|
@ -276,6 +276,15 @@ py_class!(class MyIterator |py| {
|
|||
Special method that is used by the `format()` builtin and the `str.format()` method.
|
||||
Possible return types are `PyResult<String>` or `PyResult<PyString>`.
|
||||
|
||||
## Comparison operators
|
||||
|
||||
TODO: implement support for `__cmp__`, `__lt__`, `__le__`, `__gt__`, `__ge__`, `__eq__`, `__ne__`.
|
||||
|
||||
* `def __hash__(&self) -> PyResult<impl PrimInt>`
|
||||
|
||||
Objects that compare equal must have the same hash value.
|
||||
The return type must be `PyResult<T>` where `T` is one of Rust's primitive integer types.
|
||||
|
||||
*/
|
||||
#[macro_export]
|
||||
macro_rules! py_class {
|
||||
|
|
|
@ -595,7 +595,9 @@ special_names = {
|
|||
'__eq__': unimplemented(),
|
||||
'__ne__': unimplemented(),
|
||||
'__cmp__': unimplemented(),
|
||||
'__hash__': unimplemented(),
|
||||
'__hash__': unary_operator('tp_hash',
|
||||
res_conv='$crate::py_class::slots::HashConverter',
|
||||
res_ffi_type='$crate::Py_hash_t'),
|
||||
'__nonzero__': error('__nonzero__ is not supported by py_class!; use the Python 3 spelling __bool__ instead.'),
|
||||
'__bool__': unimplemented(),
|
||||
# Customizing attribute access
|
||||
|
|
|
@ -478,11 +478,34 @@ macro_rules! py_class_impl {
|
|||
} => {
|
||||
py_error! { "__gt__ is not supported by py_class! yet." }
|
||||
};
|
||||
{ $class:ident $py:ident $info:tt
|
||||
/* slots: */ {
|
||||
/* type_slots */ [ $( $tp_slot_name:ident : $tp_slot_value:expr, )* ]
|
||||
$as_number:tt $as_sequence:tt
|
||||
}
|
||||
{ $( $imp:item )* }
|
||||
$members:tt;
|
||||
def __hash__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)*
|
||||
} => { py_class_impl! {
|
||||
$class $py $info
|
||||
/* slots: */ {
|
||||
/* type_slots */ [
|
||||
$( $tp_slot_name : $tp_slot_value, )*
|
||||
tp_hash: py_class_unary_slot!($class::__hash__, $crate::Py_hash_t, $crate::py_class::slots::HashConverter),
|
||||
]
|
||||
$as_number $as_sequence
|
||||
}
|
||||
/* impl: */ {
|
||||
$($imp)*
|
||||
py_class_impl_item! { $class, $py, __hash__(&$slf,) $res_type; { $($body)* } [] }
|
||||
}
|
||||
$members; $($tail)*
|
||||
}};
|
||||
// def __hash__()
|
||||
{ $class:ident $py:ident $info:tt $slots:tt $impls:tt $members:tt;
|
||||
def __hash__ $($tail:tt)*
|
||||
} => {
|
||||
py_error! { "__hash__ is not supported by py_class! yet." }
|
||||
py_error! { "Invalid signature for unary operator __hash__" }
|
||||
};
|
||||
// def __iadd__()
|
||||
{ $class:ident $py:ident $info:tt $slots:tt $impls:tt $members:tt;
|
||||
|
|
|
@ -23,6 +23,7 @@ use conversion::ToPyObject;
|
|||
use function::CallbackConverter;
|
||||
use err::{PyErr};
|
||||
use exc;
|
||||
use Py_hash_t;
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
|
@ -199,3 +200,49 @@ impl <T> CallbackConverter<Option<T>, *mut ffi::PyObject>
|
|||
}
|
||||
}
|
||||
|
||||
pub trait WrappingCastTo<T> {
|
||||
fn wrapping_cast(self) -> T;
|
||||
}
|
||||
|
||||
macro_rules! wrapping_cast {
|
||||
($from:ty, $to:ty) => {
|
||||
impl WrappingCastTo<$to> for $from {
|
||||
#[inline]
|
||||
fn wrapping_cast(self) -> $to {
|
||||
self as $to
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
wrapping_cast!(u8, Py_hash_t);
|
||||
wrapping_cast!(u16, Py_hash_t);
|
||||
wrapping_cast!(u32, Py_hash_t);
|
||||
wrapping_cast!(usize, Py_hash_t);
|
||||
wrapping_cast!(u64, Py_hash_t);
|
||||
wrapping_cast!(i8, Py_hash_t);
|
||||
wrapping_cast!(i16, Py_hash_t);
|
||||
wrapping_cast!(i32, Py_hash_t);
|
||||
wrapping_cast!(isize, Py_hash_t);
|
||||
wrapping_cast!(i64, Py_hash_t);
|
||||
|
||||
pub struct HashConverter;
|
||||
|
||||
impl <T> CallbackConverter<T, Py_hash_t> for HashConverter
|
||||
where T: WrappingCastTo<Py_hash_t>
|
||||
{
|
||||
#[inline]
|
||||
fn convert(val: T, _py: Python) -> Py_hash_t {
|
||||
let hash = val.wrapping_cast();
|
||||
if hash == -1 {
|
||||
-2
|
||||
} else {
|
||||
hash
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn error_value() -> Py_hash_t {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -385,3 +385,27 @@ fn python3_string_methods() {
|
|||
}
|
||||
|
||||
|
||||
py_class!(class Comparisons |py| {
|
||||
data val: i32;
|
||||
|
||||
def __hash__(&self) -> PyResult<i32> {
|
||||
Ok(*self.val(py))
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
#[test]
|
||||
fn comparisons() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let one = Comparisons::create_instance(py, 1).unwrap();
|
||||
let ten = Comparisons::create_instance(py, 10).unwrap();
|
||||
let minus_one = Comparisons::create_instance(py, -1).unwrap();
|
||||
py_assert!(py, one, "hash(one) == 1");
|
||||
py_assert!(py, ten, "hash(ten) == 10");
|
||||
py_assert!(py, minus_one, "hash(minus_one) == -2");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue