Add parameter extraction support to py_fn! and py_method!.

These macros now support specifying an argument list:
`py_fn!(myfn(myarg: i32))`
will expect `myfn` to be a function with the signature:
`fn myfn<'p>(py: Python<'p>, myarg: i32) -> PyResult<'p, _>`

It can called from python as `myfn(1)`
or using keyword arguments: `myfn(myarg=1)`.

If no parameter list is specified (`py_fn!(myfn)`), the expected signature
now includes the keyword arguments:
`fn run<'p>(py: Python<'p>, args: &PyTuple<'p>, kwargs: Option<&PyDict<'p>>)`

Due to the additional `kwargs` argument, this is a [breaking-change].
This commit is contained in:
Daniel Grunwald 2015-08-03 00:06:15 +02:00
parent 072f4d24eb
commit a23b5b5910
7 changed files with 335 additions and 77 deletions

View file

@ -4,18 +4,18 @@
#[macro_use] extern crate cpython;
use cpython::{PythonObject, PyObject, PyRustObject, PyTuple, PyResult};
use cpython::{PythonObject, PyObject, PyRustObject, PyResult};
py_module_initializer!(custom_type, |_py, m| {
try!(m.add("__doc__", "Module documentation string"));
try!(m.add_type::<i32>("MyType")
.add("a", py_method!(a))
.add("a", py_method!(a()))
.finish());
Ok(())
});
fn a<'p>(slf: &PyRustObject<'p, i32>, args: &PyTuple<'p>) -> PyResult<'p, PyObject<'p>> {
println!("a() was called with self={:?} and args={:?}", slf.get(), args.as_object());
fn a<'p>(slf: &PyRustObject<'p, i32>) -> PyResult<'p, PyObject<'p>> {
println!("a() was called with self={:?}", slf.get());
Ok(slf.python().None())
}

View file

@ -4,24 +4,29 @@
#[macro_use] extern crate cpython;
use cpython::{PyObject, PyResult,Python, PyTuple};
use cpython::{PyObject, PyResult, Python, PyTuple, PyDict};
py_module_initializer!(hello, |_py, m| {
try!(m.add("__doc__", "Module documentation string"));
try!(m.add("run", py_fn!(run)));
try!(m.add("val", py_fn!(val)));
try!(m.add("val", py_fn!(val())));
Ok(())
});
fn run<'p>(py: Python<'p>, args: &PyTuple<'p>) -> PyResult<'p, PyObject<'p>> {
fn run<'p>(py: Python<'p>, args: &PyTuple<'p>, kwargs: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>> {
println!("Rust says: Hello Python!");
for arg in args {
println!("Rust got {}", arg);
}
if let Some(kwargs) = kwargs {
for (key, val) in kwargs.items() {
println!("{} = {}", key, val);
}
}
Ok(py.None())
}
fn val<'p>(_: Python<'p>, _: &PyTuple<'p>) -> PyResult<'p, i32> {
fn val<'p>(_: Python<'p>) -> PyResult<'p, i32> {
Ok(42)
}

View file

@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
//! This module contains logic for parsing a python argument list.
//! See also the macros `py_argparse!`, `py_fn` and `py_method`.
use std::ptr;
use python::{Python, PythonObject};
@ -25,19 +26,22 @@ use conversion::ToPyObject;
use ffi;
use err::{self, PyResult};
/// Description of a python parameter; used for `parse_args()`.
pub struct ParamDescription<'a> {
name: &'a str,
is_optional: bool
/// The name of the parameter.
pub name: &'a str,
/// Whether the parameter is optional.
pub is_optional: bool
}
/// Parse argument list
///
/// fname: Name of the current function
/// params: Declared parameters of the function
/// args: Positional arguments
/// kwargs: Keyword arguments
/// output: Output array that receives the arguments.
/// Must have same length as `params` and must be initialized to `None`.
/// * fname: Name of the current function
/// * params: Declared parameters of the function
/// * args: Positional arguments
/// * kwargs: Keyword arguments
/// * output: Output array that receives the arguments.
/// Must have same length as `params` and must be initialized to `None`.
pub fn parse_args<'p>(
fname: Option<&str>, params: &[ParamDescription],
args: &PyTuple<'p>, kwargs: Option<&PyDict<'p>>,
@ -99,10 +103,12 @@ pub fn parse_args<'p>(
Ok(())
}
macro_rules! argparse_extract {
( ( ) $body:block ) => { $body };
( ( $pname:ident : $ptype:ty ) $body:block) => {
match <$ptype as $crate::ExtractPyObject>::prepare_extract($pname.as_ref().unwrap()) {
#[doc(hidden)]
#[macro_export]
macro_rules! py_argparse_extract {
( $iter:expr, ( ) $body:block ) => { $body };
( $iter:expr, ( $pname:ident : $ptype:ty ) $body:block) => {
match <$ptype as $crate::ExtractPyObject>::prepare_extract($iter.next().unwrap().as_ref().unwrap()) {
Ok(prepared) => {
match <$ptype as $crate::ExtractPyObject>::extract(&prepared) {
Ok($pname) => $body,
@ -112,18 +118,37 @@ macro_rules! argparse_extract {
Err(e) => Err(e)
}
};
( ( $pname:ident : $ptype:ty , $($r:tt)+ ) $body:block) => {
argparse_extract!(($pname: $ptype) {
argparse_extract!( ( $($r)* ) $body)
( $iter:expr, ( $pname:ident : $ptype:ty , $($r:tt)+ ) $body:block) => {
py_argparse_extract!($iter, ($pname: $ptype) {
py_argparse_extract!( $iter, ( $($r)* ) $body)
})
}
}
macro_rules! argparse_snd {
( $fst:expr, $snd:expr) => { $snd }
#[doc(hidden)]
#[macro_export]
macro_rules! py_argparse_snd {
( $fst:expr, $snd:expr ) => { $snd }
}
macro_rules! argparse {
/// This macro is used to parse a parameter list into a set of variables.
///
/// Syntax: `py_argparse!(fname, args, kwargs, (parameter-list) { body })`
///
/// * `fname`: expression of type `Option<&str>`: Name of the function used in error messages.
/// * `args`: expression of type `&PyTuple`: The position arguments
/// * `kwargs`: expression of type `Option<&PyDict>`: The named arguments
/// * `parameter-list`: a comma-separated list of Rust parameter declarations (`name: type`).
/// The types used must implement the `ExtractPyObject` trait.
/// * `body`: expression of type `PyResult<_>`.
///
/// `py_argparse!()` expands to code that extracts values from `args` and `kwargs` and assigns
/// them to the parameters. If the extraction is successful, `py_argparse!()` evaluates
/// the body expression (where the extracted parameters are available) and returns the result
/// value of the body expression.
/// If extraction fails, `py_argparse!()` returns a failed `PyResult` without evaluating `body`.
#[macro_export]
macro_rules! py_argparse {
($fname:expr, $args:expr, $kwargs:expr, ($( $pname:ident : $ptype:ty ),*) $body:block) => {{
const PARAMS: &'static [$crate::argparse::ParamDescription<'static>] = &[
$(
@ -133,11 +158,15 @@ macro_rules! argparse {
}
),*
];
let mut output = [$( argparse_snd!($pname, None) ),*];
let mut output = [$( py_argparse_snd!($pname, None) ),*];
match $crate::argparse::parse_args($fname, PARAMS, $args, $kwargs, &mut output) {
Ok(()) => {
let &[$(ref $pname),*] = &output;
argparse_extract!( ( $( $pname : $ptype ),* ) $body )
// We can't use experimental slice pattern syntax in macros
//let &[$(ref $pname),*] = &output;
let mut iter = output.iter();
let ret = py_argparse_extract!( iter, ( $( $pname : $ptype ),* ) $body );
assert!(iter.next() == None);
ret
},
Err(e) => Err(e)
}
@ -155,7 +184,7 @@ mod test {
let py = gil_guard.python();
let mut called = false;
let tuple = ("abc", 42).to_py_object(py);
argparse!(None, &tuple, None, (x: &str, y: i32) {
py_argparse!(None, &tuple, None, (x: &str, y: i32) {
assert_eq!(x, "abc");
assert_eq!(y, 42);
called = true;

View file

@ -74,7 +74,7 @@ pub trait ToPyObject<'p> {
///
/// Usage:
/// ```let obj: PyObject = ...;
/// let prepared = <TargetType as FromPyObject>::prepare_extract(&obj);
/// let prepared = <TargetType as ExtractPyObject>::prepare_extract(&obj);
/// let extracted = try!(extract(&prepared));```
///
/// Note: depending on the implementation, the lifetime of the extracted result may

View file

@ -17,11 +17,11 @@
// DEALINGS IN THE SOFTWARE.
use std::ptr;
use python::Python;
use objects::PyObject;
use python::{Python, PythonObject};
use objects::{PyObject, PyTuple, PyDict, PyString, exc};
use conversion::ToPyObject;
use ffi;
use err;
use err::{self, PyResult};
/// Creates a Python callable object that invokes a Rust function.
///
@ -37,14 +37,19 @@ macro_rules! py_fn {
($f: ident) => ( interpolate_idents! {{
unsafe extern "C" fn [ wrap_ $f ](
_slf: *mut $crate::_detail::ffi::PyObject,
args: *mut $crate::_detail::ffi::PyObject)
args: *mut $crate::_detail::ffi::PyObject,
kwargs: *mut $crate::_detail::ffi::PyObject)
-> *mut $crate::_detail::ffi::PyObject
{
let _guard = $crate::_detail::PanicGuard::with_message("Rust panic in py_fn!");
let py = $crate::_detail::bounded_assume_gil_acquired(&args);
let args = $crate::PyObject::from_borrowed_ptr(py, args);
let args = <$crate::PyTuple as $crate::PythonObject>::unchecked_downcast_from(args);
match $f(py, &args) {
let kwargs = match $crate::PyObject::from_borrowed_ptr_opt(py, kwargs) {
Some(kwargs) => Some(<$crate::PyDict as $crate::PythonObject>::unchecked_downcast_from(kwargs)),
None => None
};
match $f(py, &args, kwargs.as_ref()) {
Ok(val) => {
let obj = $crate::ToPyObject::into_py_object(val, py);
return $crate::PythonObject::into_object(obj).steal_ptr();
@ -58,20 +63,68 @@ macro_rules! py_fn {
static mut [ method_def_ $f ]: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
//ml_name: bytes!(stringify!($f), "\0"),
ml_name: 0 as *const $crate::_detail::libc::c_char,
ml_meth: Some([ wrap_ $f ]),
ml_flags: $crate::_detail::ffi::METH_VARARGS,
ml_meth: None,
ml_flags: $crate::_detail::ffi::METH_VARARGS | $crate::_detail::ffi::METH_KEYWORDS,
ml_doc: 0 as *const $crate::_detail::libc::c_char
};
unsafe {
[ method_def_ $f ].ml_name =
concat!(stringify!($f), "\0").as_ptr() as *const _;
[ method_def_ $f ].ml_name = concat!(stringify!($f), "\0").as_ptr() as *const _;
[ method_def_ $f ].ml_meth = Some(
std::mem::transmute::<$crate::_detail::ffi::PyCFunctionWithKeywords,
$crate::_detail::ffi::PyCFunction>([ wrap_ $f ])
);
$crate::_detail::py_fn_impl(&mut [ method_def_ $f ])
}
}})
}});
($f: ident ( $( $pname:ident : $ptype:ty ),* ) ) => ( interpolate_idents! {{
unsafe extern "C" fn [ wrap_ $f ](
_slf: *mut $crate::_detail::ffi::PyObject,
args: *mut $crate::_detail::ffi::PyObject,
kwargs: *mut $crate::_detail::ffi::PyObject)
-> *mut $crate::_detail::ffi::PyObject
{
let _guard = $crate::_detail::PanicGuard::with_message("Rust panic in py_fn!");
let py = $crate::_detail::bounded_assume_gil_acquired(&args);
let args = $crate::PyObject::from_borrowed_ptr(py, args);
let args = <$crate::PyTuple as $crate::PythonObject>::unchecked_downcast_from(args);
let kwargs = match $crate::PyObject::from_borrowed_ptr_opt(py, kwargs) {
Some(kwargs) => Some(<$crate::PyDict as $crate::PythonObject>::unchecked_downcast_from(kwargs)),
None => None
};
match py_argparse!(Some(stringify!($f)), &args, kwargs.as_ref(),
( $($pname : $ptype),* ) { $f( py, $($pname),* ) })
{
Ok(val) => {
let obj = $crate::ToPyObject::into_py_object(val, py);
return $crate::PythonObject::into_object(obj).steal_ptr();
}
Err(e) => {
e.restore();
return ::std::ptr::null_mut();
}
}
}
static mut [ method_def_ $f ]: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
//ml_name: bytes!(stringify!($f), "\0"),
ml_name: 0 as *const $crate::_detail::libc::c_char,
ml_meth: None,
ml_flags: $crate::_detail::ffi::METH_VARARGS | $crate::_detail::ffi::METH_KEYWORDS,
ml_doc: 0 as *const $crate::_detail::libc::c_char
};
unsafe {
[ method_def_ $f ].ml_name = concat!(stringify!($f), "\0").as_ptr() as *const _;
[ method_def_ $f ].ml_meth = Some(
std::mem::transmute::<$crate::_detail::ffi::PyCFunctionWithKeywords,
$crate::_detail::ffi::PyCFunction>([ wrap_ $f ])
);
$crate::_detail::py_fn_impl(&mut [ method_def_ $f ])
}
}});
}
pub struct PyFn(*mut ffi::PyMethodDef);
#[inline]
pub unsafe fn py_fn_impl(def: *mut ffi::PyMethodDef) -> PyFn {
PyFn(def)
}
@ -86,3 +139,4 @@ impl <'p> ToPyObject<'p> for PyFn {
}
}

View file

@ -126,6 +126,7 @@ mod conversion;
mod objects;
mod objectprotocol;
mod pythonrun;
pub mod argparse;
mod function;
mod rustobject;
@ -168,15 +169,15 @@ pub mod _detail {
/// #![feature(plugin)]
/// #![plugin(interpolate_idents)]
/// #[macro_use] extern crate cpython;
/// use cpython::{Python, PyResult, PyObject, PyTuple};
/// use cpython::{Python, PyResult, PyObject};
///
/// py_module_initializer!(example, |py, m| {
/// try!(m.add("__doc__", "Module documentation string"));
/// try!(m.add("run", py_fn!(run)));
/// try!(m.add("run", py_fn!(run())));
/// Ok(())
/// });
///
/// fn run<'p>(py: Python<'p>, args: &PyTuple<'p>) -> PyResult<'p, PyObject<'p>> {
/// fn run<'p>(py: Python<'p>) -> PyResult<'p, PyObject<'p>> {
/// println!("Rust says: Hello Python!");
/// Ok(py.None())
/// }

View file

@ -41,9 +41,9 @@ use err;
/// PyTuple, PyRustObject, PyRustTypeBuilder};
/// use cpython::{exc};
///
/// fn mul<'p>(slf: &PyRustObject<'p, i32>, args: &PyTuple<'p>) -> PyResult<'p, i32> {
/// fn mul<'p>(slf: &PyRustObject<'p, i32>, arg: i32) -> PyResult<'p, i32> {
/// let py = slf.python();
/// match slf.get().checked_mul(try!(args.get_item(0).extract::<i32>())) {
/// match slf.get().checked_mul(arg) {
/// Some(val) => Ok(val),
/// None => Err(PyErr::new_lazy_init(py.get_type::<exc::OverflowError>(), None))
/// }
@ -52,7 +52,7 @@ use err;
/// fn main() {
/// let gil = Python::acquire_gil();
/// let multiplier_type = PyRustTypeBuilder::<i32>::new(gil.python(), "Multiplier")
/// .add("mul", py_method!(mul))
/// .add("mul", py_method!(mul(arg: i32)))
/// .finish().unwrap();
/// let obj = multiplier_type.create_instance(3, ()).into_object();
/// let result = obj.call_method("mul", &(4,), None).unwrap().extract::<i32>().unwrap();
@ -64,7 +64,8 @@ macro_rules! py_method {
($f: ident) => ( interpolate_idents! {{
unsafe extern "C" fn [ wrap_ $f ](
slf: *mut $crate::_detail::ffi::PyObject,
args: *mut $crate::_detail::ffi::PyObject)
args: *mut $crate::_detail::ffi::PyObject,
kwargs: *mut $crate::_detail::ffi::PyObject)
-> *mut $crate::_detail::ffi::PyObject
{
let _guard = $crate::_detail::PanicGuard::with_message("Rust panic in py_method!");
@ -73,7 +74,11 @@ macro_rules! py_method {
let slf = $crate::PythonObject::unchecked_downcast_from(slf);
let args = $crate::PyObject::from_borrowed_ptr(py, args);
let args = <$crate::PyTuple as $crate::PythonObject>::unchecked_downcast_from(args);
match $f(&slf, &args) {
let kwargs = match $crate::PyObject::from_borrowed_ptr_opt(py, kwargs) {
Some(kwargs) => Some(<$crate::PyDict as $crate::PythonObject>::unchecked_downcast_from(kwargs)),
None => None
};
match $f(&slf, &args, kwargs.as_ref()) {
Ok(val) => {
let obj = $crate::ToPyObject::into_py_object(val, py);
return $crate::PythonObject::into_object(obj).steal_ptr();
@ -87,27 +92,134 @@ macro_rules! py_method {
static mut [ method_def_ $f ]: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
//ml_name: bytes!(stringify!($f), "\0"),
ml_name: 0 as *const $crate::_detail::libc::c_char,
ml_meth: Some([ wrap_ $f ]),
ml_flags: $crate::_detail::ffi::METH_VARARGS,
ml_meth: None,
ml_flags: $crate::_detail::ffi::METH_VARARGS | $crate::_detail::ffi::METH_KEYWORDS,
ml_doc: 0 as *const $crate::_detail::libc::c_char
};
unsafe {
[ method_def_ $f ].ml_name =
concat!(stringify!($f), "\0").as_ptr() as *const _;
$crate::_detail::py_method_impl(&mut [ method_def_ $f ], $f)
[ method_def_ $f ].ml_name = concat!(stringify!($f), "\0").as_ptr() as *const _;
[ method_def_ $f ].ml_meth = Some(
std::mem::transmute::<$crate::_detail::ffi::PyCFunctionWithKeywords,
$crate::_detail::ffi::PyCFunction>([ wrap_ $f ])
);
$crate::_detail::py_method_impl::py_method_impl(&mut [ method_def_ $f ], $f)
}
}});
($f: ident ( $( $pname:ident : $ptype:ty ),* ) ) => ( interpolate_idents! {{
unsafe extern "C" fn [ wrap_ $f ](
slf: *mut $crate::_detail::ffi::PyObject,
args: *mut $crate::_detail::ffi::PyObject,
kwargs: *mut $crate::_detail::ffi::PyObject)
-> *mut $crate::_detail::ffi::PyObject
{
let _guard = $crate::_detail::PanicGuard::with_message("Rust panic in py_method!");
let py = $crate::_detail::bounded_assume_gil_acquired(&args);
let slf = $crate::PyObject::from_borrowed_ptr(py, slf);
let slf = $crate::PythonObject::unchecked_downcast_from(slf);
let args = $crate::PyObject::from_borrowed_ptr(py, args);
let args = <$crate::PyTuple as $crate::PythonObject>::unchecked_downcast_from(args);
let kwargs = match $crate::PyObject::from_borrowed_ptr_opt(py, kwargs) {
Some(kwargs) => Some(<$crate::PyDict as $crate::PythonObject>::unchecked_downcast_from(kwargs)),
None => None
};
match py_argparse!(Some(stringify!($f)), &args, kwargs.as_ref(),
( $($pname : $ptype),* ) { $f( &slf, $($pname),* ) })
{
Ok(val) => {
let obj = $crate::ToPyObject::into_py_object(val, py);
return $crate::PythonObject::into_object(obj).steal_ptr();
}
Err(e) => {
e.restore();
return ::std::ptr::null_mut();
}
}
}
static mut [ method_def_ $f ]: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
//ml_name: bytes!(stringify!($f), "\0"),
ml_name: 0 as *const $crate::_detail::libc::c_char,
ml_meth: None,
ml_flags: $crate::_detail::ffi::METH_VARARGS | $crate::_detail::ffi::METH_KEYWORDS,
ml_doc: 0 as *const $crate::_detail::libc::c_char
};
unsafe {
[ method_def_ $f ].ml_name = concat!(stringify!($f), "\0").as_ptr() as *const _;
[ method_def_ $f ].ml_meth = Some(
std::mem::transmute::<$crate::_detail::ffi::PyCFunctionWithKeywords,
$crate::_detail::ffi::PyCFunction>([ wrap_ $f ])
);
py_method_call_impl!(&mut [ method_def_ $f ], $f ( $($pname : $ptype),* ) )
}
}})
}
pub struct MethodDescriptor<T>(*mut ffi::PyMethodDef, marker::PhantomData<fn(&T)>);
// py_method_impl takes fn(&T) to ensure that the T in MethodDescriptor<T>
// corresponds to the T in the function signature.
pub unsafe fn py_method_impl<'p, T, R>(
def: *mut ffi::PyMethodDef,
_f: fn(&T, &PyTuple<'p>) -> err::PyResult<'p, R>
) -> MethodDescriptor<T> {
MethodDescriptor(def, marker::PhantomData)
#[doc(hidden)]
pub mod py_method_impl {
use ffi;
use err;
use objects::{PyTuple, PyDict};
use super::MethodDescriptor;
use std::marker;
// py_method_impl takes fn(&T) to ensure that the T in MethodDescriptor<T>
// corresponds to the T in the function signature.
pub unsafe fn py_method_impl<'p, T, R>(
def: *mut ffi::PyMethodDef,
_f: fn(&T, &PyTuple<'p>, Option<&PyDict<'p>>) -> err::PyResult<'p, R>
) -> MethodDescriptor<T> {
MethodDescriptor(def, marker::PhantomData)
}
#[macro_export]
macro_rules! py_method_call_impl {
( $def:expr, $f:ident ( ) )
=> { $crate::_detail::py_method_impl::py_method_impl_0($def, $f) };
( $def:expr, $f:ident ( $n1:ident : $t1:ty ) )
=> { $crate::_detail::py_method_impl::py_method_impl_1($def, $f) };
( $def:expr, $f:ident ( $n1:ident : $t1:ty, $n2:ident : $t2:ty ) )
=> { $crate::_detail::py_method_impl::py_method_impl_2($def, $f) };
( $def:expr, $f:ident ( $n1:ident : $t1:ty, $n2:ident : $t2:ty, $n3:ident : $t3:ty ) )
=> { $crate::_detail::py_method_impl::py_method_impl_3($def, $f) };
( $def:expr, $f:ident ( $n1:ident : $t1:ty, $n2:ident : $t2:ty, $n3:ident : $t3:ty, $n4:ident : $t4:ty ) )
=> { $crate::_detail::py_method_impl::py_method_impl_3($def, $f) };
}
pub unsafe fn py_method_impl_0<'p, T, R>(
def: *mut ffi::PyMethodDef,
_f: fn(&T) -> err::PyResult<'p, R>
) -> MethodDescriptor<T> {
MethodDescriptor(def, marker::PhantomData)
}
pub unsafe fn py_method_impl_1<'p, T, P1, R>(
def: *mut ffi::PyMethodDef,
_f: fn(&T, P1) -> err::PyResult<'p, R>
) -> MethodDescriptor<T> {
MethodDescriptor(def, marker::PhantomData)
}
pub unsafe fn py_method_impl_2<'p, T, P1, P2, R>(
def: *mut ffi::PyMethodDef,
_f: fn(&T, P1, P2) -> err::PyResult<'p, R>
) -> MethodDescriptor<T> {
MethodDescriptor(def, marker::PhantomData)
}
pub unsafe fn py_method_impl_3<'p, T, P1, P2, P3, R>(
def: *mut ffi::PyMethodDef,
_f: fn(&T, P1, P2, P3) -> err::PyResult<'p, R>
) -> MethodDescriptor<T> {
MethodDescriptor(def, marker::PhantomData)
}
pub unsafe fn py_method_impl_4<'p, T, P1, P2, P3, P4, R>(
def: *mut ffi::PyMethodDef,
_f: fn(&T, P1, P2, P3, P4) -> err::PyResult<'p, R>
) -> MethodDescriptor<T> {
MethodDescriptor(def, marker::PhantomData)
}
}
impl <'p, T> TypeMember<'p, T> for MethodDescriptor<T> where T: PythonObject<'p> {
@ -144,16 +256,16 @@ impl <'p, T> TypeMember<'p, T> for MethodDescriptor<T> where T: PythonObject<'p>
/// PyTuple, PyType, PyRustTypeBuilder, NoArgs};
/// use cpython::{exc};
///
/// fn method<'p>(ty: &PyType<'p>, args: &PyTuple<'p>) -> PyResult<'p, i32> {
/// fn method<'p>(py: Python<'p>) -> PyResult<'p, i32> {
/// Ok(42)
/// }
///
/// fn main() {
/// let gil = Python::acquire_gil();
/// let my_type = PyRustTypeBuilder::<i32>::new(gil.python(), "MyType")
/// .add("method", py_class_method!(method))
/// .add("method", py_class_method!(method()))
/// .finish().unwrap();
/// let result = my_type.as_object().call_method("method", &NoArgs, None).unwrap();
/// let result = my_type.as_object().call_method("method", NoArgs, None).unwrap();
/// assert_eq!(42, result.extract::<i32>().unwrap());
/// }
/// ```
@ -162,7 +274,8 @@ macro_rules! py_class_method {
($f: ident) => ( interpolate_idents! {{
unsafe extern "C" fn [ wrap_ $f ](
slf: *mut $crate::_detail::ffi::PyObject,
args: *mut $crate::_detail::ffi::PyObject)
args: *mut $crate::_detail::ffi::PyObject,
kwargs: *mut $crate::_detail::ffi::PyObject)
-> *mut $crate::_detail::ffi::PyObject
{
let _guard = $crate::_detail::PanicGuard::with_message("Rust panic in py_method!");
@ -171,7 +284,11 @@ macro_rules! py_class_method {
let slf = <$crate::PyType as $crate::PythonObject>::unchecked_downcast_from(slf);
let args = $crate::PyObject::from_borrowed_ptr(py, args);
let args = <$crate::PyTuple as $crate::PythonObject>::unchecked_downcast_from(args);
match $f(&slf, &args) {
let kwargs = match $crate::PyObject::from_borrowed_ptr_opt(py, kwargs) {
Some(kwargs) => Some(<$crate::PyDict as $crate::PythonObject>::unchecked_downcast_from(kwargs)),
None => None
};
match $f(&slf, &args, kwargs.as_ref()) {
Ok(val) => {
let obj = $crate::ToPyObject::into_py_object(val, py);
return $crate::PythonObject::into_object(obj).steal_ptr();
@ -184,24 +301,76 @@ macro_rules! py_class_method {
}
static mut [ method_def_ $f ]: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
//ml_name: bytes!(stringify!($f), "\0"),
ml_name: b"<rust method>\0" as *const u8 as *const $crate::_detail::libc::c_char,
ml_meth: Some([ wrap_ $f ]),
ml_flags: $crate::_detail::ffi::METH_VARARGS | $crate::_detail::ffi::METH_CLASS,
ml_name: 0 as *const $crate::_detail::libc::c_char,
ml_meth: None,
ml_flags: $crate::_detail::ffi::METH_VARARGS
| $crate::_detail::ffi::METH_KEYWORDS
| $crate::_detail::ffi::METH_CLASS,
ml_doc: 0 as *const $crate::_detail::libc::c_char
};
unsafe { $crate::_detail::py_class_method_impl(&mut [ method_def_ $f ], $f) }
unsafe {
[ method_def_ $f ].ml_name = concat!(stringify!($f), "\0").as_ptr() as *const _;
[ method_def_ $f ].ml_meth = Some(
std::mem::transmute::<$crate::_detail::ffi::PyCFunctionWithKeywords,
$crate::_detail::ffi::PyCFunction>([ wrap_ $f ])
);
$crate::_detail::py_class_method_impl(&mut [ method_def_ $f ])
}
}});
($f: ident ( $( $pname:ident : $ptype:ty ),* ) ) => ( interpolate_idents! {{
unsafe extern "C" fn [ wrap_ $f ](
slf: *mut $crate::_detail::ffi::PyObject,
args: *mut $crate::_detail::ffi::PyObject,
kwargs: *mut $crate::_detail::ffi::PyObject)
-> *mut $crate::_detail::ffi::PyObject
{
let _guard = $crate::_detail::PanicGuard::with_message("Rust panic in py_method!");
let py = $crate::_detail::bounded_assume_gil_acquired(&args);
let slf = $crate::PyObject::from_borrowed_ptr(py, slf);
let slf = <$crate::PyType as $crate::PythonObject>::unchecked_downcast_from(slf);
let args = $crate::PyObject::from_borrowed_ptr(py, args);
let args = <$crate::PyTuple as $crate::PythonObject>::unchecked_downcast_from(args);
let kwargs = match $crate::PyObject::from_borrowed_ptr_opt(py, kwargs) {
Some(kwargs) => Some(<$crate::PyDict as $crate::PythonObject>::unchecked_downcast_from(kwargs)),
None => None
};
match py_argparse!(Some(stringify!($f)), &args, kwargs.as_ref(),
( $($pname : $ptype),* ) { $f( py, $($pname),* ) })
{
Ok(val) => {
let obj = $crate::ToPyObject::into_py_object(val, py);
return $crate::PythonObject::into_object(obj).steal_ptr();
}
Err(e) => {
e.restore();
return ::std::ptr::null_mut();
}
}
}
static mut [ method_def_ $f ]: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
//ml_name: bytes!(stringify!($f), "\0"),
ml_name: 0 as *const $crate::_detail::libc::c_char,
ml_meth: None,
ml_flags: $crate::_detail::ffi::METH_VARARGS
| $crate::_detail::ffi::METH_KEYWORDS
| $crate::_detail::ffi::METH_CLASS,
ml_doc: 0 as *const $crate::_detail::libc::c_char
};
unsafe {
[ method_def_ $f ].ml_name = concat!(stringify!($f), "\0").as_ptr() as *const _;
[ method_def_ $f ].ml_meth = Some(
std::mem::transmute::<$crate::_detail::ffi::PyCFunctionWithKeywords,
$crate::_detail::ffi::PyCFunction>([ wrap_ $f ])
);
$crate::_detail::py_class_method_impl(&mut [ method_def_ $f ])
}
}})
}
pub struct ClassMethodDescriptor(*mut ffi::PyMethodDef);
// py_method_impl takes fn(&T) to ensure that the T in MethodDescriptor<T>
// corresponds to the T in the function signature.
pub unsafe fn py_class_method_impl<'p, R>(
def: *mut ffi::PyMethodDef,
_f: fn(&PyType<'p>, &PyTuple<'p>) -> err::PyResult<'p, R>
) -> ClassMethodDescriptor
{
#[inline]
pub unsafe fn py_class_method_impl(def: *mut ffi::PyMethodDef) -> ClassMethodDescriptor {
ClassMethodDescriptor(def)
}