replace py_fn! macro with pyfn attribute
This commit is contained in:
parent
319ee30c79
commit
706477f139
20
README.md
20
README.md
|
@ -84,7 +84,17 @@ use pyo3::{py, PyResult, Python, PyModule};
|
|||
#[py::modinit(rust2py)]
|
||||
fn init_mod(py: Python, m: &PyModule) -> PyResult<()> {
|
||||
m.add(py, "__doc__", "This module is implemented in Rust.")?;
|
||||
m.add(py, "sum_as_string", py_fn!(py, sum_as_string_py(a: i64, b:i64)))?;
|
||||
|
||||
#[pyfn(m, "sum_as_string")]
|
||||
// pyo3 aware function. All of our python interface could be
|
||||
// declared in a separate module.
|
||||
// Note that the py_fn!() macro automatically converts the arguments from
|
||||
// Python objects to Rust values; and the Rust return value back into a Python object.
|
||||
fn sum_as_string_py(_: Python, a:i64, b:i64) -> PyResult<String> {
|
||||
let out = sum_as_string(a, b);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -93,14 +103,6 @@ fn sum_as_string(a:i64, b:i64) -> String {
|
|||
format!("{}", a + b).to_string()
|
||||
}
|
||||
|
||||
// pyo3 aware function. All of our python interface could be
|
||||
// declared in a separate module.
|
||||
// Note that the py_fn!() macro automatically converts the arguments from
|
||||
// Python objects to Rust values; and the Rust return value back into a Python object.
|
||||
fn sum_as_string_py(_: Python, a:i64, b:i64) -> PyResult<String> {
|
||||
let out = sum_as_string(a, b);
|
||||
Ok(out)
|
||||
}
|
||||
```
|
||||
|
||||
For `setup.py` integration, see [setuptools-rust](https://github.com/PyO3/setuptools-rust)
|
||||
|
|
|
@ -122,30 +122,6 @@ pub fn methods(_: TokenStream, input: TokenStream) -> TokenStream {
|
|||
TokenStream::from_str(s.as_str()).unwrap()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
// do nothing, if impl block is not wrapped into #[methods] macro
|
||||
pub fn getter(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
input
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
// do nothing, if impl block is not wrapped into #[methods] macro
|
||||
pub fn setter(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
input
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
// do nothing, if impl block is not wrapped into #[methods] macro
|
||||
pub fn args(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
input
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
// do nothing, if impl block is not wrapped into #[methods] macro
|
||||
pub fn defaults(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
input
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn ptr(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
// Construct a string representation of the type definition
|
||||
|
|
|
@ -181,7 +181,9 @@ impl<'a> FnSpec<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_arg_ty_and_optional<'a>(name: &'a syn::Ident, ty: &'a syn::Ty) -> Option<&'a syn::Ty> {
|
||||
pub fn check_arg_ty_and_optional<'a>(name: &'a syn::Ident, ty: &'a syn::Ty)
|
||||
-> Option<&'a syn::Ty>
|
||||
{
|
||||
match ty {
|
||||
&syn::Ty::Path(_, ref path) => {
|
||||
//if let &Some(ref qs) = qs {
|
||||
|
|
|
@ -3,12 +3,34 @@
|
|||
use syn;
|
||||
use quote::Tokens;
|
||||
|
||||
use args;
|
||||
use method;
|
||||
use py_method;
|
||||
use utils;
|
||||
|
||||
|
||||
pub fn build_py3_module_init(ast: &mut syn::Item, attr: String) -> Tokens {
|
||||
let modname = &attr.to_string()[1..attr.to_string().len()-1].to_string();
|
||||
|
||||
match ast.node {
|
||||
syn::ItemKind::Fn(_, _, _, _, _, _) => {
|
||||
syn::ItemKind::Fn(_, _, _, _, _, ref mut block) => {
|
||||
let mut stmts = Vec::new();
|
||||
for stmt in block.stmts.iter_mut() {
|
||||
match stmt {
|
||||
&mut syn::Stmt::Item(ref mut item) => {
|
||||
if let Some(block) = wrap_fn(item) {
|
||||
for stmt in block.stmts.iter() {
|
||||
stmts.push(stmt.clone());
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
stmts.push(stmt.clone());
|
||||
}
|
||||
block.stmts = stmts;
|
||||
|
||||
py3_init(&ast.ident, &modname)
|
||||
},
|
||||
_ => panic!("#[modinit] can only be used with fn block"),
|
||||
|
@ -112,3 +134,195 @@ pub fn py2_init(fnname: &syn::Ident, name: &String) -> Tokens {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn wrap_fn(item: &mut syn::Item) -> Option<Box<syn::Block>> {
|
||||
let name = item.ident.clone();
|
||||
let mut new_attrs = Vec::new();
|
||||
let mut fnname = None;
|
||||
let mut modname = None;
|
||||
let mut fn_attrs = Vec::new();
|
||||
|
||||
for attr in item.attrs.iter() {
|
||||
match attr.value {
|
||||
syn::MetaItem::List(ref name, ref meta) => {
|
||||
match name.as_ref() {
|
||||
"pyfn" => {
|
||||
if meta.len() >= 2 {
|
||||
match meta[0] {
|
||||
syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref ident)) => {
|
||||
modname = Some(ident.clone());
|
||||
}
|
||||
_ => modname = None
|
||||
}
|
||||
match meta[1] {
|
||||
syn::NestedMetaItem::Literal(syn::Lit::Str(ref s, _)) => {
|
||||
fnname = Some(syn::Ident::from(s.as_str()));
|
||||
}
|
||||
_ => fnname = None
|
||||
}
|
||||
} else {
|
||||
println!("can not parse 'pyfn' params {:?}", attr);
|
||||
modname = None
|
||||
}
|
||||
if meta.len() >= 3 {
|
||||
match meta[2] {
|
||||
syn::NestedMetaItem::Literal(syn::Lit::Str(ref s, _)) => {
|
||||
fn_attrs = args::parse_arguments(s.as_ref());
|
||||
},
|
||||
_ => modname = None
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
new_attrs.push(attr.clone())
|
||||
}
|
||||
item.attrs.clear();
|
||||
item.attrs.extend(new_attrs);
|
||||
|
||||
if let None = fnname {
|
||||
return None
|
||||
}
|
||||
if let None = modname {
|
||||
return None
|
||||
}
|
||||
|
||||
match item.node {
|
||||
syn::ItemKind::Fn(ref decl, _, _, _, _, _) => {
|
||||
let mut py = false;
|
||||
let mut arguments = Vec::new();
|
||||
|
||||
for input in decl.inputs.iter() {
|
||||
match input {
|
||||
&syn::FnArg::SelfRef(_, _) | &syn::FnArg::SelfValue(_) => (),
|
||||
&syn::FnArg::Captured(ref pat, ref ty) => {
|
||||
let (mode, ident) = match pat {
|
||||
&syn::Pat::Ident(ref mode, ref ident, _) =>
|
||||
(mode, ident),
|
||||
_ =>
|
||||
panic!("unsupported argument: {:?}", pat),
|
||||
};
|
||||
|
||||
if !py {
|
||||
match ty {
|
||||
&syn::Ty::Path(_, ref path) =>
|
||||
if let Some(segment) = path.segments.last() {
|
||||
if segment.ident.as_ref() == "Python" {
|
||||
py = true;
|
||||
continue;
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
let opt = method::check_arg_ty_and_optional(&name, ty);
|
||||
arguments.push(method::FnArg {name: ident,
|
||||
mode: mode,
|
||||
ty: ty,
|
||||
optional: opt});
|
||||
}
|
||||
&syn::FnArg::Ignored(_) =>
|
||||
panic!("ignored argument: {:?}", name),
|
||||
}
|
||||
}
|
||||
|
||||
let ty = match decl.output {
|
||||
syn::FunctionRetTy::Default => syn::Ty::Infer,
|
||||
syn::FunctionRetTy::Ty(ref ty) => ty.clone()
|
||||
};
|
||||
|
||||
let spec = method::FnSpec {
|
||||
tp: method::FnType::Fn,
|
||||
attrs: fn_attrs,
|
||||
args: arguments,
|
||||
output: ty,
|
||||
};
|
||||
|
||||
let m = modname.unwrap();
|
||||
let fnname = fnname.unwrap();
|
||||
let wrapper = impl_wrap(&name, &spec);
|
||||
let item2 = item.clone();
|
||||
let doc = utils::get_doc(&item.attrs);
|
||||
|
||||
let tokens = quote! {
|
||||
fn test() {
|
||||
#item2
|
||||
|
||||
{
|
||||
use std;
|
||||
use pyo3 as _pyo3;
|
||||
|
||||
#wrapper
|
||||
|
||||
let def = pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#fnname),
|
||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(wrap),
|
||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS,
|
||||
ml_doc: #doc,
|
||||
}.as_method_def();
|
||||
|
||||
unsafe {
|
||||
let func = PyObject::from_owned_ptr_or_panic(
|
||||
py, pyo3::ffi::PyCFunction_New(
|
||||
&def as *const _ as *mut _, std::ptr::null_mut()));
|
||||
std::mem::forget(def);
|
||||
|
||||
#m.add(py, stringify!(#fnname), func)?
|
||||
}
|
||||
}
|
||||
}
|
||||
}.to_string();
|
||||
|
||||
println!("TEST {}", tokens);
|
||||
|
||||
let item = syn::parse_item(tokens.as_str()).unwrap();
|
||||
match item.node {
|
||||
syn::ItemKind::Fn(_, _, _, _, _, ref block) => {
|
||||
return Some(block.clone())
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
/// Generate static method wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub fn impl_wrap(name: &syn::Ident, spec: &method::FnSpec) -> Tokens {
|
||||
let names: Vec<&syn::Ident> = spec.args.iter().map(|item| item.name).collect();
|
||||
let cb = quote! {{
|
||||
#name(py, #(#names),*)
|
||||
}};
|
||||
|
||||
let body = py_method::impl_arg_params(spec, cb);
|
||||
let output = &spec.output;
|
||||
|
||||
quote! {
|
||||
#[allow(unused_mut)]
|
||||
unsafe extern "C" fn wrap(_slf: *mut _pyo3::ffi::PyObject,
|
||||
args: *mut _pyo3::ffi::PyObject,
|
||||
kwargs: *mut _pyo3::ffi::PyObject) -> *mut _pyo3::ffi::PyObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!(#name), "()");
|
||||
|
||||
_pyo3::callback::cb_meth(LOCATION, |py| {
|
||||
let args = _pyo3::PyTuple::from_borrowed_ptr(py, args);
|
||||
let kwargs = _pyo3::argparse::get_kwargs(py, kwargs);
|
||||
|
||||
let result: #output = {
|
||||
#body
|
||||
};
|
||||
_pyo3::callback::cb_convert(
|
||||
_pyo3::callback::PyObjectCallbackConverter, py, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -290,7 +290,7 @@ fn impl_call(_cls: &Box<syn::Ty>, fname: &syn::Ident, spec: &FnSpec) -> Tokens {
|
|||
}}
|
||||
}
|
||||
|
||||
fn impl_arg_params(spec: &FnSpec, body: Tokens) -> Tokens {
|
||||
pub fn impl_arg_params(spec: &FnSpec, body: Tokens) -> Tokens {
|
||||
if spec.args.is_empty() {
|
||||
return body
|
||||
}
|
||||
|
|
406
src/argparse.rs
406
src/argparse.rs
|
@ -1,23 +1,7 @@
|
|||
// Copyright (c) 2015 Daniel Grunwald
|
||||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
|
||||
|
||||
//! This module contains logic for parsing a python argument list.
|
||||
//! See also the macros `py_argparse!`, `py_fn!` and `py_method!`.
|
||||
|
||||
use ffi;
|
||||
use python::Python;
|
||||
|
@ -78,6 +62,10 @@ pub fn parse_args<'p>(py: Python<'p>,
|
|||
}
|
||||
},
|
||||
None => {
|
||||
if p.kw_only {
|
||||
return Err(err::PyErr::new::<exc::TypeError, _>(
|
||||
py, format!("Required keywordargument ('{}') not found", p.name)));
|
||||
}
|
||||
if i < nargs {
|
||||
*out = Some(args.get_item(py, i));
|
||||
} else {
|
||||
|
@ -107,253 +95,6 @@ pub fn parse_args<'p>(py: Python<'p>,
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// This macro is used to parse a parameter list into a set of variables.
|
||||
///
|
||||
/// Syntax: `py_argparse!(py, fname, args, kwargs, (parameter-list) { body })`
|
||||
///
|
||||
/// * `py`: the `Python` token
|
||||
/// * `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 parameter declarations.
|
||||
/// Parameter declarations have one of these formats:
|
||||
/// 1. `name`
|
||||
/// 2. `name: ty`
|
||||
/// 3. `name: ty = default_value`
|
||||
/// 4. `*name`
|
||||
/// 5. `*name : ty`
|
||||
/// 6. `**name`
|
||||
/// 7. `**name : ty`
|
||||
///
|
||||
/// The types used must implement the `FromPyObject` trait.
|
||||
/// If no type is specified, the parameter implicitly uses
|
||||
/// `&PyObject` (format 1), `&PyTuple` (format 4) or `&PyDict` (format 6).
|
||||
/// If a default value is specified, it must be a compile-time constant
|
||||
// of type `ty`.
|
||||
/// * `body`: expression of type `PyResult<_>`.
|
||||
/// The extracted argument values are available in this scope.
|
||||
///
|
||||
/// `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 and returns of that evaluation.
|
||||
/// If extraction fails, `py_argparse!()` returns a failed `PyResult` without evaluating `body`.
|
||||
///
|
||||
/// The `py_argparse!()` macro special-cases reference types (when `ty` starts with a `&` token):
|
||||
/// In this case, the macro uses the `RefFromPyObject` trait instead of the `FromPyObject` trait.
|
||||
/// When using at least one reference parameter, the `body` block is placed within a closure,
|
||||
/// so `return` statements might behave unexpectedly in this case. (this only affects direct use
|
||||
/// of `py_argparse!`; `py_fn!` is unaffected as the body there is always in a separate function
|
||||
/// from the generated argument-parsing code).
|
||||
#[macro_export]
|
||||
macro_rules! py_argparse {
|
||||
($py:expr, $fname:expr, $args:expr, $kwargs:expr, $plist:tt $body:block) => {
|
||||
py_argparse_parse_plist! { py_argparse_impl { $py, $fname, $args, $kwargs, $body, } $plist }
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_argparse_parse_plist {
|
||||
// Parses a parameter-list into a format more suitable for consumption by Rust macros.
|
||||
// py_argparse_parse_plist! { callback { initial_args } (plist) }
|
||||
// = callback! { initial_args [{ pname:ptype = [ {**} {default-value} ] } ...] }
|
||||
// The braces around the *s and the default-value are used even if they are empty.
|
||||
|
||||
// Special-case entry-point for empty parameter list:
|
||||
{ $callback:ident { $($initial_arg:tt)* } ( ) } => {
|
||||
$callback! { $($initial_arg)* [] }
|
||||
};
|
||||
// Regular entry point for non-empty parameter list:
|
||||
{ $callback:ident $initial_args:tt ( $( $p:tt )+ ) } => {
|
||||
// add trailing comma to plist so that the parsing step can assume every
|
||||
// parameter ends with a comma.
|
||||
py_argparse_parse_plist_impl! { $callback $initial_args [] ( $($p)*, ) }
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_argparse_parse_plist_impl {
|
||||
// TT muncher macro that does the main work for py_argparse_parse_plist!.
|
||||
|
||||
// Base case: all parameters handled
|
||||
{ $callback:ident { $($initial_arg:tt)* } $output:tt ( ) } => {
|
||||
$callback! { $($initial_arg)* $output }
|
||||
};
|
||||
// Kwargs parameter with reference extraction
|
||||
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
|
||||
( ** $name:ident : &$t:ty , $($tail:tt)* )
|
||||
} => {
|
||||
py_argparse_parse_plist_impl! {
|
||||
$callback $initial_args
|
||||
[ $($output)* { $name:&$t = [ {**} {} {$t} ] } ]
|
||||
($($tail)*)
|
||||
}
|
||||
};
|
||||
// Kwargs parameter
|
||||
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
|
||||
( ** $name:ident : $t:ty , $($tail:tt)* )
|
||||
} => {
|
||||
py_argparse_parse_plist_impl! {
|
||||
$callback $initial_args
|
||||
[ $($output)* { $name:$t = [ {**} {} {} ] } ]
|
||||
($($tail)*)
|
||||
}
|
||||
};
|
||||
// Kwargs parameter with implicit type
|
||||
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
|
||||
( ** $name:ident , $($tail:tt)* )
|
||||
} => {
|
||||
py_argparse_parse_plist_impl! {
|
||||
$callback $initial_args
|
||||
[ $($output)* { $name:Option<&$crate::PyDict> = [ {**} {} {} ] } ]
|
||||
($($tail)*)
|
||||
}
|
||||
};
|
||||
// Varargs parameter with reference extraction
|
||||
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
|
||||
( * $name:ident : &$t:ty , $($tail:tt)* )
|
||||
} => {
|
||||
py_argparse_parse_plist_impl! {
|
||||
$callback $initial_args
|
||||
[ $($output)* { $name:&$t = [ {*} {} {$t} ] } ]
|
||||
($($tail)*)
|
||||
}
|
||||
};
|
||||
// Varargs parameter
|
||||
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
|
||||
( * $name:ident : $t:ty , $($tail:tt)* )
|
||||
} => {
|
||||
py_argparse_parse_plist_impl! {
|
||||
$callback $initial_args
|
||||
[ $($output)* { $name:$t = [ {*} {} {} ] } ]
|
||||
($($tail)*)
|
||||
}
|
||||
};
|
||||
// Varargs parameter with implicit type
|
||||
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
|
||||
( * $name:ident , $($tail:tt)* )
|
||||
} => {
|
||||
py_argparse_parse_plist_impl! {
|
||||
$callback $initial_args
|
||||
[ $($output)* { $name:&$crate::PyTuple = [ {*} {} {} ] } ]
|
||||
($($tail)*)
|
||||
}
|
||||
};
|
||||
// Simple parameter with reference extraction
|
||||
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
|
||||
( $name:ident : &$t:ty , $($tail:tt)* )
|
||||
} => {
|
||||
py_argparse_parse_plist_impl! {
|
||||
$callback $initial_args
|
||||
[ $($output)* { $name:&$t = [ {} {} {$t} ] } ]
|
||||
($($tail)*)
|
||||
}
|
||||
};
|
||||
// Simple parameter
|
||||
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
|
||||
( $name:ident : $t:ty , $($tail:tt)* )
|
||||
} => {
|
||||
py_argparse_parse_plist_impl! {
|
||||
$callback $initial_args
|
||||
[ $($output)* { $name:$t = [ {} {} {} ] } ]
|
||||
($($tail)*)
|
||||
}
|
||||
};
|
||||
// Simple parameter with implicit type
|
||||
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
|
||||
( $name:ident , $($tail:tt)* )
|
||||
} => {
|
||||
py_argparse_parse_plist_impl! {
|
||||
$callback $initial_args
|
||||
[ $($output)* { $name:&$crate::PyObject = [ {} {} {} ] } ]
|
||||
($($tail)*)
|
||||
}
|
||||
};
|
||||
// Optional parameter with reference extraction
|
||||
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
|
||||
( $name:ident : &$t:ty = $default:expr, $($tail:tt)* )
|
||||
} => {
|
||||
py_argparse_parse_plist_impl! {
|
||||
$callback $initial_args
|
||||
[ $($output)* { $name:&$t = [ {} {$default} {$t} ] } ]
|
||||
($($tail)*)
|
||||
}
|
||||
};
|
||||
// Optional parameter
|
||||
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
|
||||
( $name:ident : $t:ty = $default:expr , $($tail:tt)* )
|
||||
} => {
|
||||
py_argparse_parse_plist_impl! {
|
||||
$callback $initial_args
|
||||
[ $($output)* { $name:$t = [ {} {$default} {} ] } ]
|
||||
($($tail)*)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// The main py_argparse!() macro, except that it expects the parameter-list
|
||||
// in the output format of py_argparse_parse_plist!().
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_argparse_impl {
|
||||
// special case: function signature is (*args, **kwargs),
|
||||
// so we can directly pass along our inputs without calling parse_args().
|
||||
($py:expr, $fname:expr, $args:expr, $kwargs:expr, $body:block,
|
||||
[
|
||||
{ $pargs:ident : $pargs_type:ty = [ {*} {} {} ] }
|
||||
{ $pkwargs:ident : $pkwargs_type:ty = [ {**} {} {} ] }
|
||||
]
|
||||
) => {{
|
||||
let _py: $crate::Python = $py;
|
||||
// TODO: use extract() to be more flexible in which type is expected
|
||||
let $pargs: $pargs_type = $args;
|
||||
let $pkwargs: $pkwargs_type = $kwargs;
|
||||
$body
|
||||
}};
|
||||
|
||||
// normal argparse logic
|
||||
($py:expr, $fname:expr, $args:expr, $kwargs:expr, $body:block,
|
||||
[ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]
|
||||
) => {{
|
||||
const PARAMS: &'static [$crate::argparse::ParamDescription<'static>] = &[
|
||||
$(
|
||||
py_argparse_param_description! { $pname : $ptype = $detail }
|
||||
),*
|
||||
];
|
||||
let py: $crate::Python = $py;
|
||||
let mut output = [$( py_replace_expr!($pname None) ),*];
|
||||
match $crate::argparse::parse_args(py, $fname, PARAMS, $args, $kwargs, false, false, &mut output) {
|
||||
Ok(()) => {
|
||||
// Experimental slice pattern syntax would be really nice here (#23121)
|
||||
//let [$(ref $pname),*] = output;
|
||||
// We'll use an iterator instead.
|
||||
let mut _iter = output.iter();
|
||||
// We'll have to generate a bunch of nested `match` statements
|
||||
// (at least until we can use ? + catch, assuming that will be hygienic wrt. macros),
|
||||
// so use a recursive helper macro for that:
|
||||
py_argparse_extract!(py, _iter, $body,
|
||||
[ $( { $pname : $ptype = $detail } )* ])
|
||||
},
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
// Like py_argparse_impl!(), but accepts `*mut ffi::PyObject` for $args and $kwargs.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_argparse_raw {
|
||||
($py:ident, $fname:expr, $args:expr, $kwargs:expr, $plist:tt $body:block) => {{
|
||||
let args: $crate::PyTuple = $crate::PyTuple::from_borrowed_ptr($py, $args);
|
||||
let kwargs: Option<$crate::PyDict> = $crate::argparse::get_kwargs($py, $kwargs);
|
||||
let ret = py_argparse_impl!($py, $fname, &args, kwargs.as_ref(), $body, $plist);
|
||||
$py.release(kwargs);
|
||||
$py.release(args);
|
||||
ret
|
||||
}};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn get_kwargs(py: Python, ptr: *mut ffi::PyObject) -> Option<PyDict> {
|
||||
|
@ -364,79 +105,6 @@ pub unsafe fn get_kwargs(py: Python, ptr: *mut ffi::PyObject) -> Option<PyDict>
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_argparse_param_description {
|
||||
// normal parameter
|
||||
{ $pname:ident : $ptype:ty = [ {} {} $rtype:tt ] } => (
|
||||
$crate::argparse::ParamDescription {
|
||||
name: stringify!($pname),
|
||||
is_optional: false,
|
||||
kw_only: false,
|
||||
}
|
||||
);
|
||||
// optional parameters
|
||||
{ $pname:ident : $ptype:ty = [ {} {$default:expr} {$($rtype:tt)*} ] } => (
|
||||
$crate::argparse::ParamDescription {
|
||||
name: stringify!($pname),
|
||||
is_optional: true,
|
||||
kw_only: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_argparse_extract {
|
||||
// base case
|
||||
( $py:expr, $iter:expr, $body:block, [] ) => { $body };
|
||||
// normal parameter
|
||||
( $py:expr, $iter:expr, $body:block,
|
||||
[ { $pname:ident : $ptype:ty = [ {} {} {} ] } $($tail:tt)* ]
|
||||
) => {
|
||||
// First unwrap() asserts the iterated sequence is long enough (which should be guaranteed);
|
||||
// second unwrap() asserts the parameter was not missing (which fn parse_args already checked for).
|
||||
match <$ptype as $crate::FromPyObject>::extract(
|
||||
$py, $iter.next().unwrap().as_ref().unwrap()) {
|
||||
Ok($pname) => py_argparse_extract!($py, $iter, $body, [$($tail)*]),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
};
|
||||
// normal parameter with reference extraction
|
||||
( $py:expr, $iter:expr, $body:block,
|
||||
[ { $pname:ident : $ptype:ty = [ {} {} {$rtype:ty} ] } $($tail:tt)* ]
|
||||
) => {
|
||||
// First unwrap() asserts the iterated sequence is long enough (which should be guaranteed);
|
||||
// second unwrap() asserts the parameter was not missing (which fn parse_args already checked for).
|
||||
match <$rtype as $crate::RefFromPyObject>::with_extracted(
|
||||
$py, $iter.next().unwrap().as_ref().unwrap(),
|
||||
|$pname: $ptype| py_argparse_extract!($py, $iter, $body, [$($tail)*])
|
||||
) {
|
||||
Ok(v) => v,
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
};
|
||||
// optional parameter
|
||||
( $py:expr, $iter:expr, $body:block,
|
||||
[ { $pname:ident : $ptype:ty = [ {} {$default:expr} {} ] } $($tail:tt)* ]
|
||||
) => {
|
||||
match $iter.next().unwrap().as_ref().map(|obj| obj.extract::<_>($py)).unwrap_or(Ok($default)) {
|
||||
Ok($pname) => py_argparse_extract!($py, $iter, $body, [$($tail)*]),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
};
|
||||
// optional parameter with reference extraction
|
||||
( $py:expr, $iter:expr, $body:block,
|
||||
[ { $pname:ident : $ptype:ty = [ {} {$default:expr} {$rtype:ty} ] } $($tail:tt)* ]
|
||||
) => {
|
||||
//unwrap() asserts the iterated sequence is long enough (which should be guaranteed);
|
||||
$crate::argparse::with_extracted_or_default($py, $iter.next().unwrap().as_ref(),
|
||||
|$pname: $ptype| py_argparse_extract!($py, $iter, $body, [$($tail)*]),
|
||||
$default)
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)] // used in py_argparse_extract!() macro
|
||||
pub fn with_extracted_or_default<'p, P: ?Sized, R, F>(
|
||||
py: Python, obj: Option<&'p PyObject>, f: F, default: &'static P) -> PyResult<R>
|
||||
|
@ -451,65 +119,3 @@ pub fn with_extracted_or_default<'p, P: ?Sized, R, F>(
|
|||
None => f(default)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use python::Python;
|
||||
use objects::PyTuple;
|
||||
use conversion::IntoPyTuple;
|
||||
|
||||
#[test]
|
||||
pub fn test_parse() {
|
||||
let gil_guard = Python::acquire_gil();
|
||||
let py = gil_guard.python();
|
||||
let mut called = false;
|
||||
let tuple = ("abc", 42).into_tuple(py);
|
||||
py_argparse!(py, None, &tuple, None, (x: &str, y: i32) {
|
||||
assert_eq!(x, "abc");
|
||||
assert_eq!(y, 42);
|
||||
called = true;
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
assert!(called);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_default_param_type() {
|
||||
let gil_guard = Python::acquire_gil();
|
||||
let py = gil_guard.python();
|
||||
let mut called = false;
|
||||
let tuple = ("abc",).into_tuple(py);
|
||||
py_argparse!(py, None, &tuple, None, (x) {
|
||||
assert_eq!(*x, tuple.get_item(py, 0));
|
||||
called = true;
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
assert!(called);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_default_value() {
|
||||
let gil_guard = Python::acquire_gil();
|
||||
let py = gil_guard.python();
|
||||
let mut called = false;
|
||||
let tuple = (0, "foo").into_tuple(py);
|
||||
py_argparse!(py, None, &tuple, None, (x: usize = 42, y: &str = "abc") {
|
||||
assert_eq!(x, 0);
|
||||
assert_eq!(y, "foo");
|
||||
called = true;
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
assert!(called);
|
||||
|
||||
let mut called = false;
|
||||
let tuple = PyTuple::empty(py);
|
||||
py_argparse!(py, None, &tuple, None, (x: usize = 42, y: &str = "abc") {
|
||||
assert_eq!(x, 42);
|
||||
assert_eq!(y, "abc");
|
||||
called = true;
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
assert!(called);
|
||||
}
|
||||
}
|
||||
|
|
141
src/function.rs
141
src/function.rs
|
@ -1,141 +0,0 @@
|
|||
// Copyright (c) 2015 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use std::ptr;
|
||||
|
||||
use ffi;
|
||||
use python::Python;
|
||||
use objects::PyObject;
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_method_def {
|
||||
($name: expr, $flags: expr, $wrap: expr) => {{
|
||||
static mut METHOD_DEF: $crate::ffi::PyMethodDef = $crate::ffi::PyMethodDef {
|
||||
//ml_name: bytes!(stringify!($name), "\0"),
|
||||
ml_name: 0 as *const $crate::c_char,
|
||||
ml_meth: None,
|
||||
ml_flags: $crate::ffi::METH_VARARGS | $crate::ffi::METH_KEYWORDS | $flags,
|
||||
ml_doc: 0 as *const $crate::c_char
|
||||
};
|
||||
METHOD_DEF.ml_name = concat!($name, "\0").as_ptr() as *const _;
|
||||
METHOD_DEF.ml_meth = Some(
|
||||
::std::mem::transmute::<$crate::ffi::PyCFunctionWithKeywords,
|
||||
$crate::ffi::PyCFunction>($wrap)
|
||||
);
|
||||
&mut METHOD_DEF
|
||||
}}
|
||||
}
|
||||
|
||||
/// Creates a Python callable object that invokes a Rust function.
|
||||
///
|
||||
/// There are two forms of this macro:
|
||||
///
|
||||
/// 1. `py_fn!(py, f(parameter_list))`
|
||||
/// 1. `py_fn!(py, f(parameter_list) -> PyResult<T> { body })`
|
||||
///
|
||||
/// both forms return a value of type `PyObject`.
|
||||
/// This python object is a callable object that invokes
|
||||
/// the Rust function when called.
|
||||
///
|
||||
/// When called, the arguments are converted into
|
||||
/// the Rust types specified in the parameter list.
|
||||
/// See `py_argparse!()` for details on argument parsing.
|
||||
///
|
||||
/// Form 1:
|
||||
///
|
||||
/// * `py` must be an expression of type `Python`
|
||||
/// * `f` must be the name of a function that is compatible with the specified
|
||||
/// parameter list, except that a single parameter of type `Python` is prepended.
|
||||
/// The function must return `PyResult<T>` for some `T` that implements `ToPyObject`.
|
||||
///
|
||||
/// Form 2:
|
||||
///
|
||||
/// * `py` must be an identifier refers to a `Python` value.
|
||||
/// The function body will also have access to a `Python` variable of this name.
|
||||
/// * `f` must be an identifier.
|
||||
/// * The function return type must be `PyResult<T>` for some `T` that
|
||||
/// implements `ToPyObject`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// #[macro_use] extern crate pyo3;
|
||||
/// use pyo3::{exc, Python, PyResult, PyErr, PyDict};
|
||||
///
|
||||
/// fn multiply(py: Python, lhs: i32, rhs: i32) -> PyResult<i32> {
|
||||
/// match lhs.checked_mul(rhs) {
|
||||
/// Some(val) => Ok(val),
|
||||
/// None => Err(PyErr::new_lazy_init(py.get_type::<exc::OverflowError>(), None))
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// let py = gil.python();
|
||||
/// let dict = PyDict::new(py);
|
||||
/// dict.set_item(py, "multiply", py_fn!(py, multiply(lhs: i32, rhs: i32))).unwrap();
|
||||
/// py.run("print(multiply(6, 7))", None, Some(&dict)).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! py_fn {
|
||||
($py:expr, $f:ident $plist:tt ) => {
|
||||
py_argparse_parse_plist! { py_fn_impl { $py, $f } $plist }
|
||||
};
|
||||
($py:ident, $f:ident $plist:tt -> $ret:ty { $($body:tt)* } ) => {
|
||||
py_argparse_parse_plist! { py_fn_impl { $py, $f, $ret, { $($body)* } } $plist }
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_fn_impl {
|
||||
// Form 1: reference existing function
|
||||
{ $py:expr, $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
|
||||
unsafe extern "C" fn wrap(
|
||||
_slf: *mut $crate::ffi::PyObject,
|
||||
args: *mut $crate::ffi::PyObject,
|
||||
kwargs: *mut $crate::ffi::PyObject)
|
||||
-> *mut $crate::ffi::PyObject
|
||||
{
|
||||
$crate::callback::handle(
|
||||
stringify!($f), $crate::callback::PyObjectCallbackConverter, |py|
|
||||
{
|
||||
py_argparse_raw!(py, Some(stringify!($f)), args, kwargs,
|
||||
[ $( { $pname : $ptype = $detail } )* ]
|
||||
{ $f(py $(, $pname )* ) })
|
||||
})
|
||||
}
|
||||
unsafe {
|
||||
$crate::function::py_fn_impl($py,
|
||||
py_method_def!(stringify!($f), 0, wrap))
|
||||
}
|
||||
}};
|
||||
// Form 2: inline function definition
|
||||
{ $py:ident, $f:ident, $ret:ty, $body:block [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
|
||||
fn $f($py: $crate::Python $( , $pname : $ptype )* ) -> $ret $body
|
||||
py_fn_impl!($py, $f [ $( { $pname : $ptype = $detail } )* ])
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn py_fn_impl<'p>(py: Python<'p>,
|
||||
method_def: *mut ffi::PyMethodDef) -> PyObject {
|
||||
PyObject::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(method_def, ptr::null_mut()))
|
||||
}
|
25
src/lib.rs
25
src/lib.rs
|
@ -63,23 +63,33 @@
|
|||
//! This function will be called when the module is imported, and is responsible
|
||||
//! for adding the module's members.
|
||||
//!
|
||||
//! To creates a Python callable object that invokes a Rust function, specify rust
|
||||
//! function and decroate it with `#[pyfn()]` attribute. `pyfn()` accepts three parameters.
|
||||
//!
|
||||
//! 1. `m`: The module name.
|
||||
//! 2. function name, name of function visible to Python code.
|
||||
//! 3. arguments description string, i.e. "param1, param2=None, *, param3='default'"
|
||||
//!
|
||||
//!
|
||||
//! # Example
|
||||
//! ```
|
||||
//! #![feature(proc_macro)]
|
||||
//! #[macro_use] extern crate pyo3;
|
||||
//! use pyo3::{py, Python, PyResult, PyObject, PyModule};
|
||||
//! use pyo3::{py, Python, PyResult, PyObject, PyModule, PyString};
|
||||
//!
|
||||
//! #[py::modinit(hello)]
|
||||
//! fn init_module(py: Python, m: &PyModule) -> PyResult<()> {
|
||||
//! m.add(py, "__doc__", "Module documentation string")?;
|
||||
//! m.add(py, "run", py_fn!(py, run()))?;
|
||||
//!
|
||||
//! #[pyfn(m, "run_rust_func")]
|
||||
//! fn run(py: Python, name: PyString) -> PyResult<PyObject> {
|
||||
//! println!("Rust says: Hello {} of Python!", name);
|
||||
//! Ok(py.None())
|
||||
//! }
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//!
|
||||
//! fn run(py: Python) -> PyResult<PyObject> {
|
||||
//! println!("Rust says: Hello Python!");
|
||||
//! Ok(py.None())
|
||||
//! }
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
|
@ -105,7 +115,7 @@
|
|||
//!
|
||||
//! ```python
|
||||
//! >>> import hello
|
||||
//! >>> hello.run()
|
||||
//! >>> hello.run_rust_func("test")
|
||||
//! Rust says: Hello Python!
|
||||
//! ```
|
||||
|
||||
|
@ -184,7 +194,6 @@ mod pythonrun;
|
|||
pub mod callback;
|
||||
pub mod typeob;
|
||||
pub mod argparse;
|
||||
pub mod function;
|
||||
pub mod buffer;
|
||||
pub mod freelist;
|
||||
|
||||
|
|
|
@ -44,8 +44,8 @@ impl IntoPyObject for String {
|
|||
}
|
||||
}
|
||||
|
||||
// /// Allows extracting strings from Python objects.
|
||||
// /// Accepts Python `str` and `unicode` objects.
|
||||
/// Allows extracting strings from Python objects.
|
||||
/// Accepts Python `str` and `unicode` objects.
|
||||
pyobject_extract!(py, obj to Cow<'source, str> => {
|
||||
try!(obj.cast_as::<PyString>(py)).to_string(py)
|
||||
});
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
#[macro_use] extern crate pyo3;
|
||||
|
||||
use pyo3::{PyResult, Python, NoArgs, ObjectProtocol, PyDict};
|
||||
use std::sync::atomic;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
|
||||
#[test]
|
||||
fn no_args() {
|
||||
static CALL_COUNT: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT;
|
||||
|
||||
fn f(_py: Python) -> PyResult<usize> {
|
||||
Ok(CALL_COUNT.fetch_add(1, Relaxed))
|
||||
}
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let obj = py_fn!(py, f());
|
||||
|
||||
assert_eq!(CALL_COUNT.load(Relaxed), 0);
|
||||
assert_eq!(obj.call(py, NoArgs, None).unwrap().extract::<i32>(py).unwrap(), 0);
|
||||
assert_eq!(obj.call(py, NoArgs, None).unwrap().extract::<i32>(py).unwrap(), 1);
|
||||
assert_eq!(CALL_COUNT.load(Relaxed), 2);
|
||||
assert!(obj.call(py, (1,), None).is_err());
|
||||
assert_eq!(CALL_COUNT.load(Relaxed), 2);
|
||||
assert_eq!(obj.call(py, NoArgs, Some(&PyDict::new(py)))
|
||||
.unwrap().extract::<i32>(py).unwrap(), 2);
|
||||
assert_eq!(CALL_COUNT.load(Relaxed), 3);
|
||||
let dict = PyDict::new(py);
|
||||
dict.set_item(py, "param", 42).unwrap();
|
||||
assert!(obj.call(py, NoArgs, Some(&dict)).is_err());
|
||||
assert_eq!(CALL_COUNT.load(Relaxed), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_arg() {
|
||||
fn f(_py: Python, i: usize) -> PyResult<usize> {
|
||||
Ok(i * 2)
|
||||
}
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let obj = py_fn!(py, f(i: usize));
|
||||
|
||||
assert!(obj.call(py, NoArgs, None).is_err());
|
||||
assert_eq!(obj.call(py, (1,), None).unwrap().extract::<i32>(py).unwrap(), 2);
|
||||
assert!(obj.call(py, (1, 2), None).is_err());
|
||||
|
||||
let dict = PyDict::new(py);
|
||||
dict.set_item(py, "i", 42).unwrap();
|
||||
assert_eq!(obj.call(py, NoArgs, Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 84);
|
||||
assert!(obj.call(py, (1,), Some(&dict)).is_err());
|
||||
dict.set_item(py, "j", 10).unwrap();
|
||||
assert!(obj.call(py, NoArgs, Some(&dict)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inline_two_args() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let obj = py_fn!(py, f(a: i32, b: i32) -> PyResult<i32> {
|
||||
drop(py); // avoid unused variable warning
|
||||
Ok(a * b)
|
||||
});
|
||||
|
||||
assert!(obj.call(py, NoArgs, None).is_err());
|
||||
assert_eq!(obj.call(py, (6, 7), None).unwrap().extract::<i32>(py).unwrap(), 42);
|
||||
}
|
||||
|
||||
/* TODO: reimplement flexible sig support
|
||||
#[test]
|
||||
fn flexible_sig() {
|
||||
fn f(py: Python, args: &PyTuple, kwargs: &PyDict) -> PyResult<usize> {
|
||||
Ok(args.len(py) + 100 * kwargs.map_or(0, |kwargs| kwargs.len(py)))
|
||||
}
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let obj = py_fn!(f(*args, **kwargs)).to_py_object(py);
|
||||
|
||||
assert_eq!(obj.call(py, NoArgs, None).unwrap().extract::<i32>(py).unwrap(), 0);
|
||||
assert_eq!(obj.call(py, (1,), None).unwrap().extract::<i32>(py).unwrap(), 1);
|
||||
assert_eq!(obj.call(py, (1,2), None).unwrap().extract::<i32>(py).unwrap(), 2);
|
||||
|
||||
let dict = PyDict::new(py);
|
||||
dict.set_item(py, "i", 42).unwrap();
|
||||
assert_eq!(obj.call(py, NoArgs, Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 100);
|
||||
assert_eq!(obj.call(py, (1,2), Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 102);
|
||||
dict.set_item(py, "j", 10).unwrap();
|
||||
assert_eq!(obj.call(py, (1,2,3), Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 203);
|
||||
}
|
||||
*/
|
||||
|
Loading…
Reference in New Issue