Add support for __len__ special method.

This commit is contained in:
Daniel Grunwald 2016-04-17 23:26:33 +02:00
parent a14e3d2b77
commit fffcf57834
13 changed files with 457 additions and 112 deletions

View file

@ -55,7 +55,7 @@ stamps/test-custom_class: custom_class.out
@grep "a() was called with self=42" custom_class.out >/dev/null
@touch $@
#all: stamps/test-btree
all: stamps/test-btree
stamps/test-btree: test_btree.py btree.so
python$(PY) test_btree.py
@touch $@

View file

@ -41,8 +41,12 @@ py_class!(class BTreeSet |py| {
cell::RefCell::new(collections::BTreeSet::new()))
}
def __bool__(&self) -> PyResult<bool> {
Ok(!self.set(py).borrow().is_empty())
// def __bool__(&self) -> PyResult<bool> {
// Ok(!self.set(py).borrow().is_empty())
// }
def __len__(&self) -> PyResult<usize> {
Ok(self.set(py).borrow().len())
}
});

View file

@ -9,7 +9,7 @@ class TestBTreeSet(unittest.TestCase):
def test_empty(self):
# make sure the initial set is empty
self.assertEqual(bool(self.set), False)
# TODO self.assertEqual(bool(self.set), False)
self.assertEqual(len(self.set), 0)
if __name__ == '__main__':

View file

@ -243,6 +243,48 @@ impl Clone for PyNumberMethods {
#[inline] fn clone(&self) -> PyNumberMethods { *self }
}
pub const PyNumberMethods_INIT : PyNumberMethods = PyNumberMethods {
nb_add: None,
nb_subtract: None,
nb_multiply: None,
nb_divide: None,
nb_remainder: None,
nb_divmod: None,
nb_power: None,
nb_negative: None,
nb_positive: None,
nb_absolute: None,
nb_nonzero: None,
nb_invert: None,
nb_lshift: None,
nb_rshift: None,
nb_and: None,
nb_xor: None,
nb_or: None,
nb_coerce: None,
nb_c_int: None,
nb_long: None,
nb_float: None,
nb_oct: None,
nb_hex: None,
nb_inplace_add: None,
nb_inplace_subtract: None,
nb_inplace_multiply: None,
nb_inplace_divide: None,
nb_inplace_remainder: None,
nb_inplace_power: None,
nb_inplace_lshift: None,
nb_inplace_rshift: None,
nb_inplace_and: None,
nb_inplace_xor: None,
nb_inplace_or: None,
nb_floor_divide: None,
nb_true_divide: None,
nb_inplace_floor_divide: None,
nb_inplace_true_divide: None,
nb_index: None,
};
#[repr(C)]
#[derive(Copy)]
pub struct PySequenceMethods {
@ -262,6 +304,19 @@ impl Clone for PySequenceMethods {
#[inline] fn clone(&self) -> PySequenceMethods { *self }
}
pub const PySequenceMethods_INIT : PySequenceMethods = PySequenceMethods {
sq_length: None,
sq_concat: None,
sq_repeat: None,
sq_item: None,
sq_slice: None,
sq_ass_item: None,
sq_ass_slice: None,
sq_contains: None,
sq_inplace_concat: None,
sq_inplace_repeat: None,
};
#[repr(C)]
#[derive(Copy)]
pub struct PyMappingMethods {
@ -274,6 +329,12 @@ impl Clone for PyMappingMethods {
#[inline] fn clone(&self) -> PyMappingMethods { *self }
}
pub const PyMappingMethods_INIT : PyMappingMethods = PyMappingMethods {
mp_length: None,
mp_subscript: None,
mp_ass_subscript: None,
};
#[repr(C)]
#[derive(Copy)]
pub struct PyBufferProcs {
@ -289,6 +350,15 @@ impl Clone for PyBufferProcs {
#[inline] fn clone(&self) -> PyBufferProcs { *self }
}
pub const PyBufferProcs_INIT : PyBufferProcs = PyBufferProcs {
bf_getreadbuffer: None,
bf_getwritebuffer: None,
bf_getsegcount: None,
bf_getcharbuffer: None,
bf_getbuffer: None,
bf_releasebuffer: None,
};
pub type freefunc =
unsafe extern "C" fn(arg1: *mut c_void);
pub type destructor =

View file

@ -287,6 +287,62 @@ mod typeobject {
impl Default for PyNumberMethods {
#[inline] fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
macro_rules! as_expr { ($e:expr) => {$e} }
macro_rules! py_number_methods_init {
($($tail:tt)*) => {
as_expr! {
PyNumberMethods {
nb_add: None,
nb_subtract: None,
nb_multiply: None,
nb_remainder: None,
nb_divmod: None,
nb_power: None,
nb_negative: None,
nb_positive: None,
nb_absolute: None,
nb_bool: None,
nb_invert: None,
nb_lshift: None,
nb_rshift: None,
nb_and: None,
nb_xor: None,
nb_or: None,
nb_int: None,
nb_reserved: 0 as *mut c_void,
nb_float: None,
nb_inplace_add: None,
nb_inplace_subtract: None,
nb_inplace_multiply: None,
nb_inplace_remainder: None,
nb_inplace_power: None,
nb_inplace_lshift: None,
nb_inplace_rshift: None,
nb_inplace_and: None,
nb_inplace_xor: None,
nb_inplace_or: None,
nb_floor_divide: None,
nb_true_divide: None,
nb_inplace_floor_divide: None,
nb_inplace_true_divide: None,
nb_index: None,
$($tail)*
}
}
}
}
#[cfg(not(Py_3_5))]
pub const PyNumberMethods_INIT: PyNumberMethods = py_number_methods_init!();
#[cfg(Py_3_5)]
pub const PyNumberMethods_INIT: PyNumberMethods = py_number_methods_init! {
nb_matrix_multiply: None,
nb_inplace_matrix_multiply: None,
};
#[repr(C)]
#[derive(Copy)]
pub struct PySequenceMethods {
@ -307,6 +363,18 @@ mod typeobject {
impl Default for PySequenceMethods {
#[inline] fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
pub const PySequenceMethods_INIT : PySequenceMethods = PySequenceMethods {
sq_length: None,
sq_concat: None,
sq_repeat: None,
sq_item: None,
was_sq_slice: 0 as *mut _,
sq_ass_item: None,
was_sq_ass_slice: 0 as *mut _,
sq_contains: None,
sq_inplace_concat: None,
sq_inplace_repeat: None,
};
#[repr(C)]
#[derive(Copy)]
pub struct PyMappingMethods {
@ -320,6 +388,11 @@ mod typeobject {
impl Default for PyMappingMethods {
#[inline] fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
pub const PyMappingMethods_INIT : PyMappingMethods = PyMappingMethods {
mp_length: None,
mp_subscript: None,
mp_ass_subscript: None,
};
#[repr(C)]
#[derive(Copy)]
#[cfg(Py_3_5)]
@ -336,6 +409,12 @@ mod typeobject {
impl Default for PyAsyncMethods {
#[inline] fn default() -> Self { unsafe { ::core::mem::zeroed() } }
}
#[cfg(Py_3_5)]
pub const PyAsyncMethods_INIT : PyAsyncMethods = PyAsyncMethods {
am_await: None,
am_aiter: None,
am_anext: None,
};
#[repr(C)]
#[derive(Copy)]
pub struct PyBufferProcs {
@ -419,8 +498,6 @@ mod typeobject {
#[inline] fn clone(&self) -> Self { *self }
}
macro_rules! as_expr { ($e:expr) => {$e} }
macro_rules! py_type_object_init {
($tp_as_async:ident, $($tail:tt)*) => {
as_expr! {

View file

@ -17,7 +17,8 @@
// DEALINGS IN THE SOFTWARE.
use std;
use python::{PythonObject, ToPythonPointer, Python, PythonObjectDowncastError, PythonObjectWithTypeObject, PyClone};
use python::{PythonObject, ToPythonPointer, Python, PythonObjectDowncastError,
PythonObjectWithTypeObject, PyClone, PyDrop};
use objects::{PyObject, PyType, exc};
#[cfg(feature="python27-sys")]
use objects::oldstyle::PyClass;
@ -161,9 +162,12 @@ impl PyErr {
/// Return true if the current exception matches the exception in `exc`.
/// If `exc` is a class object, this also returns `true` when `self` is an instance of a subclass.
/// If `exc` is a tuple, all exceptions in the tuple (and recursively in subtuples) are searched for a match.
#[inline]
pub fn matches(&self, _py: Python, exc: &PyObject) -> bool {
unsafe { ffi::PyErr_GivenExceptionMatches(self.ptype.as_ptr(), exc.as_ptr()) != 0 }
pub fn matches<T>(&self, py: Python, exc: T) -> bool
where T: ToPyObject
{
exc.with_borrowed_ptr(py, |exc| unsafe {
ffi::PyErr_GivenExceptionMatches(self.ptype.as_ptr(), exc) != 0
})
}
/// Normalizes the error. This ensures that the exception value is an instance of the exception type.
@ -244,6 +248,24 @@ impl PyErr {
}
}
impl PyDrop for PyErr {
fn release_ref(self, py: Python) {
self.ptype.release_ref(py);
self.pvalue.release_ref(py);
self.ptraceback.release_ref(py);
}
}
impl PyClone for PyErr {
fn clone_ref(&self, py: Python) -> PyErr {
PyErr {
ptype: self.ptype.clone_ref(py),
pvalue: self.pvalue.clone_ref(py),
ptraceback: self.ptraceback.clone_ref(py)
}
}
}
/// Converts `PythonObjectDowncastError` to Python `TypeError`.
impl <'p> std::convert::From<PythonObjectDowncastError<'p>> for PyErr {
fn from(err: PythonObjectDowncastError<'p>) -> PyErr {

View file

@ -117,7 +117,7 @@ macro_rules! py_fn_impl {
-> *mut $crate::_detail::ffi::PyObject
{
$crate::_detail::handle_callback(
stringify!($f),
stringify!($f), $crate::_detail::PyObjectCallbackConverter,
|py| {
py_argparse_raw!(py, Some(stringify!($f)), args, kwargs,
[ $( { $pname : $ptype = $detail } )* ]
@ -142,56 +142,77 @@ pub unsafe fn py_fn_impl(py: Python, method_def: *mut ffi::PyMethodDef) -> PyObj
err::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(method_def, ptr::null_mut()))
}
pub trait CallbackConverter<T, R> {
fn convert(T, Python) -> R;
fn error_value() -> R;
}
pub struct PyObjectCallbackConverter;
impl <T> CallbackConverter<T, *mut ffi::PyObject> for PyObjectCallbackConverter
where T: ToPyObject
{
fn convert(val: T, py: Python) -> *mut ffi::PyObject {
val.into_py_object(py).into_object().steal_ptr()
}
fn error_value() -> *mut ffi::PyObject {
ptr::null_mut()
}
}
#[cfg(feature="nightly")]
pub unsafe fn handle_callback<F, T>(location: &str, f: F) -> *mut ffi::PyObject
pub unsafe fn handle_callback<F, T, R, C>(location: &str, _c: C, f: F) -> R
where F: FnOnce(Python) -> PyResult<T>,
F: panic::UnwindSafe,
T: ToPyObject
C: CallbackConverter<T, R>
{
let guard = AbortOnDrop(location);
let ret = panic::catch_unwind(|| {
let py = Python::assume_gil_acquired();
match f(py) {
Ok(val) => {
val.into_py_object(py).into_object().steal_ptr()
C::convert(val, py)
}
Err(e) => {
e.restore(py);
ptr::null_mut()
C::error_value()
}
}
});
let ret = match ret {
Ok(r) => r,
Err(ref err) => handle_panic(Python::assume_gil_acquired(), err)
Err(ref err) => {
handle_panic(Python::assume_gil_acquired(), err);
C::error_value()
}
};
mem::forget(guard);
ret
}
#[cfg(feature="nightly")]
fn handle_panic(_py: Python, _panic: &any::Any) -> *mut ffi::PyObject {
fn handle_panic(_py: Python, _panic: &any::Any) {
let msg = cstr!("Rust panic");
unsafe {
ffi::PyErr_SetString(ffi::PyExc_SystemError, msg.as_ptr());
}
ptr::null_mut()
}
#[cfg(not(feature="nightly"))]
pub unsafe fn handle_callback<F, T>(location: &str, f: F) -> *mut ffi::PyObject
pub unsafe fn handle_callback<F, T, R>(location: &str, f: F) -> R
where F: FnOnce(Python) -> PyResult<T>,
T: ToPyObject
T: CallbackConvertTo<R>
{
let guard = AbortOnDrop(location);
let py = Python::assume_gil_acquired();
let ret = match f(py) {
Ok(val) => {
val.into_py_object(py).into_object().steal_ptr()
val.convert(py)
}
Err(e) => {
e.restore(py);
ptr::null_mut()
R::error_value()
}
};
mem::forget(guard);

View file

@ -152,7 +152,7 @@ pub mod _detail {
pub use ::libc::{c_char, c_void, c_int};
}
pub use err::{from_owned_ptr_or_panic, result_from_owned_ptr};
pub use function::{handle_callback, py_fn_impl, AbortOnDrop};
pub use function::{handle_callback, py_fn_impl, AbortOnDrop, PyObjectCallbackConverter};
}
/// Expands to an `extern "C"` function that allows Python to load

View file

@ -76,7 +76,7 @@ macro_rules! py_class_instance_method {
{
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
$crate::_detail::handle_callback(
LOCATION,
LOCATION, $crate::_detail::PyObjectCallbackConverter,
|py| {
py_argparse_raw!(py, Some(LOCATION), args, kwargs,
[ $( { $pname : $ptype = $detail } )* ]
@ -123,7 +123,7 @@ macro_rules! py_class_class_method {
{
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
$crate::_detail::handle_callback(
LOCATION,
LOCATION, $crate::_detail::PyObjectCallbackConverter,
|py| {
py_argparse_raw!(py, Some(LOCATION), args, kwargs,
[ $( { $pname : $ptype = $detail } )* ]
@ -173,7 +173,7 @@ macro_rules! py_class_static_method {
{
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
$crate::_detail::handle_callback(
LOCATION,
LOCATION, $crate::_detail::PyObjectCallbackConverter,
|py| {
py_argparse_raw!(py, Some(LOCATION), args, kwargs,
[ $( { $pname : $ptype = $detail } )* ]

View file

@ -170,7 +170,7 @@ base_case = '''
}
fn init($py: $crate::Python) -> $crate::PyResult<$crate::PyType> {
py_class_type_object_dynamic_init!($class, $py, type_object);
py_class_type_object_dynamic_init!($class, $py, type_object, $slots);
py_class_init_members!($class, $py, type_object, $members);
unsafe {
if $crate::_detail::ffi::PyType_Ready(&mut type_object) == 0 {
@ -269,19 +269,23 @@ def write(text):
min_indent_level = len(indentation)
last_char = line[-1]
Slot = namedtuple('Slot', ['slot_type', 'slot_name'])
slot_groups = (
('tp', 'type_slots'),
('nb', 'as_number'),
('sq', 'as_sequence'),
)
def generate_case(pattern, new_impl=None, new_slots=None, new_members=None):
write('{ $class:ident $py:ident')
write('$info:tt')
if new_slots:
write('\n/* slots: */ {\n')
if any(s.slot_type == 'type_slots' for s, v in new_slots):
write('\n/* type_slots */ [ $( $type_slot_name:ident : $type_slot_value:expr, )* ]\n')
else:
write('$type_slots:tt')
write('$as_number:tt')
write('$as_sequence:tt')
for prefix, group_name in slot_groups:
if any(s.startswith(prefix) for s, v in new_slots):
write('\n/* %s */ [ $( $%s_slot_name:ident : $%s_slot_value:expr, )* ]\n'
% (group_name, prefix, prefix))
else:
write('$%s:tt' % group_name)
write('\n}\n')
else:
write('$slots:tt')
@ -301,17 +305,16 @@ def generate_case(pattern, new_impl=None, new_slots=None, new_members=None):
write('$info')
if new_slots:
write('\n/* slots: */ {\n')
if any(s.slot_type == 'type_slots' for s, v in new_slots):
write('\n/* type_slots */ [\n')
write('$( $type_slot_name : $type_slot_value, )*\n')
for s, v in new_slots:
if s.slot_type == 'type_slots':
write('%s: %s,\n' % (s.slot_name, v))
write(']\n')
else:
write('$type_slots')
write('$as_number')
write('$as_sequence')
for prefix, group_name in slot_groups:
if any(s.startswith(prefix) for s, v in new_slots):
write('\n/* %s */ [\n' % group_name)
write('$( $%s_slot_name : $%s_slot_value, )*\n' % (prefix, prefix))
for s, v in new_slots:
if s.startswith(prefix):
write('%s: %s,\n' % (s, v))
write(']\n')
else:
write('$%s' % group_name)
write('\n}\n')
else:
write('$slots')
@ -333,9 +336,8 @@ def generate_case(pattern, new_impl=None, new_slots=None, new_members=None):
write('; $($tail)*\n')
write('}};\n')
def class_method(decoration='', special_name=None,
def generate_class_method(special_name=None, decoration='',
slot=None, add_member=False, value_macro=None, value_args=None):
assert(slot is None or isinstance(slot, Slot))
name_pattern = special_name or '$name:ident'
name_use = special_name or '$name'
def impl(with_params):
@ -364,12 +366,6 @@ def class_method(decoration='', special_name=None,
impl(False) # without parameters
impl(True) # with parameters
def tp_new():
class_method(special_name='__new__',
slot=Slot('type_slots', 'tp_new'),
value_macro='py_class_wrap_newfunc',
value_args='$class::__new__')
def traverse_and_clear():
print('''
// def __traverse__(self, visit)
@ -549,8 +545,31 @@ def error(special_name, msg):
def unimplemented(special_name):
return error('%s is not supported by py_class! yet.' % special_name)(special_name)
@special_method
def special_class_method(special_name, *args, **kwargs):
generate_class_method(special_name=special_name, *args, **kwargs)
@special_method
def unary_operator(special_name, slot,
res_type='*mut $crate::_detail::ffi::PyObject',
res_conv='*mut $crate::_detail::PyObjectCallbackConverter'):
generate_case(
pattern='def %s(&$slf:ident) -> $res_type:ty { $($body:tt)* }' % special_name,
new_impl='py_class_impl_item! { $class, $py, %s(&$slf,) $res_type; { $($body)* } [] }'
% special_name,
new_slots=[(slot, 'py_class_unary_slot!($class::%s, %s, %s)'
% (special_name, res_type, res_conv))]
)
# Generate fall-back matcher that produces an error
# when using the wrong method signature
error('Invalid signature for unary operator %s' % special_name)(special_name)
special_names = {
'__init__': error('__init__ is not supported by py_class!; use __new__ instead.'),
'__new__': special_class_method(
slot='tp_new',
value_macro='py_class_wrap_newfunc',
value_args='$class::__new__'),
'__del__': error('__del__ is not supported by py_class!; Use a data member with a Drop impl instead.'),
'__repr__': unimplemented(),
'__str__': unimplemented(),
@ -588,7 +607,9 @@ special_names = {
'__call__': unimplemented(),
# Emulating container types
'__len__': unimplemented(),
'__len__': unary_operator('sq_length',
res_type='$crate::_detail::ffi::Py_ssize_t',
res_conv='$crate::py_class::slots::LenResultConverter'),
'__length_hint__': unimplemented(),
'__getitem__': unimplemented(),
'__missing__': unimplemented(),
@ -684,12 +705,11 @@ def main():
print(macro_start)
print(base_case)
data_decl()
tp_new()
traverse_and_clear()
for name, f in sorted(special_names.items()):
f(name)
print(instance_method)
class_method(decoration='@classmethod',
generate_class_method(decoration='@classmethod',
add_member=True,
value_macro='py_class_class_method',
value_args='$py, $class::$name')

View file

@ -164,7 +164,7 @@ macro_rules! py_class_impl {
}
fn init($py: $crate::Python) -> $crate::PyResult<$crate::PyType> {
py_class_type_object_dynamic_init!($class, $py, type_object);
py_class_type_object_dynamic_init!($class, $py, type_object, $slots);
py_class_init_members!($class, $py, type_object, $members);
unsafe {
if $crate::_detail::ffi::PyType_Ready(&mut type_object) == 0 {
@ -224,55 +224,6 @@ macro_rules! py_class_impl {
$($tail)*
}};
{ $class:ident $py:ident $info:tt
/* slots: */ {
/* type_slots */ [ $( $type_slot_name:ident : $type_slot_value:expr, )* ]
$as_number:tt $as_sequence:tt
}
{ $( $imp:item )* }
$members:tt;
def __new__ ($cls:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)*
} => { py_class_impl! {
$class $py $info
/* slots: */ {
/* type_slots */ [
$( $type_slot_name : $type_slot_value, )*
tp_new: py_class_wrap_newfunc!{$class::__new__ []},
]
$as_number $as_sequence
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py,__new__($cls: &$crate::PyType,) $res_type; { $($body)* } [] }
}
$members; $($tail)*
}};
{ $class:ident $py:ident $info:tt
/* slots: */ {
/* type_slots */ [ $( $type_slot_name:ident : $type_slot_value:expr, )* ]
$as_number:tt $as_sequence:tt
}
{ $( $imp:item )* }
$members:tt;
def __new__ ($cls:ident, $($p:tt)+) -> $res_type:ty { $( $body:tt )* } $($tail:tt)*
} => { py_class_impl! {
$class $py $info
/* slots: */ {
/* type_slots */ [
$( $type_slot_name : $type_slot_value, )*
tp_new: py_argparse_parse_plist_impl!{py_class_wrap_newfunc {$class::__new__} [] ($($p)+,)},
]
$as_number $as_sequence
}
/* impl: */ {
$($imp)*
py_argparse_parse_plist_impl!{
py_class_impl_item { $class, $py, __new__($cls: &$crate::PyType,) $res_type; { $($body)* } }
[] ($($p)+,)
}
}
$members; $($tail)*
}};
// def __traverse__(self, visit)
{ $class:ident $py:ident
@ -677,11 +628,34 @@ macro_rules! py_class_impl {
} => {
py_error! { "__le__ is not supported by py_class! yet." }
};
{ $class:ident $py:ident $info:tt
/* slots: */ {
$type_slots:tt $as_number:tt
/* as_sequence */ [ $( $sq_slot_name:ident : $sq_slot_value:expr, )* ]
}
{ $( $imp:item )* }
$members:tt;
def __len__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)*
} => { py_class_impl! {
$class $py $info
/* slots: */ {
$type_slots $as_number
/* as_sequence */ [
$( $sq_slot_name : $sq_slot_value, )*
sq_length: py_class_unary_slot!($class::__len__, $crate::_detail::ffi::Py_ssize_t, $crate::py_class::slots::LenResultConverter),
]
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py, __len__(&$slf,) $res_type; { $($body)* } [] }
}
$members; $($tail)*
}};
// def __len__()
{ $class:ident $py:ident $info:tt $slots:tt $impls:tt $members:tt;
def __len__ $($tail:tt)*
} => {
py_error! { "__len__ is not supported by py_class! yet." }
py_error! { "Invalid signature for unary operator __len__" }
};
// def __length_hint__()
{ $class:ident $py:ident $info:tt $slots:tt $impls:tt $members:tt;
@ -743,6 +717,55 @@ macro_rules! py_class_impl {
} => {
py_error! { "__neg__ 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 __new__ ($cls:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)*
} => { py_class_impl! {
$class $py $info
/* slots: */ {
/* type_slots */ [
$( $tp_slot_name : $tp_slot_value, )*
tp_new: py_class_wrap_newfunc!{$class::__new__ []},
]
$as_number $as_sequence
}
/* impl: */ {
$($imp)*
py_class_impl_item! { $class, $py,__new__($cls: &$crate::PyType,) $res_type; { $($body)* } [] }
}
$members; $($tail)*
}};
{ $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 __new__ ($cls:ident, $($p:tt)+) -> $res_type:ty { $( $body:tt )* } $($tail:tt)*
} => { py_class_impl! {
$class $py $info
/* slots: */ {
/* type_slots */ [
$( $tp_slot_name : $tp_slot_value, )*
tp_new: py_argparse_parse_plist_impl!{py_class_wrap_newfunc {$class::__new__} [] ($($p)+,)},
]
$as_number $as_sequence
}
/* impl: */ {
$($imp)*
py_argparse_parse_plist_impl!{
py_class_impl_item { $class, $py, __new__($cls: &$crate::PyType,) $res_type; { $($body)* } }
[] ($($p)+,)
}
}
$members; $($tail)*
}};
// def __next__()
{ $class:ident $py:ident $info:tt $slots:tt $impls:tt $members:tt;
def __next__ $($tail:tt)*

View file

@ -17,8 +17,11 @@
// DEALINGS IN THE SOFTWARE.
use ffi;
use std::mem;
use std::{mem, isize};
use python::Python;
use function::CallbackConverter;
use err::{PyErr};
use exc;
#[macro_export]
#[doc(hidden)]
@ -63,12 +66,20 @@ macro_rules! py_class_type_object_flags {
#[doc(hidden)]
macro_rules! py_class_type_object_dynamic_init {
// initialize those fields of PyTypeObject that we couldn't initialize statically
($class: ident, $py:ident, $type_object:ident) => {
($class: ident, $py:ident, $type_object:ident,
/* slots: */ {
$type_slots:tt
$as_number:tt
$as_sequence:tt
}
) => {
unsafe {
$type_object.tp_name = concat!(stringify!($class), "\0").as_ptr() as *const _;
$type_object.tp_basicsize = <$class as $crate::py_class::BaseObject>::size()
as $crate::_detail::ffi::Py_ssize_t;
}
// call slot macros outside of unsafe block
*(unsafe { &mut $type_object.tp_as_sequence }) = py_class_as_sequence!($as_sequence);
}
}
@ -94,7 +105,7 @@ macro_rules! py_class_wrap_newfunc {
{
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
$crate::_detail::handle_callback(
LOCATION,
LOCATION, $crate::_detail::PyObjectCallbackConverter,
|py| {
py_argparse_raw!(py, Some(LOCATION), args, kwargs,
[ $( { $pname : $ptype = $detail } )* ]
@ -109,3 +120,60 @@ macro_rules! py_class_wrap_newfunc {
Some(wrap_newfunc::<()>)
}}
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_class_as_sequence {
([]) => (0 as *mut $crate::_detail::ffi::PySequenceMethods);
([$( $slot_name:ident : $slot_value:expr ,)+]) => {{
static mut SEQUENCE_METHODS : $crate::_detail::ffi::PySequenceMethods
= $crate::_detail::ffi::PySequenceMethods {
$( $slot_name : $slot_value, )*
..
$crate::_detail::ffi::PySequenceMethods_INIT
};
unsafe { &mut SEQUENCE_METHODS }
}}
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_class_unary_slot {
($class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{
unsafe extern "C" fn wrap_unary<DUMMY>(
slf: *mut $crate::_detail::ffi::PyObject)
-> $res_type
{
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
$crate::_detail::handle_callback(
LOCATION, $conv,
|py| {
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>();
let ret = slf.$f(py);
$crate::PyDrop::release_ref(slf, py);
ret
})
}
Some(wrap_unary::<()>)
}}
}
pub struct LenResultConverter;
impl CallbackConverter<usize, isize> for LenResultConverter {
fn convert(val: usize, py: Python) -> isize {
if val <= (isize::MAX as usize) {
val as isize
} else {
PyErr::new_lazy_init(py.get_type::<exc::OverflowError>(), None).restore(py);
-1
}
}
fn error_value() -> isize {
-1
}
}

View file

@ -2,12 +2,32 @@
#[macro_use] extern crate cpython;
use cpython::{PyObject, PythonObject, PyDrop, PyClone, PyResult, Python, NoArgs, ObjectProtocol, PyDict};
use std::mem;
use cpython::{PyObject, PythonObject, PyDrop, PyClone, PyResult, Python, NoArgs, ObjectProtocol, PyDict, exc};
use std::{mem, isize};
use std::cell::RefCell;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
macro_rules! py_assert {
($py:expr, $val:ident, $assertion:expr) => {{
let d = PyDict::new($py);
d.set_item($py, stringify!($val), $val).unwrap();
$py.run(concat!("assert ", $assertion), None, Some(&d)).unwrap();
}}
}
macro_rules! py_expect_exception {
($py:expr, $val:ident, $code:expr, $err:ident) => {{
let d = PyDict::new($py);
d.set_item($py, stringify!($val), $val).unwrap();
let res = $py.eval($code, None, Some(&d));
let err = res.unwrap_err();
assert!(err.matches($py, $py.get_type::<exc::$err>()));
}}
}
py_class!(class EmptyClass |py| { });
#[test]
@ -268,3 +288,23 @@ fn gc_integration() {
assert!(drop_called.load(Ordering::Relaxed));
}
py_class!(class Len |py| {
data l: usize;
def __len__(&self) -> PyResult<usize> {
Ok(*self.l(py))
}
});
#[test]
fn len() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = Len::create_instance(py, 10).unwrap();
py_assert!(py, inst, "len(inst) == 10");
let inst = Len::create_instance(py, (isize::MAX as usize) + 1).unwrap();
py_expect_exception!(py, inst, "len(inst)", OverflowError);
}