py_class!: add __call__ slot
This commit is contained in:
parent
78f673893d
commit
318d9a9ef3
|
@ -285,7 +285,7 @@ TODO: implement support for `__cmp__`, `__lt__`, `__le__`, `__gt__`, `__ge__`, `
|
|||
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.
|
||||
|
||||
## Not sure where this belongs to in the documentation
|
||||
## Other Special Methods
|
||||
|
||||
* `def __bool__(&self) -> PyResult<bool>`
|
||||
|
||||
|
@ -294,6 +294,12 @@ TODO: implement support for `__cmp__`, `__lt__`, `__le__`, `__gt__`, `__ge__`, `
|
|||
Note that `py_class!` always expects this member to be called `__bool__`,
|
||||
even on Python 2.7 where the Python spelling was `__nonzero__`.
|
||||
|
||||
* `def __call__(&self, parameter-list) -> PyResult<impl ToPyObject>`
|
||||
|
||||
For details on `parameter-list`, see the documentation of `py_argparse!()`.
|
||||
The return type must be `PyResult<T>` for some `T` that implements `ToPyObject`.
|
||||
|
||||
|
||||
*/
|
||||
#[macro_export]
|
||||
macro_rules! py_class {
|
||||
|
|
|
@ -438,50 +438,36 @@ def traverse_and_clear():
|
|||
$($tail)*
|
||||
}};''')
|
||||
|
||||
instance_method = '''
|
||||
// def instance_method(&self)
|
||||
{ $class:ident $py:ident $info:tt $slots:tt
|
||||
{ $( $imp:item )* }
|
||||
{ $( $member_name:ident = $member_expr:expr; )* };
|
||||
def $name:ident (&$slf:ident)
|
||||
-> $res_type:ty { $( $body:tt )* } $($tail:tt)*
|
||||
} => { py_class_impl! {
|
||||
$class $py $info $slots
|
||||
/* impl: */ {
|
||||
$($imp)*
|
||||
py_class_impl_item! { $class, $py, $name(&$slf,) $res_type; { $($body)* } [] }
|
||||
}
|
||||
/* members: */ {
|
||||
$( $member_name = $member_expr; )*
|
||||
$name = py_class_instance_method!{$py, $class::$name []};
|
||||
};
|
||||
$($tail)*
|
||||
}};
|
||||
// def instance_method(&self, params)
|
||||
{ $class:ident $py:ident $info:tt $slots:tt
|
||||
{ $( $imp:item )* }
|
||||
{ $( $member_name:ident = $member_expr:expr; )* };
|
||||
def $name:ident (&$slf:ident, $($p:tt)+)
|
||||
-> $res_type:ty { $( $body:tt )* } $($tail:tt)*
|
||||
} => { py_class_impl! {
|
||||
$class $py $info $slots
|
||||
/* impl: */ {
|
||||
$($imp)*
|
||||
py_argparse_parse_plist_impl!{
|
||||
py_class_impl_item { $class, $py, $name(&$slf,) $res_type; { $($body)* } }
|
||||
def generate_instance_method(special_name=None, decoration='',
|
||||
slot=None, add_member=False, value_macro=None, value_args=None):
|
||||
name_pattern = special_name or '$name:ident'
|
||||
name_use = special_name or '$name'
|
||||
def impl(with_params):
|
||||
if with_params:
|
||||
param_pattern = ', $($p:tt)+'
|
||||
impl = '''py_argparse_parse_plist_impl!{
|
||||
py_class_impl_item { $class, $py, %s(&$slf,) $res_type; { $($body)* } }
|
||||
[] ($($p)+,)
|
||||
}
|
||||
}
|
||||
/* members: */ {
|
||||
$( $member_name = $member_expr; )*
|
||||
$name = py_argparse_parse_plist_impl!{
|
||||
py_class_instance_method {$py, $class::$name}
|
||||
[] ($($p)+,)
|
||||
};
|
||||
};
|
||||
$($tail)*
|
||||
}};
|
||||
'''
|
||||
}''' % name_use
|
||||
value = 'py_argparse_parse_plist_impl!{%s {%s} [] ($($p)+,)}' \
|
||||
% (value_macro, value_args)
|
||||
else:
|
||||
param_pattern = ''
|
||||
impl = 'py_class_impl_item! { $class, $py, %s(&$slf,) $res_type; { $($body)* } [] }' \
|
||||
% name_use
|
||||
value = '%s!{%s []}' % (value_macro, value_args)
|
||||
pattern = '%s def %s (&$slf:ident%s) -> $res_type:ty { $( $body:tt )* }' \
|
||||
% (decoration, name_pattern, param_pattern)
|
||||
slots = []
|
||||
if slot is not None:
|
||||
slots.append((slot, value))
|
||||
members = []
|
||||
if add_member:
|
||||
members.append((name_use, value))
|
||||
generate_case(pattern, new_impl=impl, new_slots=slots, new_members=members)
|
||||
impl(False) # without parameters
|
||||
impl(True) # with parameters
|
||||
|
||||
|
||||
static_method = '''
|
||||
// @staticmethod def static_method(params)
|
||||
|
@ -577,6 +563,14 @@ def unary_operator(special_name, slot,
|
|||
# when using the wrong method signature
|
||||
error('Invalid signature for unary operator %s' % special_name)(special_name)
|
||||
|
||||
@special_method
|
||||
def call_operator(special_name, slot):
|
||||
generate_instance_method(
|
||||
special_name=special_name,
|
||||
slot=slot,
|
||||
value_macro='py_class_call_slot',
|
||||
value_args='$class::%s' % special_name)
|
||||
|
||||
special_names = {
|
||||
'__init__': error('__init__ is not supported by py_class!; use __new__ instead.'),
|
||||
'__new__': special_class_method(
|
||||
|
@ -610,7 +604,7 @@ special_names = {
|
|||
'__setattr__': unimplemented(),
|
||||
'__delattr__': unimplemented(),
|
||||
'__dir__': unimplemented(),
|
||||
|
||||
|
||||
# Implementing Descriptors
|
||||
'__get__': unimplemented(),
|
||||
'__set__': unimplemented(),
|
||||
|
@ -621,8 +615,8 @@ special_names = {
|
|||
'__subclasscheck__': unimplemented(),
|
||||
|
||||
# Emulating callable objects
|
||||
'__call__': unimplemented(),
|
||||
|
||||
'__call__': call_operator('tp_call'),
|
||||
|
||||
# Emulating container types
|
||||
'__len__': unary_operator('sq_length',
|
||||
res_ffi_type='$crate::_detail::ffi::Py_ssize_t',
|
||||
|
@ -726,7 +720,10 @@ def main():
|
|||
traverse_and_clear()
|
||||
for name, f in sorted(special_names.items()):
|
||||
f(name)
|
||||
print(instance_method)
|
||||
generate_instance_method(
|
||||
add_member=True,
|
||||
value_macro='py_class_instance_method',
|
||||
value_args='$py, $class::$name')
|
||||
generate_class_method(decoration='@classmethod',
|
||||
add_member=True,
|
||||
value_macro='py_class_class_method',
|
||||
|
|
|
@ -365,12 +365,55 @@ macro_rules! py_class_impl {
|
|||
} => {
|
||||
py_error! { "Invalid signature for unary operator __bool__" }
|
||||
};
|
||||
// def __call__()
|
||||
{ $class:ident $py:ident $info:tt $slots:tt $impls:tt $members:tt;
|
||||
def __call__ $($tail:tt)*
|
||||
} => {
|
||||
py_error! { "__call__ 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 __call__ (&$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_call: py_class_call_slot!{$class::__call__ []},
|
||||
]
|
||||
$as_number $as_sequence
|
||||
}
|
||||
/* impl: */ {
|
||||
$($imp)*
|
||||
py_class_impl_item! { $class, $py, __call__(&$slf,) $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 __call__ (&$slf: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_call: py_argparse_parse_plist_impl!{py_class_call_slot {$class::__call__} [] ($($p)+,)},
|
||||
]
|
||||
$as_number $as_sequence
|
||||
}
|
||||
/* impl: */ {
|
||||
$($imp)*
|
||||
py_argparse_parse_plist_impl!{
|
||||
py_class_impl_item { $class, $py, __call__(&$slf,) $res_type; { $($body)* } }
|
||||
[] ($($p)+,)
|
||||
}
|
||||
}
|
||||
$members; $($tail)*
|
||||
}};
|
||||
// def __cmp__()
|
||||
{ $class:ident $py:ident $info:tt $slots:tt $impls:tt $members:tt;
|
||||
def __cmp__ $($tail:tt)*
|
||||
|
@ -1086,13 +1129,10 @@ macro_rules! py_class_impl {
|
|||
} => {
|
||||
py_error! { "__xor__ is not supported by py_class! yet." }
|
||||
};
|
||||
|
||||
// def instance_method(&self)
|
||||
{ $class:ident $py:ident $info:tt $slots:tt
|
||||
{ $( $imp:item )* }
|
||||
{ $( $member_name:ident = $member_expr:expr; )* };
|
||||
def $name:ident (&$slf:ident)
|
||||
-> $res_type:ty { $( $body:tt )* } $($tail:tt)*
|
||||
def $name:ident (&$slf:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)*
|
||||
} => { py_class_impl! {
|
||||
$class $py $info $slots
|
||||
/* impl: */ {
|
||||
|
@ -1102,15 +1142,12 @@ macro_rules! py_class_impl {
|
|||
/* members: */ {
|
||||
$( $member_name = $member_expr; )*
|
||||
$name = py_class_instance_method!{$py, $class::$name []};
|
||||
};
|
||||
$($tail)*
|
||||
}; $($tail)*
|
||||
}};
|
||||
// def instance_method(&self, params)
|
||||
{ $class:ident $py:ident $info:tt $slots:tt
|
||||
{ $( $imp:item )* }
|
||||
{ $( $member_name:ident = $member_expr:expr; )* };
|
||||
def $name:ident (&$slf:ident, $($p:tt)+)
|
||||
-> $res_type:ty { $( $body:tt )* } $($tail:tt)*
|
||||
def $name:ident (&$slf:ident, $($p:tt)+) -> $res_type:ty { $( $body:tt )* } $($tail:tt)*
|
||||
} => { py_class_impl! {
|
||||
$class $py $info $slots
|
||||
/* impl: */ {
|
||||
|
@ -1122,14 +1159,9 @@ macro_rules! py_class_impl {
|
|||
}
|
||||
/* members: */ {
|
||||
$( $member_name = $member_expr; )*
|
||||
$name = py_argparse_parse_plist_impl!{
|
||||
py_class_instance_method {$py, $class::$name}
|
||||
[] ($($p)+,)
|
||||
};
|
||||
};
|
||||
$($tail)*
|
||||
$name = py_argparse_parse_plist_impl!{py_class_instance_method {$py, $class::$name} [] ($($p)+,)};
|
||||
}; $($tail)*
|
||||
}};
|
||||
|
||||
{ $class:ident $py:ident $info:tt $slots:tt
|
||||
{ $( $imp:item )* }
|
||||
{ $( $member_name:ident = $member_expr:expr; )* };
|
||||
|
|
|
@ -365,12 +365,55 @@ macro_rules! py_class_impl {
|
|||
} => {
|
||||
py_error! { "Invalid signature for unary operator __bool__" }
|
||||
};
|
||||
// def __call__()
|
||||
{ $class:ident $py:ident $info:tt $slots:tt $impls:tt $members:tt;
|
||||
def __call__ $($tail:tt)*
|
||||
} => {
|
||||
py_error! { "__call__ 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 __call__ (&$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_call: py_class_call_slot!{$class::__call__ []},
|
||||
]
|
||||
$as_number $as_sequence
|
||||
}
|
||||
/* impl: */ {
|
||||
$($imp)*
|
||||
py_class_impl_item! { $class, $py, __call__(&$slf,) $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 __call__ (&$slf: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_call: py_argparse_parse_plist_impl!{py_class_call_slot {$class::__call__} [] ($($p)+,)},
|
||||
]
|
||||
$as_number $as_sequence
|
||||
}
|
||||
/* impl: */ {
|
||||
$($imp)*
|
||||
py_argparse_parse_plist_impl!{
|
||||
py_class_impl_item { $class, $py, __call__(&$slf,) $res_type; { $($body)* } }
|
||||
[] ($($p)+,)
|
||||
}
|
||||
}
|
||||
$members; $($tail)*
|
||||
}};
|
||||
// def __cmp__()
|
||||
{ $class:ident $py:ident $info:tt $slots:tt $impls:tt $members:tt;
|
||||
def __cmp__ $($tail:tt)*
|
||||
|
@ -1086,13 +1129,10 @@ macro_rules! py_class_impl {
|
|||
} => {
|
||||
py_error! { "__xor__ is not supported by py_class! yet." }
|
||||
};
|
||||
|
||||
// def instance_method(&self)
|
||||
{ $class:ident $py:ident $info:tt $slots:tt
|
||||
{ $( $imp:item )* }
|
||||
{ $( $member_name:ident = $member_expr:expr; )* };
|
||||
def $name:ident (&$slf:ident)
|
||||
-> $res_type:ty { $( $body:tt )* } $($tail:tt)*
|
||||
def $name:ident (&$slf:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)*
|
||||
} => { py_class_impl! {
|
||||
$class $py $info $slots
|
||||
/* impl: */ {
|
||||
|
@ -1102,15 +1142,12 @@ macro_rules! py_class_impl {
|
|||
/* members: */ {
|
||||
$( $member_name = $member_expr; )*
|
||||
$name = py_class_instance_method!{$py, $class::$name []};
|
||||
};
|
||||
$($tail)*
|
||||
}; $($tail)*
|
||||
}};
|
||||
// def instance_method(&self, params)
|
||||
{ $class:ident $py:ident $info:tt $slots:tt
|
||||
{ $( $imp:item )* }
|
||||
{ $( $member_name:ident = $member_expr:expr; )* };
|
||||
def $name:ident (&$slf:ident, $($p:tt)+)
|
||||
-> $res_type:ty { $( $body:tt )* } $($tail:tt)*
|
||||
def $name:ident (&$slf:ident, $($p:tt)+) -> $res_type:ty { $( $body:tt )* } $($tail:tt)*
|
||||
} => { py_class_impl! {
|
||||
$class $py $info $slots
|
||||
/* impl: */ {
|
||||
|
@ -1122,14 +1159,9 @@ macro_rules! py_class_impl {
|
|||
}
|
||||
/* members: */ {
|
||||
$( $member_name = $member_expr; )*
|
||||
$name = py_argparse_parse_plist_impl!{
|
||||
py_class_instance_method {$py, $class::$name}
|
||||
[] ($($p)+,)
|
||||
};
|
||||
};
|
||||
$($tail)*
|
||||
$name = py_argparse_parse_plist_impl!{py_class_instance_method {$py, $class::$name} [] ($($p)+,)};
|
||||
}; $($tail)*
|
||||
}};
|
||||
|
||||
{ $class:ident $py:ident $info:tt $slots:tt
|
||||
{ $( $imp:item )* }
|
||||
{ $( $member_name:ident = $member_expr:expr; )* };
|
||||
|
|
|
@ -285,3 +285,33 @@ impl CallbackConverter<bool> for BoolConverter {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_call_slot {
|
||||
($class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{
|
||||
unsafe extern "C" fn wrap_call<DUMMY>(
|
||||
slf: *mut $crate::_detail::ffi::PyObject,
|
||||
args: *mut $crate::_detail::ffi::PyObject,
|
||||
kwargs: *mut $crate::_detail::ffi::PyObject)
|
||||
-> *mut $crate::_detail::ffi::PyObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(
|
||||
LOCATION, $crate::_detail::PyObjectCallbackConverter,
|
||||
|py| {
|
||||
py_argparse_raw!(py, Some(LOCATION), args, kwargs,
|
||||
[ $( { $pname : $ptype = $detail } )* ]
|
||||
{
|
||||
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>();
|
||||
let ret = slf.$f(py $(, $pname )* );
|
||||
$crate::PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
})
|
||||
})
|
||||
}
|
||||
Some(wrap_call::<()>)
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -416,4 +416,26 @@ fn comparisons() {
|
|||
}
|
||||
|
||||
|
||||
py_class!(class Callable |py| {
|
||||
def __call__(&self, arg: i32) -> PyResult<i32> {
|
||||
Ok(arg * 6)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
#[test]
|
||||
fn callable() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let c = Callable::create_instance(py).unwrap();
|
||||
py_assert!(py, c, "callable(c)");
|
||||
py_assert!(py, c, "c(7) == 42");
|
||||
|
||||
let nc = Comparisons::create_instance(py, 0).unwrap();
|
||||
py_assert!(py, nc, "not callable(nc)");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue