Add support for __len__ special method.
This commit is contained in:
parent
a14e3d2b77
commit
fffcf57834
|
@ -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 $@
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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__':
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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! {
|
||||
|
|
30
src/err.rs
30
src/err.rs
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 } )* ]
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)*
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue