pyo3/pyo3cls/src/py_method.rs

422 lines
14 KiB
Rust
Raw Normal View History

2017-05-16 05:24:06 +00:00
// Copyright (c) 2017-present PyO3 Project and Contributors
use syn;
2017-05-18 07:05:49 +00:00
use quote::{Tokens, ToTokens};
2017-05-20 06:14:59 +00:00
use method::{FnArg, FnSpec, FnType};
2017-05-18 07:05:49 +00:00
2017-05-16 05:24:06 +00:00
pub fn gen_py_method<'a>(cls: &Box<syn::Ty>, name: &syn::Ident,
2017-05-20 06:14:59 +00:00
sig: &mut syn::MethodSig, meth_attrs: &mut Vec<syn::Attribute>) -> Tokens
2017-05-16 07:07:36 +00:00
{
2017-05-16 05:24:06 +00:00
check_generic(name, sig);
2017-05-22 05:22:45 +00:00
println!("====0");
2017-05-16 05:24:06 +00:00
2017-05-20 06:14:59 +00:00
let spec = FnSpec::parse(name, sig, meth_attrs);
2017-05-16 05:24:06 +00:00
2017-05-22 05:22:45 +00:00
println!("====1");
2017-05-20 06:14:59 +00:00
match spec.tp {
2017-05-16 18:58:18 +00:00
FnType::Fn =>
2017-05-20 06:14:59 +00:00
impl_py_method_def(name, &impl_wrap(cls, name, &spec)),
FnType::FnNew =>
impl_py_method_def_new(name, &impl_wrap_new(cls, name, &spec)),
FnType::FnCall =>
impl_py_method_def_call(name, &impl_wrap(cls, name, &spec)),
FnType::Getter(ref getter) =>
impl_py_getter_def(name, getter, &impl_wrap_getter(cls, name, &spec)),
FnType::Setter(ref setter) =>
impl_py_setter_def(name, setter, &impl_wrap_setter(cls, name, &spec)),
2017-05-18 07:05:49 +00:00
}
}
2017-05-16 05:24:06 +00:00
2017-05-16 18:58:18 +00:00
fn check_generic(name: &syn::Ident, sig: &syn::MethodSig) {
2017-05-16 05:24:06 +00:00
if !sig.generics.ty_params.is_empty() {
panic!("python method can not be generic: {:?}", name);
}
}
2017-05-20 06:14:59 +00:00
/// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords)
pub fn impl_wrap(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
let cb = impl_call(cls, name, &spec);
let body = impl_arg_params(&spec, cb);
quote! {
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!(#cls), ".", stringify!(#name), "()");
_pyo3::callback::handle_callback(
LOCATION, _pyo3::callback::PyObjectCallbackConverter, |py|
{
let args: _pyo3::PyTuple =
_pyo3::PyObject::from_borrowed_ptr(py, args).unchecked_cast_into();
let kwargs: Option<_pyo3::PyDict> = _pyo3::argparse::get_kwargs(py, kwargs);
let ret = {
#body
};
_pyo3::PyDrop::release_ref(args, py);
_pyo3::PyDrop::release_ref(kwargs, py);
ret
})
}
2017-05-16 05:24:06 +00:00
}
}
2017-05-20 06:14:59 +00:00
/// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords)
pub fn impl_wrap_new(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
let cb = impl_class_new(cls, name, spec);
let body = impl_arg_params(spec, cb);
2017-05-16 05:24:06 +00:00
quote! {
2017-05-20 06:14:59 +00:00
unsafe extern "C" fn wrap(cls: *mut _pyo3::ffi::PyTypeObject,
args: *mut _pyo3::ffi::PyObject,
kwargs: *mut _pyo3::ffi::PyObject) -> *mut _pyo3::ffi::PyObject
2017-05-16 05:24:06 +00:00
{
const LOCATION: &'static str = concat!(
stringify!(#cls), ".", stringify!(#name), "()");
2017-05-18 20:46:09 +00:00
_pyo3::callback::handle_callback(
2017-05-18 18:15:06 +00:00
LOCATION, _pyo3::callback::PyObjectCallbackConverter, |py|
2017-05-16 05:24:06 +00:00
{
2017-05-18 18:15:06 +00:00
let args: _pyo3::PyTuple =
_pyo3::PyObject::from_borrowed_ptr(py, args).unchecked_cast_into();
2017-05-18 20:46:09 +00:00
let kwargs: Option<_pyo3::PyDict> = _pyo3::argparse::get_kwargs(py, kwargs);
2017-05-16 05:24:06 +00:00
let ret = {
#body
};
2017-05-18 18:15:06 +00:00
_pyo3::PyDrop::release_ref(args, py);
_pyo3::PyDrop::release_ref(kwargs, py);
2017-05-16 05:24:06 +00:00
ret
})
}
}
}
2017-05-16 18:58:18 +00:00
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
2017-05-20 06:14:59 +00:00
fn impl_wrap_getter(cls: &Box<syn::Ty>, name: &syn::Ident, _spec: &FnSpec) -> Tokens {
2017-05-16 18:58:18 +00:00
quote! {
2017-05-18 18:15:06 +00:00
unsafe extern "C" fn wrap (slf: *mut _pyo3::ffi::PyObject,
_: *mut _pyo3::c_void)
-> *mut _pyo3::ffi::PyObject
2017-05-16 18:58:18 +00:00
{
const LOCATION: &'static str = concat!(
stringify!(#cls), ".getter_", stringify!(#name), "()");
2017-05-18 18:15:06 +00:00
_pyo3::callback::handle_callback(
LOCATION, _pyo3::callback::PyObjectCallbackConverter, |py|
2017-05-16 18:58:18 +00:00
{
2017-05-18 18:15:06 +00:00
let slf = _pyo3::PyObject::from_borrowed_ptr(
2017-05-16 18:58:18 +00:00
py, slf).unchecked_cast_into::<#cls>();
let ret = slf.#name(py);
2017-05-18 18:15:06 +00:00
_pyo3::PyDrop::release_ref(slf, py);
2017-05-16 18:58:18 +00:00
ret
})
}
}
}
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
2017-05-20 06:14:59 +00:00
fn impl_wrap_setter(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
let val_ty = spec.args[0].ty;
2017-05-16 18:58:18 +00:00
quote! {
2017-05-18 18:15:06 +00:00
unsafe extern "C" fn wrap(slf: *mut _pyo3::ffi::PyObject,
value: *mut _pyo3::ffi::PyObject,
_: *mut _pyo3::c_void) -> _pyo3::c_int
2017-05-16 18:58:18 +00:00
{
const LOCATION: &'static str = concat!(
stringify!(#cls), ".setter", stringify!(#name), "()");
2017-05-18 18:15:06 +00:00
_pyo3::callback::handle_callback(
LOCATION, _pyo3::callback::UnitCallbackConverter, |py|
2017-05-16 18:58:18 +00:00
{
2017-05-18 18:15:06 +00:00
let slf = _pyo3::PyObject::from_borrowed_ptr(py, slf)
2017-05-16 18:58:18 +00:00
.unchecked_cast_into::<#cls>();
2017-05-18 18:15:06 +00:00
let value = _pyo3::PyObject::from_borrowed_ptr(py, value);
let ret = match <#val_ty as _pyo3::FromPyObject>::extract(py, &value) {
Ok(val) => {
let ret = slf.#name(py, val);
ret.map(|o| ())
}
Err(e) => Err(e)
};
2017-05-18 18:15:06 +00:00
_pyo3::PyDrop::release_ref(slf, py);
_pyo3::PyDrop::release_ref(value, py);
ret
2017-05-16 18:58:18 +00:00
})
}
}
}
2017-05-20 06:14:59 +00:00
fn impl_call(cls: &Box<syn::Ty>, fname: &syn::Ident, spec: &FnSpec) -> Tokens {
let names: Vec<&syn::Ident> = spec.args.iter().map(|item| item.name).collect();
2017-05-16 05:24:06 +00:00
quote! {
{
2017-05-18 18:15:06 +00:00
let slf = _pyo3::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<#cls>();
2017-05-16 05:24:06 +00:00
let ret = slf.#fname(py, #(#names),*);
2017-05-18 18:15:06 +00:00
_pyo3::PyDrop::release_ref(slf, py);
2017-05-16 05:24:06 +00:00
ret
}
}
}
2017-05-20 06:14:59 +00:00
fn impl_class_new(cls: &Box<syn::Ty>, fname: &syn::Ident, spec: &FnSpec) -> Tokens {
let names: Vec<&syn::Ident> = spec.args.iter().map(|item| item.name).collect();
quote! {
{
let cls = _pyo3::PyType::from_type_ptr(py, cls);
let ret = #cls::#fname(&cls, py, #(#names),*);
_pyo3::PyDrop::release_ref(cls, py);
ret
}
}
}
fn impl_arg_params(spec: &FnSpec, body: Tokens) -> Tokens {
2017-05-16 05:24:06 +00:00
let mut params = Vec::new();
2017-05-20 06:14:59 +00:00
for arg in spec.args.iter() {
if ! (spec.is_args(&arg.name) || spec.is_kwargs(&arg.name)) {
2017-05-18 07:05:49 +00:00
let name = arg.name.as_ref();
let opt = if let Some(_) = arg.optional {
syn::Ident::from("true")
} else {
2017-05-20 06:14:59 +00:00
if let Some(_) = spec.default_value(&arg.name) {
2017-05-18 07:05:49 +00:00
syn::Ident::from("true")
} else {
syn::Ident::from("false")
}
};
params.push(
quote! {
2017-05-18 18:15:06 +00:00
_pyo3::argparse::ParamDescription{name: #name, is_optional: #opt,}
2017-05-18 07:05:49 +00:00
}
);
}
2017-05-16 05:24:06 +00:00
}
let placeholders: Vec<syn::Ident> = params.iter().map(
|_| syn::Ident::from("None")).collect();
// generate extrat args
2017-05-20 06:14:59 +00:00
let mut rargs = spec.args.clone();
rargs.reverse();
2017-05-16 05:24:06 +00:00
let mut body = body;
for arg in rargs.iter() {
2017-05-18 07:05:49 +00:00
body = impl_arg_param(&arg, &spec, &body);
2017-05-16 05:24:06 +00:00
}
2017-05-18 07:05:49 +00:00
let accept_args = syn::Ident::from(
2017-05-20 06:14:59 +00:00
if spec.accept_args() { "true" } else { "false" });
2017-05-18 07:05:49 +00:00
let accept_kwargs = syn::Ident::from(
2017-05-20 06:14:59 +00:00
if spec.accept_kwargs() { "true" } else { "false" });
2017-05-18 07:05:49 +00:00
2017-05-16 05:24:06 +00:00
// create array of arguments, and then parse
quote! {
2017-05-18 18:15:06 +00:00
const PARAMS: &'static [_pyo3::argparse::ParamDescription<'static>] = &[
2017-05-16 05:24:06 +00:00
#(#params),*
];
let mut output = [#(#placeholders),*];
2017-05-18 18:15:06 +00:00
match _pyo3::argparse::parse_args(
2017-05-18 07:05:49 +00:00
py, Some(LOCATION), PARAMS, &args,
kwargs.as_ref(), #accept_args, #accept_kwargs, &mut output) {
2017-05-16 05:24:06 +00:00
Ok(_) => {
let mut _iter = output.iter();
#body
},
Err(err) => Err(err)
}
}
}
2017-05-20 06:14:59 +00:00
fn impl_arg_param(arg: &FnArg, spec: &FnSpec, body: &Tokens) -> Tokens {
2017-05-16 05:24:06 +00:00
let ty = arg.ty;
let name = arg.name;
// 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).
2017-05-20 06:14:59 +00:00
if spec.is_args(&name) {
2017-05-16 05:24:06 +00:00
quote! {
2017-05-18 18:15:06 +00:00
match <#ty as _pyo3::FromPyObject>::extract(py, args.as_object())
2017-05-16 05:24:06 +00:00
{
Ok(#name) => {
#body
}
Err(e) => Err(e)
}
}
}
2017-05-20 06:14:59 +00:00
else if spec.is_kwargs(&name) {
2017-05-18 07:05:49 +00:00
quote! {
let #name = kwargs.as_ref();
#body
}
}
else {
if let Some(ref opt_ty) = arg.optional {
// default value
let mut default = Tokens::new();
2017-05-20 06:14:59 +00:00
if let Some(d) = spec.default_value(name) {
2017-05-18 18:15:06 +00:00
let dt = quote!{ Some(#d) };
dt.to_tokens(&mut default);
2017-05-18 07:05:49 +00:00
} else {
syn::Ident::from("None").to_tokens(&mut default);
}
quote! {
match match _iter.next().unwrap().as_ref() {
Some(obj) => {
2017-05-19 04:35:08 +00:00
if obj == &py.None() {
Ok(#default)
} else {
match <#opt_ty as _pyo3::FromPyObject>::extract(py, obj) {
Ok(obj) => Ok(Some(obj)),
Err(e) => Err(e)
}
2017-05-18 07:05:49 +00:00
}
},
2017-05-18 18:15:06 +00:00
None => Ok(#default)
2017-05-18 07:05:49 +00:00
} {
Ok(#name) => #body,
Err(e) => Err(e)
}
}
2017-05-20 06:14:59 +00:00
} else if let Some(default) = spec.default_value(name) {
2017-05-18 07:05:49 +00:00
quote! {
match match _iter.next().unwrap().as_ref() {
Some(obj) => {
2017-05-19 04:35:08 +00:00
if obj == &py.None() {
Ok(#default)
} else {
match <#ty as _pyo3::FromPyObject>::extract(py, obj) {
Ok(obj) => Ok(obj),
Err(e) => Err(e),
}
2017-05-18 07:05:49 +00:00
}
},
None => Ok(#default)
} {
Ok(#name) => #body,
Err(e) => Err(e)
}
}
}
else {
quote! {
2017-05-18 18:15:06 +00:00
match <#ty as _pyo3::FromPyObject>::extract(
2017-05-18 07:05:49 +00:00
py, _iter.next().unwrap().as_ref().unwrap())
{
Ok(#name) => {
#body
}
Err(e) => Err(e)
}
}
}
}
}
2017-05-20 06:14:59 +00:00
pub fn impl_py_method_def(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
quote! {
_pyo3::class::PyMethodDefType::Method({
#wrapper
2017-05-18 07:05:49 +00:00
2017-05-20 06:14:59 +00:00
_pyo3::class::PyMethodDef {
ml_name: stringify!(#name),
ml_meth: _pyo3::class::PyMethodType::PyCFunctionWithKeywords(wrap),
ml_flags: _pyo3::ffi::METH_VARARGS | _pyo3::ffi::METH_KEYWORDS,
ml_doc: "",
}
})
2017-05-18 07:05:49 +00:00
}
}
2017-05-20 06:14:59 +00:00
pub fn impl_py_method_def_new(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
quote! {
_pyo3::class::PyMethodDefType::New({
#wrapper
2017-05-18 07:05:49 +00:00
2017-05-20 06:14:59 +00:00
_pyo3::class::PyMethodDef {
ml_name: stringify!(#name),
ml_meth: _pyo3::class::PyMethodType::PyNewFunc(wrap),
ml_flags: _pyo3::ffi::METH_VARARGS | _pyo3::ffi::METH_KEYWORDS,
ml_doc: "",
}
})
2017-05-18 07:05:49 +00:00
}
2017-05-16 05:24:06 +00:00
}
2017-05-20 06:14:59 +00:00
pub fn impl_py_method_def_call(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
2017-05-16 18:58:18 +00:00
quote! {
2017-05-20 06:14:59 +00:00
_pyo3::class::PyMethodDefType::Call({
2017-05-16 18:58:18 +00:00
#wrapper
2017-05-18 18:15:06 +00:00
_pyo3::class::PyMethodDef {
2017-05-16 18:58:18 +00:00
ml_name: stringify!(#name),
2017-05-18 18:15:06 +00:00
ml_meth: _pyo3::class::PyMethodType::PyCFunctionWithKeywords(wrap),
ml_flags: _pyo3::ffi::METH_VARARGS | _pyo3::ffi::METH_KEYWORDS,
2017-05-16 18:58:18 +00:00
ml_doc: "",
}
})
}
}
2017-05-20 06:14:59 +00:00
fn impl_py_setter_def(name: &syn::Ident, setter: &Option<String>, wrapper: &Tokens) -> Tokens {
let n = if let &Some(ref name) = setter {
2017-05-16 18:58:18 +00:00
name.to_string()
} else {
let n = String::from(name.as_ref());
if n.starts_with("set_") {
n[4..].to_string()
} else {
n
}
};
quote! {
2017-05-18 18:15:06 +00:00
_pyo3::class::PyMethodDefType::Setter({
2017-05-16 18:58:18 +00:00
#wrapper
2017-05-18 18:15:06 +00:00
_pyo3::class::PySetterDef {
2017-05-16 18:58:18 +00:00
name: #n,
meth: wrap,
doc: "",
}
})
}
}
2017-05-20 06:14:59 +00:00
fn impl_py_getter_def(name: &syn::Ident, getter: &Option<String>, wrapper: &Tokens) -> Tokens {
let n = if let &Some(ref name) = getter {
2017-05-16 18:58:18 +00:00
name.to_string()
} else {
let n = String::from(name.as_ref());
if n.starts_with("get_") {
n[4..].to_string()
} else {
n
2017-05-16 05:24:06 +00:00
}
2017-05-16 18:58:18 +00:00
};
quote! {
2017-05-18 18:15:06 +00:00
_pyo3::class::PyMethodDefType::Getter({
2017-05-16 18:58:18 +00:00
#wrapper
2017-05-18 18:15:06 +00:00
_pyo3::class::PyGetterDef {
2017-05-16 18:58:18 +00:00
name: #n,
meth: wrap,
doc: "",
}
})
}
2017-05-16 05:24:06 +00:00
}