__contains__: if extraction fails with TypeError, return False instead.

This commit is contained in:
Daniel Grunwald 2016-05-07 23:31:46 +02:00
parent 9eafaebfa2
commit 04081bc8de
6 changed files with 41 additions and 15 deletions

View file

@ -321,6 +321,9 @@ TODO: implement support for `__cmp__`, `__lt__`, `__le__`, `__gt__`, `__ge__`, `
For mapping types, this should consider the keys of the mapping rather than the values For mapping types, this should consider the keys of the mapping rather than the values
or the key-item pairs. or the key-item pairs.
If extraction of the `item` parameter fails with `TypeError`,
`__contains__` will return `Ok(false)`.
## Other Special Methods ## Other Special Methods
* `def __bool__(&self) -> PyResult<bool>` * `def __bool__(&self) -> PyResult<bool>`

View file

@ -506,6 +506,10 @@ def special_class_method(special_name, *args, **kwargs):
generate_class_method(special_name=special_name, *args, **kwargs) generate_class_method(special_name=special_name, *args, **kwargs)
Argument = namedtuple('Argument', ['name', 'default_type']) Argument = namedtuple('Argument', ['name', 'default_type'])
class Argument(object):
def __init__(self, name, extract_err='passthrough'):
self.name = name
self.extract_err = 'py_class_extract_error_%s' % extract_err
@special_method @special_method
def operator(special_name, slot, def operator(special_name, slot,
@ -535,8 +539,8 @@ def operator(special_name, slot,
new_slots = [(slot, 'py_class_unary_slot!($class::%s, %s, %s)' new_slots = [(slot, 'py_class_unary_slot!($class::%s, %s, %s)'
% (special_name, res_ffi_type, res_conv))] % (special_name, res_ffi_type, res_conv))]
elif len(args) == 1: elif len(args) == 1:
new_slots = [(slot, 'py_class_binary_slot!($class::%s, $%s_type, %s, %s)' new_slots = [(slot, 'py_class_binary_slot!($class::%s, $%s_type, %s, %s, %s)'
% (special_name, args[0].name, res_ffi_type, res_conv))] % (special_name, args[0].name, args[0].extract_err, res_ffi_type, res_conv))]
elif len(args) == 2: elif len(args) == 2:
new_slots = [(slot, 'py_class_ternary_slot!($class::%s, $%s_type, $%s_type, %s, %s)' new_slots = [(slot, 'py_class_ternary_slot!($class::%s, $%s_type, $%s_type, %s, %s)'
% (special_name, args[0].name, args[1].name, res_ffi_type, res_conv))] % (special_name, args[0].name, args[1].name, res_ffi_type, res_conv))]
@ -615,23 +619,23 @@ special_names = {
]), ]),
'__length_hint__': normal_method(), '__length_hint__': normal_method(),
'__getitem__': operator('mp_subscript', '__getitem__': operator('mp_subscript',
args=[Argument('key', '&PyObject')], args=[Argument('key')],
additional_slots=[ additional_slots=[
('sq_item', 'Some($crate::py_class::slots::sq_item)') ('sq_item', 'Some($crate::py_class::slots::sq_item)')
]), ]),
'__missing__': normal_method(), '__missing__': normal_method(),
'__setitem__': operator('sdi_setitem', '__setitem__': operator('sdi_setitem',
args=[Argument('key', '&PyObject'), Argument('value', '&PyObject')], args=[Argument('key'), Argument('value')],
res_type='()'), res_type='()'),
'__delitem__': operator('sdi_delitem', '__delitem__': operator('sdi_delitem',
args=[Argument('key', '&PyObject')], args=[Argument('key')],
res_type='()'), res_type='()'),
'__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__': normal_method(), '__reversed__': normal_method(),
'__contains__': operator('sq_contains', '__contains__': operator('sq_contains',
args=[Argument('item', '&PyObject')], args=[Argument('item', extract_err='false')],
res_type='bool'), res_type='bool'),
# Emulating numeric types # Emulating numeric types

View file

@ -430,7 +430,7 @@ macro_rules! py_class_impl {
$type_slots $as_number $type_slots $as_number
/* as_sequence */ [ /* as_sequence */ [
$( $sq_slot_name : $sq_slot_value, )* $( $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), sq_contains: py_class_binary_slot!($class::__contains__, $item_type, py_class_extract_error_false, $crate::_detail::libc::c_int, $crate::py_class::slots::BoolConverter),
] ]
$as_mapping $setdelitem $as_mapping $setdelitem
} }
@ -474,7 +474,7 @@ macro_rules! py_class_impl {
$type_slots $as_number $as_sequence $as_mapping $type_slots $as_number $as_sequence $as_mapping
/* setdelitem */ [ /* setdelitem */ [
sdi_setitem: $sdi_setitem_slot_value, sdi_setitem: $sdi_setitem_slot_value,
sdi_delitem: { py_class_binary_slot!($class::__delitem__, $key_type, $crate::_detail::libc::c_int, $crate::py_class::slots::UnitCallbackConverter) }, sdi_delitem: { py_class_binary_slot!($class::__delitem__, $key_type, py_class_extract_error_passthrough, $crate::_detail::libc::c_int, $crate::py_class::slots::UnitCallbackConverter) },
] ]
} }
/* impl: */ { /* impl: */ {
@ -556,7 +556,7 @@ macro_rules! py_class_impl {
] ]
/* as_mapping */ [ /* as_mapping */ [
$( $mp_slot_name : $mp_slot_value, )* $( $mp_slot_name : $mp_slot_value, )*
mp_subscript: py_class_binary_slot!($class::__getitem__, $key_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), mp_subscript: py_class_binary_slot!($class::__getitem__, $key_type, py_class_extract_error_passthrough, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter),
] ]
$setdelitem $setdelitem
} }

View file

@ -430,7 +430,7 @@ macro_rules! py_class_impl {
$type_slots $as_number $type_slots $as_number
/* as_sequence */ [ /* as_sequence */ [
$( $sq_slot_name : $sq_slot_value, )* $( $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), sq_contains: py_class_binary_slot!($class::__contains__, $item_type, py_class_extract_error_false, $crate::_detail::libc::c_int, $crate::py_class::slots::BoolConverter),
] ]
$as_mapping $setdelitem $as_mapping $setdelitem
} }
@ -474,7 +474,7 @@ macro_rules! py_class_impl {
$type_slots $as_number $as_sequence $as_mapping $type_slots $as_number $as_sequence $as_mapping
/* setdelitem */ [ /* setdelitem */ [
sdi_setitem: $sdi_setitem_slot_value, sdi_setitem: $sdi_setitem_slot_value,
sdi_delitem: { py_class_binary_slot!($class::__delitem__, $key_type, $crate::_detail::libc::c_int, $crate::py_class::slots::UnitCallbackConverter) }, sdi_delitem: { py_class_binary_slot!($class::__delitem__, $key_type, py_class_extract_error_passthrough, $crate::_detail::libc::c_int, $crate::py_class::slots::UnitCallbackConverter) },
] ]
} }
/* impl: */ { /* impl: */ {
@ -556,7 +556,7 @@ macro_rules! py_class_impl {
] ]
/* as_mapping */ [ /* as_mapping */ [
$( $mp_slot_name : $mp_slot_value, )* $( $mp_slot_name : $mp_slot_value, )*
mp_subscript: py_class_binary_slot!($class::__getitem__, $key_type, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter), mp_subscript: py_class_binary_slot!($class::__getitem__, $key_type, py_class_extract_error_passthrough, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter),
] ]
$setdelitem $setdelitem
} }

View file

@ -253,7 +253,7 @@ macro_rules! py_class_unary_slot {
#[macro_export] #[macro_export]
#[doc(hidden)] #[doc(hidden)]
macro_rules! py_class_binary_slot { macro_rules! py_class_binary_slot {
($class:ident :: $f:ident, $arg_type:ty, $res_type:ty, $conv:expr) => {{ ($class:ident :: $f:ident, $arg_type:ty, $extract_err:ident, $res_type:ty, $conv:expr) => {{
unsafe extern "C" fn wrap_binary<DUMMY>( unsafe extern "C" fn wrap_binary<DUMMY>(
slf: *mut $crate::_detail::ffi::PyObject, slf: *mut $crate::_detail::ffi::PyObject,
arg: *mut $crate::_detail::ffi::PyObject) arg: *mut $crate::_detail::ffi::PyObject)
@ -269,10 +269,10 @@ macro_rules! py_class_binary_slot {
Ok(prepared) => { Ok(prepared) => {
match <$arg_type as $crate::ExtractPyObject>::extract(py, &prepared) { match <$arg_type as $crate::ExtractPyObject>::extract(py, &prepared) {
Ok(arg) => slf.$f(py, arg), Ok(arg) => slf.$f(py, arg),
Err(e) => Err(e) Err(e) => $extract_err!(py, e)
} }
}, },
Err(e) => Err(e) Err(e) => $extract_err!(py, e)
}; };
$crate::PyDrop::release_ref(arg, py); $crate::PyDrop::release_ref(arg, py);
$crate::PyDrop::release_ref(slf, py); $crate::PyDrop::release_ref(slf, py);
@ -329,6 +329,24 @@ macro_rules! py_class_ternary_slot {
}} }}
} }
#[macro_export]
#[doc(hidden)]
macro_rules! py_class_extract_error_passthrough {
($py: ident, $e:ident) => (Err($e));
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_class_extract_error_false {
($py: ident, $e:ident) => {
if $e.matches($py, $py.get_type::<$crate::exc::TypeError>()) {
Ok(false)
} else {
Err($e)
}
};
}
pub struct UnitCallbackConverter; pub struct UnitCallbackConverter;
impl CallbackConverter<()> for UnitCallbackConverter { impl CallbackConverter<()> for UnitCallbackConverter {

View file

@ -569,5 +569,6 @@ fn contains() {
let c = Contains::create_instance(py).unwrap(); let c = Contains::create_instance(py).unwrap();
py_run!(py, c, "assert 1 in c"); py_run!(py, c, "assert 1 in c");
py_run!(py, c, "assert -1 not in c"); py_run!(py, c, "assert -1 not in c");
py_run!(py, c, "assert 'wrong type' not in c");
} }