parent
103bde7db1
commit
b43b481980
|
@ -10,9 +10,9 @@ categories = ["api-bindings", "development-tools::ffi"]
|
|||
license = "Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
quote="0.3"
|
||||
log="0.4"
|
||||
quote="0.5"
|
||||
|
||||
[dependencies.syn]
|
||||
version="0.11"
|
||||
features=["full"]
|
||||
version="0.13"
|
||||
features=["full", "parsing", "printing", "extra-traits"]
|
||||
|
|
|
@ -10,7 +10,7 @@ pub enum Argument {
|
|||
Kwarg(String, String),
|
||||
}
|
||||
|
||||
pub fn parse_arguments(items: &[syn::NestedMetaItem]) -> Vec<Argument> {
|
||||
pub fn parse_arguments(items: &[syn::NestedMeta]) -> Vec<Argument> {
|
||||
let mut arguments = Vec::new();
|
||||
let mut has_kw = false;
|
||||
let mut has_varargs = false;
|
||||
|
@ -22,7 +22,7 @@ pub fn parse_arguments(items: &[syn::NestedMetaItem]) -> Vec<Argument> {
|
|||
|
||||
for item in items.iter() {
|
||||
match item {
|
||||
&syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref ident)) => {
|
||||
&syn::NestedMeta::Meta(syn::Meta::Word(ref ident)) => {
|
||||
// arguments in form #[args(somename)]
|
||||
if has_kwargs {
|
||||
println!("syntax error, keyword arguments is defined: {:?}", args_str);
|
||||
|
@ -35,11 +35,11 @@ pub fn parse_arguments(items: &[syn::NestedMetaItem]) -> Vec<Argument> {
|
|||
}
|
||||
arguments.push(Argument::Arg(ident.as_ref().to_owned(), None))
|
||||
}
|
||||
&syn::NestedMetaItem::MetaItem(syn::MetaItem::NameValue(ref ident, ref lit)) => {
|
||||
let name = ident.as_ref().to_owned();
|
||||
match lit {
|
||||
&syn::Lit::Str(ref s, _) => {
|
||||
if s == "*" { // #[args(args="*")]
|
||||
&syn::NestedMeta::Meta(syn::Meta::NameValue(ref nv)) => {
|
||||
let name = nv.ident.as_ref().to_owned();
|
||||
match nv.lit {
|
||||
syn::Lit::Str(ref litstr) => {
|
||||
if litstr.value() == "*" { // #[args(args="*")]
|
||||
if has_kwargs {
|
||||
println!("* - syntax error, keyword arguments is defined: {:?}",
|
||||
args_str);
|
||||
|
@ -51,8 +51,7 @@ pub fn parse_arguments(items: &[syn::NestedMetaItem]) -> Vec<Argument> {
|
|||
}
|
||||
has_varargs = true;
|
||||
arguments.push(Argument::VarArgs(name));
|
||||
}
|
||||
else if s == "**" { // #[args(kwargs="**")]
|
||||
} else if litstr.value() == "**" { // #[args(kwargs="**")]
|
||||
if has_kwargs {
|
||||
println!("arguments already define ** (kw args): {:?}",
|
||||
args_str);
|
||||
|
@ -62,7 +61,7 @@ pub fn parse_arguments(items: &[syn::NestedMetaItem]) -> Vec<Argument> {
|
|||
arguments.push(Argument::KeywordArgs(name));
|
||||
} else {
|
||||
if has_varargs {
|
||||
arguments.push(Argument::Kwarg(name, s.clone()))
|
||||
arguments.push(Argument::Kwarg(name, litstr.value().clone()))
|
||||
} else {
|
||||
if has_kwargs {
|
||||
println!("syntax error, keyword arguments is defined: {:?}",
|
||||
|
@ -70,13 +69,13 @@ pub fn parse_arguments(items: &[syn::NestedMetaItem]) -> Vec<Argument> {
|
|||
return Vec::new()
|
||||
}
|
||||
has_kw = true;
|
||||
arguments.push(Argument::Arg(name, Some(s.clone())))
|
||||
arguments.push(Argument::Arg(name, Some(litstr.value().clone())))
|
||||
}
|
||||
}
|
||||
}
|
||||
&syn::Lit::Int(ref s, _) => {
|
||||
syn::Lit::Int(ref litint) => {
|
||||
if has_varargs {
|
||||
arguments.push(Argument::Kwarg(name, format!("{}", s)));
|
||||
arguments.push(Argument::Kwarg(name, format!("{}", litint.value())));
|
||||
} else {
|
||||
if has_kwargs {
|
||||
println!("syntax error, keyword arguments is defined: {:?}",
|
||||
|
@ -84,12 +83,12 @@ pub fn parse_arguments(items: &[syn::NestedMetaItem]) -> Vec<Argument> {
|
|||
return Vec::new()
|
||||
}
|
||||
has_kw = true;
|
||||
arguments.push(Argument::Arg(name, Some(format!("{}", s))));
|
||||
arguments.push(Argument::Arg(name, Some(format!("{}", litint.value()))));
|
||||
}
|
||||
}
|
||||
&syn::Lit::Bool(ref b) => {
|
||||
syn::Lit::Bool(ref litb) => {
|
||||
if has_varargs {
|
||||
arguments.push(Argument::Kwarg(name, format!("{}", b)));
|
||||
arguments.push(Argument::Kwarg(name, format!("{}", litb.value)));
|
||||
} else {
|
||||
if has_kwargs {
|
||||
println!("syntax error, keyword arguments is defined: {:?}",
|
||||
|
@ -97,20 +96,20 @@ pub fn parse_arguments(items: &[syn::NestedMetaItem]) -> Vec<Argument> {
|
|||
return Vec::new()
|
||||
}
|
||||
has_kw = true;
|
||||
arguments.push(Argument::Arg(name, Some(format!("{}", b))));
|
||||
arguments.push(Argument::Arg(name, Some(format!("{}", litb.value))));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("Only string literal is supported, got: {:?}", lit);
|
||||
println!("Only string literal is supported, got: {:?}", nv.lit);
|
||||
return Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
&syn::NestedMetaItem::Literal(ref lit) => {
|
||||
&syn::NestedMeta::Literal(ref lit) => {
|
||||
match lit {
|
||||
&syn::Lit::Str(ref s, _) => {
|
||||
&syn::Lit::Str(ref lits) => {
|
||||
// #[args("*")]
|
||||
if s == "*" {
|
||||
if lits.value() == "*" {
|
||||
if has_kwargs {
|
||||
println!(
|
||||
"syntax error, keyword arguments is defined: {:?}",
|
||||
|
@ -127,7 +126,7 @@ pub fn parse_arguments(items: &[syn::NestedMetaItem]) -> Vec<Argument> {
|
|||
arguments.push(Argument::VarArgsSeparator);
|
||||
} else {
|
||||
println!("Unknown string literal, got: {:?} args: {:?}",
|
||||
s, args_str);
|
||||
lits.value(), args_str);
|
||||
return Vec::new()
|
||||
}
|
||||
}
|
||||
|
@ -154,11 +153,11 @@ mod test {
|
|||
use syn;
|
||||
use args::{Argument, parse_arguments};
|
||||
|
||||
fn items(s: &'static str) -> Vec<syn::NestedMetaItem> {
|
||||
fn items(s: &'static str) -> Vec<syn::NestedMeta> {
|
||||
let i = syn::parse_outer_attr(s).unwrap();
|
||||
|
||||
match i.value {
|
||||
syn::MetaItem::List(_, items) => {
|
||||
syn::Meta::List(_, items) => {
|
||||
items
|
||||
}
|
||||
_ => unreachable!()
|
||||
|
|
|
@ -45,14 +45,17 @@ impl MethodProto {
|
|||
}
|
||||
|
||||
|
||||
pub fn impl_method_proto(cls: &Box<syn::Ty>,
|
||||
sig: &mut syn::MethodSig,
|
||||
meth: &MethodProto) -> Tokens {
|
||||
pub fn impl_method_proto(
|
||||
cls: &Box<syn::Type>,
|
||||
sig: &mut syn::MethodSig,
|
||||
meth: &MethodProto
|
||||
) -> Tokens {
|
||||
|
||||
let decl = sig.decl.clone();
|
||||
|
||||
match *meth {
|
||||
MethodProto::Free{name: _, proto} => {
|
||||
let p = syn::Ident::from(proto);
|
||||
let p: syn::Path = syn::parse_str(proto).unwrap();
|
||||
return quote! {
|
||||
impl<'p> #p<'p> for #cls {}
|
||||
}
|
||||
|
@ -61,16 +64,16 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
|
|||
};
|
||||
|
||||
match decl.output {
|
||||
syn::FunctionRetTy::Ty(ref ty) => {
|
||||
syn::ReturnType::Type(_, ref ty) => {
|
||||
match *meth {
|
||||
MethodProto::Free{name: _, proto: _} => unreachable!(),
|
||||
MethodProto::Unary{name: _, pyres, proto} => {
|
||||
let p = syn::Ident::from(proto);
|
||||
let p: syn::Path = syn::parse_str(proto).unwrap();
|
||||
let (ty, succ) = get_res_success(ty);
|
||||
|
||||
let tmp = extract_decl(syn::parse_item(
|
||||
quote! {fn test(&self)
|
||||
-> <#cls as #p<'p>>::Result {}}.as_str()).unwrap());
|
||||
let tmp = extract_decl(parse_quote!{
|
||||
fn test(&self) -> <#cls as #p<'p>>::Result {}
|
||||
});
|
||||
sig.decl.output = tmp.output.clone();
|
||||
modify_self_ty(sig);
|
||||
|
||||
|
@ -90,25 +93,27 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
|
|||
}
|
||||
},
|
||||
MethodProto::Binary{name: n, arg, pyres, proto} => {
|
||||
|
||||
if sig.decl.inputs.len() <= 1 {
|
||||
println!("Not enough arguments for {}", n);
|
||||
return Tokens::new();
|
||||
}
|
||||
let p = syn::Ident::from(proto);
|
||||
|
||||
let p: syn::Path = syn::parse_str(proto).unwrap();
|
||||
let arg_name = syn::Ident::from(arg);
|
||||
let arg_ty = get_arg_ty(sig, 1);
|
||||
let (ty, succ) = get_res_success(ty);
|
||||
|
||||
let tmp = extract_decl(syn::parse_item(
|
||||
quote! {fn test(
|
||||
&self,
|
||||
arg: <#cls as #p<'p>>::#arg_name)
|
||||
-> <#cls as #p<'p>>::Result {}}.as_str()).unwrap());
|
||||
let tmp2 = extract_decl(syn::parse_item(
|
||||
quote! {fn test(
|
||||
&self,
|
||||
arg: Option<<#cls as #p<'p>>::#arg_name>)
|
||||
-> <#cls as #p<'p>>::Result {}}.as_str()).unwrap());
|
||||
let tmp = extract_decl(
|
||||
parse_quote!{
|
||||
fn test(&self,arg: <#cls as #p<'p>>::#arg_name)-> <#cls as #p<'p>>::Result {}
|
||||
});
|
||||
|
||||
let tmp2 = extract_decl(
|
||||
parse_quote!{
|
||||
fn test( &self, arg: Option<<#cls as #p<'p>>::#arg_name>) -> <#cls as #p<'p>>::Result {}
|
||||
});
|
||||
|
||||
modify_arg_ty(sig, 1, &tmp, &tmp2);
|
||||
modify_self_ty(sig);
|
||||
|
||||
|
@ -134,7 +139,7 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
|
|||
print_err(format!("Not enough arguments {}", n), quote!(sig));
|
||||
return Tokens::new();
|
||||
}
|
||||
let p = syn::Ident::from(proto);
|
||||
let p: syn::Path = syn::parse_str(proto).unwrap();
|
||||
let arg1_name = syn::Ident::from(arg1);
|
||||
let arg1_ty = get_arg_ty(sig, 0);
|
||||
let arg2_name = syn::Ident::from(arg2);
|
||||
|
@ -142,16 +147,16 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
|
|||
let (ty, succ) = get_res_success(ty);
|
||||
|
||||
// rewrite ty
|
||||
let tmp = extract_decl(syn::parse_item(
|
||||
quote! {fn test(
|
||||
let tmp = extract_decl(
|
||||
parse_quote!{fn test(
|
||||
arg1: <#cls as #p<'p>>::#arg1_name,
|
||||
arg2: <#cls as #p<'p>>::#arg2_name)
|
||||
-> <#cls as #p<'p>>::Result {}}.as_str()).unwrap());
|
||||
let tmp2 = extract_decl(syn::parse_item(
|
||||
quote! {fn test(
|
||||
-> <#cls as #p<'p>>::Result {}});
|
||||
let tmp2 = extract_decl(
|
||||
parse_quote!{fn test(
|
||||
arg1: Option<<#cls as #p<'p>>::#arg1_name>,
|
||||
arg2: Option<<#cls as #p<'p>>::#arg2_name>)
|
||||
-> <#cls as #p<'p>>::Result {}}.as_str()).unwrap());
|
||||
-> <#cls as #p<'p>>::Result {}});
|
||||
modify_arg_ty(sig, 0, &tmp, &tmp2);
|
||||
modify_arg_ty(sig, 1, &tmp, &tmp2);
|
||||
|
||||
|
@ -179,7 +184,7 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
|
|||
print_err(format!("Not enough arguments {}", n), quote!(sig));
|
||||
return Tokens::new();
|
||||
}
|
||||
let p = syn::Ident::from(proto);
|
||||
let p: syn::Path = syn::parse_str(proto).unwrap();
|
||||
let arg1_name = syn::Ident::from(arg1);
|
||||
let arg1_ty = get_arg_ty(sig, 1);
|
||||
let arg2_name = syn::Ident::from(arg2);
|
||||
|
@ -187,18 +192,18 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
|
|||
let (ty, succ) = get_res_success(ty);
|
||||
|
||||
// rewrite ty
|
||||
let tmp = extract_decl(syn::parse_item(
|
||||
quote! {fn test(
|
||||
let tmp = extract_decl(
|
||||
parse_quote! {fn test(
|
||||
&self,
|
||||
arg1: <#cls as #p<'p>>::#arg1_name,
|
||||
arg2: <#cls as #p<'p>>::#arg2_name)
|
||||
-> <#cls as #p<'p>>::Result {}}.as_str()).unwrap());
|
||||
let tmp2 = extract_decl(syn::parse_item(
|
||||
quote! {fn test(
|
||||
-> <#cls as #p<'p>>::Result {}});
|
||||
let tmp2 = extract_decl(
|
||||
parse_quote! {fn test(
|
||||
&self,
|
||||
arg1: Option<<#cls as #p<'p>>::#arg1_name>,
|
||||
arg2: Option<<#cls as #p<'p>>::#arg2_name>)
|
||||
-> <#cls as #p<'p>>::Result {}}.as_str()).unwrap());
|
||||
-> <#cls as #p<'p>>::Result {}});
|
||||
modify_arg_ty(sig, 1, &tmp, &tmp2);
|
||||
modify_arg_ty(sig, 2, &tmp, &tmp2);
|
||||
modify_self_ty(sig);
|
||||
|
@ -227,7 +232,7 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
|
|||
print_err(format!("Not enough arguments {}", n), quote!(sig));
|
||||
return Tokens::new();
|
||||
}
|
||||
let p = syn::Ident::from(proto);
|
||||
let p: syn::Path = syn::parse_str(proto).unwrap();
|
||||
let arg1_name = syn::Ident::from(arg1);
|
||||
let arg1_ty = get_arg_ty(sig, 0);
|
||||
let arg2_name = syn::Ident::from(arg2);
|
||||
|
@ -237,18 +242,18 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
|
|||
let (ty, succ) = get_res_success(ty);
|
||||
|
||||
// rewrite ty
|
||||
let tmp = extract_decl(syn::parse_item(
|
||||
quote! {fn test(
|
||||
let tmp = extract_decl(
|
||||
parse_quote! {fn test(
|
||||
arg1: <#cls as #p<'p>>::#arg1_name,
|
||||
arg2: <#cls as #p<'p>>::#arg2_name,
|
||||
arg3: <#cls as #p<'p>>::#arg3_name)
|
||||
-> <#cls as #p<'p>>::Result {}}.as_str()).unwrap());
|
||||
let tmp2 = extract_decl(syn::parse_item(
|
||||
quote! {fn test(
|
||||
-> <#cls as #p<'p>>::Result {}});
|
||||
let tmp2 = extract_decl(
|
||||
parse_quote! {fn test(
|
||||
arg1: Option<<#cls as #p<'p>>::#arg1_name>,
|
||||
arg2: Option<<#cls as #p<'p>>::#arg2_name>,
|
||||
arg3: Option<<#cls as #p<'p>>::#arg3_name>)
|
||||
-> <#cls as #p<'p>>::Result {}}.as_str()).unwrap());
|
||||
-> <#cls as #p<'p>>::Result {}});
|
||||
modify_arg_ty(sig, 0, &tmp, &tmp2);
|
||||
modify_arg_ty(sig, 1, &tmp, &tmp2);
|
||||
modify_arg_ty(sig, 2, &tmp, &tmp2);
|
||||
|
@ -279,7 +284,7 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
|
|||
print_err(format!("Not enough arguments {}", n), quote!(sig));
|
||||
return Tokens::new();
|
||||
}
|
||||
let p = syn::Ident::from(proto);
|
||||
let p: syn::Path = syn::parse_str(proto).unwrap();
|
||||
let arg1_name = syn::Ident::from(arg1);
|
||||
let arg1_ty = get_arg_ty(sig, 1);
|
||||
let arg2_name = syn::Ident::from(arg2);
|
||||
|
@ -289,20 +294,20 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
|
|||
let (ty, succ) = get_res_success(ty);
|
||||
|
||||
// rewrite ty
|
||||
let tmp = extract_decl(syn::parse_item(
|
||||
quote! {fn test(
|
||||
let tmp = extract_decl(
|
||||
parse_quote! {fn test(
|
||||
&self,
|
||||
arg1: <#cls as #p<'p>>::#arg1_name,
|
||||
arg2: <#cls as #p<'p>>::#arg2_name,
|
||||
arg3: <#cls as #p<'p>>::#arg3_name)
|
||||
-> <#cls as #p<'p>>::Result {}}.as_str()).unwrap());
|
||||
let tmp2 = extract_decl(syn::parse_item(
|
||||
quote! {fn test(
|
||||
-> <#cls as #p<'p>>::Result {}});
|
||||
let tmp2 = extract_decl(
|
||||
parse_quote! {fn test(
|
||||
&self,
|
||||
arg1: Option<<#cls as #p<'p>>::#arg1_name>,
|
||||
arg2: Option<<#cls as #p<'p>>::#arg2_name>,
|
||||
arg3: Option<<#cls as #p<'p>>::#arg3_name>)
|
||||
-> <#cls as #p<'p>>::Result {}}.as_str()).unwrap());
|
||||
-> <#cls as #p<'p>>::Result {}});
|
||||
modify_arg_ty(sig, 1, &tmp, &tmp2);
|
||||
modify_arg_ty(sig, 2, &tmp, &tmp2);
|
||||
modify_arg_ty(sig, 3, &tmp, &tmp2);
|
||||
|
@ -326,72 +331,68 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
|
|||
|
||||
|
||||
// TODO: better arg ty detection
|
||||
fn get_arg_ty(sig: &syn::MethodSig, idx: usize) -> syn::Ty {
|
||||
fn get_arg_ty(sig: &syn::MethodSig, idx: usize) -> syn::Type {
|
||||
let mut ty = match sig.decl.inputs[idx] {
|
||||
syn::FnArg::Captured(_, ref arg_ty) => {
|
||||
match arg_ty {
|
||||
&syn::Ty::Path(_, ref path) => {
|
||||
syn::FnArg::Captured(ref cap) => {
|
||||
match cap.ty {
|
||||
syn::Type::Path(ref ty) => {
|
||||
// use only last path segment for Option<>
|
||||
let seg = path.segments.last().unwrap().clone();
|
||||
let seg = ty.path.segments.last().unwrap().value().clone();
|
||||
if seg.ident.as_ref() == "Option" {
|
||||
match seg.parameters {
|
||||
syn::PathParameters::AngleBracketed(ref data) => {
|
||||
if let Some(ty) = data.types.last() {
|
||||
return ty.clone()
|
||||
}
|
||||
match seg.arguments {
|
||||
syn::PathArguments::AngleBracketed(ref data) => {
|
||||
if let Some(pair) = data.args.last() {
|
||||
match pair.value() {
|
||||
syn::GenericArgument::Type(ref ty) => return ty.clone(),
|
||||
_ => panic!("Option only accepted for concrete types"),
|
||||
}
|
||||
};
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
arg_ty.clone()
|
||||
cap.ty.clone()
|
||||
},
|
||||
_ => arg_ty.clone()
|
||||
_ => cap.ty.clone()
|
||||
}
|
||||
},
|
||||
_ => panic!("fn arg type is not supported"),
|
||||
};
|
||||
|
||||
match ty {
|
||||
syn::Ty::Rptr(ref mut lifetime, _) => {
|
||||
match lifetime {
|
||||
&mut None => {
|
||||
*lifetime = Some(syn::Lifetime {ident: syn::Ident::from("'p")})
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => ()
|
||||
|
||||
if let syn::Type::Reference(ref mut r) = ty {
|
||||
r.lifetime.get_or_insert(parse_quote!{'p});
|
||||
}
|
||||
|
||||
ty
|
||||
}
|
||||
|
||||
// Success
|
||||
fn get_res_success(ty: &syn::Ty) -> (Tokens, syn::Ty) {
|
||||
fn get_res_success(ty: &syn::Type) -> (Tokens, syn::GenericArgument) {
|
||||
let mut result;
|
||||
let mut succ;
|
||||
|
||||
match ty {
|
||||
&syn::Ty::Path(_, ref path) => {
|
||||
if let Some(segment) = path.segments.last() {
|
||||
match segment.ident.as_ref() {
|
||||
&syn::Type::Path(ref typath) => {
|
||||
if let Some(segment) = typath.path.segments.last() {
|
||||
match segment.value().ident.as_ref() {
|
||||
// check for PyResult<T>
|
||||
"PyResult" => match segment.parameters {
|
||||
syn::PathParameters::AngleBracketed(ref data) => {
|
||||
"PyResult" => match segment.value().arguments {
|
||||
syn::PathArguments::AngleBracketed(ref data) => {
|
||||
result = true;
|
||||
succ = data.types[0].clone();
|
||||
succ = data.args[0].clone();
|
||||
|
||||
// check for PyResult<Option<T>>
|
||||
match data.types[0] {
|
||||
syn::Ty::Path(_, ref path) =>
|
||||
if let Some(segment) = path.segments.last() {
|
||||
match segment.ident.as_ref() {
|
||||
match data.args[0] {
|
||||
syn::GenericArgument::Type(syn::Type::Path(ref typath)) =>
|
||||
if let Some(segment) = typath.path.segments.last() {
|
||||
match segment.value().ident.as_ref() {
|
||||
// get T from Option<T>
|
||||
"Option" => match segment.parameters {
|
||||
syn::PathParameters::AngleBracketed(ref data) =>
|
||||
"Option" => match segment.value().arguments {
|
||||
syn::PathArguments::AngleBracketed(ref data) =>
|
||||
{
|
||||
result = false;
|
||||
succ = data.types[0].clone();
|
||||
succ = data.args[0].clone();
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
|
@ -404,10 +405,10 @@ fn get_res_success(ty: &syn::Ty) -> (Tokens, syn::Ty) {
|
|||
_ => panic!("fn result type is not supported"),
|
||||
},
|
||||
_ => panic!("fn result type has to be PyResult or (), got {:?}",
|
||||
segment.ident.as_ref())
|
||||
segment.value().ident.as_ref())
|
||||
}
|
||||
} else {
|
||||
panic!("fn result is not supported {:?}", path)
|
||||
panic!("fn result is not supported {:?}", typath)
|
||||
}
|
||||
}
|
||||
_ => panic!("not supported: {:?}", ty),
|
||||
|
@ -425,8 +426,8 @@ fn get_res_success(ty: &syn::Ty) -> (Tokens, syn::Ty) {
|
|||
|
||||
|
||||
fn extract_decl(spec: syn::Item) -> syn::FnDecl {
|
||||
match spec.node {
|
||||
syn::ItemKind::Fn(decl, _, _, _, _, _) => *decl,
|
||||
match spec {
|
||||
syn::Item::Fn(f) => *f.decl,
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
@ -437,18 +438,18 @@ fn modify_arg_ty(sig: &mut syn::MethodSig, idx: usize,
|
|||
{
|
||||
let arg = sig.decl.inputs[idx].clone();
|
||||
match arg {
|
||||
syn::FnArg::Captured(ref pat, ref arg_ty) => {
|
||||
match arg_ty {
|
||||
&syn::Ty::Path(_, ref path) => {
|
||||
let seg = path.segments.last().unwrap().clone();
|
||||
syn::FnArg::Captured(ref cap) => {
|
||||
match cap.ty {
|
||||
syn::Type::Path(ref typath) => {
|
||||
let seg = typath.path.segments.last().unwrap().value().clone();
|
||||
if seg.ident.as_ref() == "Option" {
|
||||
sig.decl.inputs[idx] = fix_name(pat, &decl2.inputs[idx]);
|
||||
sig.decl.inputs[idx] = fix_name(&cap.pat, &decl2.inputs[idx]);
|
||||
} else {
|
||||
sig.decl.inputs[idx] = fix_name(pat, &decl1.inputs[idx]);
|
||||
sig.decl.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx]);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
sig.decl.inputs[idx] = fix_name(pat, &decl1.inputs[idx]);
|
||||
sig.decl.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -458,20 +459,22 @@ fn modify_arg_ty(sig: &mut syn::MethodSig, idx: usize,
|
|||
sig.decl.output = decl1.output.clone();
|
||||
}
|
||||
|
||||
fn modify_self_ty(sig: &mut syn::MethodSig)
|
||||
{
|
||||
match sig.decl.inputs[0] {
|
||||
syn::FnArg::SelfRef(ref mut lifetime, _) => {
|
||||
*lifetime = Some(syn::Lifetime {ident: syn::Ident::from("'p")})
|
||||
},
|
||||
_ => panic!("not supported"),
|
||||
fn modify_self_ty(sig: &mut syn::MethodSig) {
|
||||
if let syn::FnArg::SelfRef(ref mut r) = sig.decl.inputs[0] {
|
||||
r.lifetime = Some(parse_quote!{'p});
|
||||
} else {
|
||||
panic!("not supported")
|
||||
}
|
||||
}
|
||||
|
||||
fn fix_name(pat: &syn::Pat, arg: &syn::FnArg) -> syn::FnArg {
|
||||
match arg {
|
||||
&syn::FnArg::Captured(_, ref arg_ty) =>
|
||||
syn::FnArg::Captured(pat.clone(), arg_ty.clone()),
|
||||
_ => panic!("func.rs::296"),
|
||||
if let syn::FnArg::Captured(ref cap) = arg {
|
||||
syn::FnArg::Captured(syn::ArgCaptured {
|
||||
pat: pat.clone(),
|
||||
colon_token: cap.colon_token,
|
||||
ty: cap.ty.clone(),
|
||||
})
|
||||
} else {
|
||||
panic!("func.rs::296")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
extern crate proc_macro;
|
||||
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use args::{Argument, parse_arguments};
|
||||
use quote::{Ident, Tokens};
|
||||
use syn;
|
||||
use quote::Tokens;
|
||||
use quote::ToTokens;
|
||||
|
||||
use args::{Argument, parse_arguments};
|
||||
use utils::for_err_msg;
|
||||
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct FnArg<'a> {
|
||||
pub name: &'a syn::Ident,
|
||||
pub mode: &'a syn::BindingMode,
|
||||
pub ty: &'a syn::Ty,
|
||||
pub optional: Option<&'a syn::Ty>,
|
||||
pub by_ref: &'a Option<syn::token::Ref>,
|
||||
pub mutability: &'a Option<syn::token::Mut>,
|
||||
pub ty: &'a syn::Type,
|
||||
pub optional: Option<&'a syn::Type>,
|
||||
pub py: bool,
|
||||
pub reference: bool,
|
||||
}
|
||||
|
@ -33,16 +36,18 @@ pub struct FnSpec<'a> {
|
|||
pub tp: FnType,
|
||||
pub attrs: Vec<Argument>,
|
||||
pub args: Vec<FnArg<'a>>,
|
||||
pub output: syn::Ty,
|
||||
pub output: syn::Type,
|
||||
}
|
||||
|
||||
pub fn get_return_info(output: &syn::FunctionRetTy) -> syn::Ty {
|
||||
|
||||
pub fn get_return_info(output: &syn::ReturnType) -> syn::Type {
|
||||
match output {
|
||||
&syn::FunctionRetTy::Default => syn::Ty::Tup(vec![]),
|
||||
&syn::FunctionRetTy::Ty(ref ty) => ty.clone()
|
||||
syn::ReturnType::Default => syn::Type::Infer(parse_quote!{_}),
|
||||
syn::ReturnType::Type(_, ref ty) => *ty.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a> FnSpec<'a> {
|
||||
/// Parser function signature and function attributes
|
||||
pub fn parse(name: &'a syn::Ident,
|
||||
|
@ -55,30 +60,30 @@ impl<'a> FnSpec<'a> {
|
|||
|
||||
for input in sig.decl.inputs.iter() {
|
||||
match input {
|
||||
&syn::FnArg::SelfRef(_, _) => {
|
||||
&syn::FnArg::SelfRef(_) => {
|
||||
has_self = true;
|
||||
},
|
||||
&syn::FnArg::SelfValue(_) => {
|
||||
has_self = true;
|
||||
}
|
||||
&syn::FnArg::Captured(ref pat, ref ty) => {
|
||||
&syn::FnArg::Captured(syn::ArgCaptured {ref pat, ref ty, ..}) => {
|
||||
// skip first argument (cls)
|
||||
if (fn_type == FnType::FnClass || fn_type == FnType::FnNew) && !has_self {
|
||||
has_self = true;
|
||||
continue
|
||||
}
|
||||
|
||||
let (mode, ident) = match pat {
|
||||
&syn::Pat::Ident(ref mode, ref ident, _) =>
|
||||
(mode, ident),
|
||||
let (ident, by_ref, mutability) = match pat {
|
||||
&syn::Pat::Ident(syn::PatIdent {ref ident, ref by_ref, ref mutability, .. }) =>
|
||||
(ident, by_ref, mutability),
|
||||
_ =>
|
||||
panic!("unsupported argument: {:?}", pat),
|
||||
};
|
||||
|
||||
let py = match ty {
|
||||
&syn::Ty::Path(_, ref path) =>
|
||||
&syn::Type::Path(syn::TypePath {ref path, ..}) =>
|
||||
if let Some(segment) = path.segments.last() {
|
||||
segment.ident.as_ref() == "Python"
|
||||
segment.value().ident.as_ref() == "Python"
|
||||
} else {
|
||||
false
|
||||
},
|
||||
|
@ -89,7 +94,9 @@ impl<'a> FnSpec<'a> {
|
|||
arguments.push(
|
||||
FnArg {
|
||||
name: ident,
|
||||
mode: mode,
|
||||
by_ref,
|
||||
mutability,
|
||||
// mode: mode,
|
||||
ty: ty,
|
||||
optional: opt,
|
||||
py: py,
|
||||
|
@ -99,6 +106,8 @@ impl<'a> FnSpec<'a> {
|
|||
}
|
||||
&syn::FnArg::Ignored(_) =>
|
||||
panic!("ignored argument: {:?}", name),
|
||||
&syn::FnArg::Inferred(_) =>
|
||||
panic!("ingerred argument: {:?}", name),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,15 +170,15 @@ impl<'a> FnSpec<'a> {
|
|||
Argument::Arg(ref ident, ref opt) => {
|
||||
if ident.as_str() == name.as_ref() {
|
||||
if let &Some(ref val) = opt {
|
||||
let i = Ident::from(val.as_str());
|
||||
return Some(quote!(#i))
|
||||
let i: syn::Expr = syn::parse_str(&val).unwrap();
|
||||
return Some(i.into_tokens())
|
||||
}
|
||||
}
|
||||
},
|
||||
Argument::Kwarg(ref ident, ref opt) => {
|
||||
if ident.as_str() == name.as_ref() {
|
||||
let i = Ident::from(opt.as_str());
|
||||
return Some(quote!(#i))
|
||||
let i: syn::Expr = syn::parse_str(&opt).unwrap();
|
||||
return Some(i.into_tokens())
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
|
@ -193,25 +202,25 @@ impl<'a> FnSpec<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_ref<'a>(name: &'a syn::Ident, ty: &'a syn::Ty) -> bool {
|
||||
pub fn is_ref<'a>(name: &'a syn::Ident, ty: &'a syn::Type) -> bool {
|
||||
match ty {
|
||||
&syn::Ty::Rptr(_, _) => {
|
||||
&syn::Type::Reference(_) => {
|
||||
return true
|
||||
}
|
||||
&syn::Ty::Path(_, ref path) => {
|
||||
&syn::Type::Path(syn::TypePath {ref path, ..}) => {
|
||||
if let Some(segment) = path.segments.last() {
|
||||
match segment.ident.as_ref() {
|
||||
match segment.value().ident.as_ref() {
|
||||
"Option" => {
|
||||
match segment.parameters {
|
||||
syn::PathParameters::AngleBracketed(ref params) => {
|
||||
if params.types.len() != 1 {
|
||||
match segment.value().arguments {
|
||||
syn::PathArguments::AngleBracketed(ref params) => {
|
||||
if params.args.len() != 1 {
|
||||
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
||||
for_err_msg(name),
|
||||
for_err_msg(ty),
|
||||
for_err_msg(path));
|
||||
}
|
||||
match ¶ms.types[params.types.len()-1] {
|
||||
&syn::Ty::Rptr(_, _) => {
|
||||
match ¶ms.args[params.args.len()-1] {
|
||||
&syn::GenericArgument::Type(syn::Type::Reference(_)) => {
|
||||
return true
|
||||
},
|
||||
_ => ()
|
||||
|
@ -234,28 +243,36 @@ pub fn is_ref<'a>(name: &'a syn::Ident, ty: &'a syn::Ty) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
pub 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::Type)
|
||||
-> Option<&'a syn::Type>
|
||||
{
|
||||
match ty {
|
||||
&syn::Ty::Path(_, ref path) => {
|
||||
&syn::Type::Path(syn::TypePath {ref path, ..}) => {
|
||||
//if let &Some(ref qs) = qs {
|
||||
// panic!("explicit Self type in a 'qualified path' is not supported: {:?} - {:?}",
|
||||
// name, qs);
|
||||
//}
|
||||
|
||||
if let Some(segment) = path.segments.last() {
|
||||
match segment.ident.as_ref() {
|
||||
match segment.value().ident.as_ref() {
|
||||
"Option" => {
|
||||
match segment.parameters {
|
||||
syn::PathParameters::AngleBracketed(ref params) => {
|
||||
if params.types.len() != 1 {
|
||||
match segment.value().arguments {
|
||||
syn::PathArguments::AngleBracketed(ref params) => {
|
||||
if params.args.len() != 1 {
|
||||
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
||||
for_err_msg(name),
|
||||
for_err_msg(ty),
|
||||
for_err_msg(path));
|
||||
}
|
||||
Some(¶ms.types[0])
|
||||
|
||||
match ¶ms.args[0] {
|
||||
&syn::GenericArgument::Type(ref ty) => Some(ty),
|
||||
_ => panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
||||
for_err_msg(name),
|
||||
for_err_msg(ty),
|
||||
for_err_msg(path)),
|
||||
}
|
||||
|
||||
},
|
||||
_ => {
|
||||
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
||||
|
@ -286,8 +303,8 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<Argument>)
|
|||
let mut res: Option<FnType> = None;
|
||||
|
||||
for attr in attrs.iter() {
|
||||
match attr.value {
|
||||
syn::MetaItem::Word(ref name) => {
|
||||
match attr.interpret_meta().unwrap() {
|
||||
syn::Meta::Word(ref name) => {
|
||||
match name.as_ref() {
|
||||
"new" | "__new__" => {
|
||||
res = Some(FnType::FnNew)
|
||||
|
@ -305,7 +322,7 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<Argument>)
|
|||
res = Some(FnType::FnStatic)
|
||||
},
|
||||
"setter" | "getter" => {
|
||||
if attr.style == syn::AttrStyle::Inner {
|
||||
if let syn::AttrStyle::Inner(_) = attr.style {
|
||||
panic!("Inner style attribute is not
|
||||
supported for setter and getter");
|
||||
}
|
||||
|
@ -323,8 +340,8 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<Argument>)
|
|||
}
|
||||
}
|
||||
},
|
||||
syn::MetaItem::List(ref name, ref meta) => {
|
||||
match name.as_ref() {
|
||||
syn::Meta::List(syn::MetaList {ref ident, ref nested, ..}) => {
|
||||
match ident.as_ref() {
|
||||
"new" => {
|
||||
res = Some(FnType::FnNew)
|
||||
},
|
||||
|
@ -335,31 +352,31 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<Argument>)
|
|||
res = Some(FnType::FnCall)
|
||||
},
|
||||
"setter" | "getter" => {
|
||||
if attr.style == syn::AttrStyle::Inner {
|
||||
if let syn::AttrStyle::Inner(_) = attr.style {
|
||||
panic!("Inner style attribute is not
|
||||
supported for setter and getter");
|
||||
}
|
||||
if res != None {
|
||||
panic!("setter/getter attribute can not be used mutiple times");
|
||||
}
|
||||
if meta.len() != 1 {
|
||||
if nested.len() != 1 {
|
||||
panic!("setter/getter requires one value");
|
||||
}
|
||||
match *meta.first().unwrap() {
|
||||
syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref w)) => {
|
||||
if name.as_ref() == "setter" {
|
||||
match nested.first().unwrap().value() {
|
||||
syn::NestedMeta::Meta(syn::Meta::Word(ref w)) => {
|
||||
if ident.as_ref() == "setter" {
|
||||
res = Some(FnType::Setter(Some(w.to_string())))
|
||||
} else {
|
||||
res = Some(FnType::Getter(Some(w.to_string())))
|
||||
}
|
||||
},
|
||||
syn::NestedMetaItem::Literal(ref lit) => {
|
||||
syn::NestedMeta::Literal(ref lit) => {
|
||||
match *lit {
|
||||
syn::Lit::Str(ref s, syn::StrStyle::Cooked) => {
|
||||
if name.as_ref() == "setter" {
|
||||
res = Some(FnType::Setter(Some(s.clone())))
|
||||
syn::Lit::Str(ref s) => {
|
||||
if ident.as_ref() == "setter" {
|
||||
res = Some(FnType::Setter(Some(s.value())))
|
||||
} else {
|
||||
res = Some(FnType::Getter(Some(s.clone())))
|
||||
res = Some(FnType::Getter(Some(s.value())))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
|
@ -368,19 +385,20 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<Argument>)
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
println!("cannot parse {:?} attribute: {:?}", name, meta);
|
||||
println!("cannot parse {:?} attribute: {:?}", ident, nested);
|
||||
},
|
||||
}
|
||||
},
|
||||
"args" => {
|
||||
spec.extend(parse_arguments(meta.as_slice()))
|
||||
let args = nested.iter().cloned().collect::<Vec<_>>();
|
||||
spec.extend(parse_arguments(args.as_slice()))
|
||||
}
|
||||
_ => {
|
||||
new_attrs.push(attr.clone())
|
||||
}
|
||||
}
|
||||
},
|
||||
syn::MetaItem::NameValue(_, _) => {
|
||||
syn::Meta::NameValue(_) => {
|
||||
new_attrs.push(attr.clone())
|
||||
},
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@ use utils;
|
|||
/// Generates the function that is called by the python interpreter to initialize the native
|
||||
/// module
|
||||
pub fn py3_init(fnname: &syn::Ident, name: &String, doc: syn::Lit) -> Tokens {
|
||||
let m_name = syn::Ident::from(name.trim().as_ref());
|
||||
let cb_name = syn::Ident::from(format!("PyInit_{}", name.trim()).as_ref());
|
||||
|
||||
let m_name: syn::Ident = syn::parse_str(name.trim().as_ref()).unwrap();
|
||||
let cb_name: syn::Ident = syn::parse_str(&format!("PyInit_{}", name.trim())).unwrap();
|
||||
|
||||
quote! {
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case, unused_imports)]
|
||||
|
@ -57,8 +59,8 @@ pub fn py3_init(fnname: &syn::Ident, name: &String, doc: syn::Lit) -> Tokens {
|
|||
}
|
||||
|
||||
pub fn py2_init(fnname: &syn::Ident, name: &String, doc: syn::Lit) -> Tokens {
|
||||
let m_name = syn::Ident::from(name.trim().as_ref());
|
||||
let cb_name = syn::Ident::from(format!("init{}", name.trim()).as_ref());
|
||||
let m_name: syn::Ident = syn::parse_str(name.trim().as_ref()).unwrap();
|
||||
let cb_name: syn::Ident = syn::parse_str(&format!("PyInit_{}", name.trim())).unwrap();
|
||||
|
||||
quote! {
|
||||
#[no_mangle]
|
||||
|
@ -96,35 +98,29 @@ pub fn py2_init(fnname: &syn::Ident, name: &String, doc: syn::Lit) -> Tokens {
|
|||
|
||||
/// Finds and takes care of the #[pyfn(...)] in #[modinit(...)]
|
||||
pub fn process_functions_in_module(ast: &mut syn::Item) {
|
||||
if let syn::ItemKind::Fn(_, _, _, _, _, ref mut block) = ast.node {
|
||||
if let syn::Item::Fn(ref mut func) = ast {
|
||||
let mut stmts: Vec<syn::Stmt> = Vec::new();
|
||||
for stmt in block.stmts.iter_mut() {
|
||||
if let &mut syn::Stmt::Item(ref mut item) = stmt {
|
||||
|
||||
for stmt in func.block.stmts.iter_mut() {
|
||||
if let syn::Stmt::Item(syn::Item::Fn(ref mut func)) = stmt {
|
||||
if let Some((module_name, python_name, pyfn_attrs)) =
|
||||
extract_pyfn_attrs(&mut item.attrs)
|
||||
extract_pyfn_attrs(&mut func.attrs)
|
||||
{
|
||||
let function_to_python = add_fn_to_module(item, &python_name, pyfn_attrs);
|
||||
let function_wrapper_ident = function_wrapper_ident(&item.ident);
|
||||
let tokens = quote! {
|
||||
let function_to_python = add_fn_to_module(func, &python_name, pyfn_attrs);
|
||||
let function_wrapper_ident = function_wrapper_ident(&func.ident);
|
||||
let item: syn::ItemFn = parse_quote!{
|
||||
fn block_wrapper() {
|
||||
#function_to_python
|
||||
|
||||
#module_name.add_function(&#function_wrapper_ident);
|
||||
}
|
||||
}.to_string();
|
||||
|
||||
let item = syn::parse_item(tokens.as_str()).unwrap();
|
||||
let block = match item.node {
|
||||
syn::ItemKind::Fn(_, _, _, _, _, ref block) => block.clone(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
stmts.extend(block.stmts.into_iter());
|
||||
stmts.extend(item.block.stmts.into_iter());
|
||||
}
|
||||
};
|
||||
stmts.push(stmt.clone());
|
||||
}
|
||||
block.stmts = stmts;
|
||||
|
||||
func.block.stmts = stmts;
|
||||
} else {
|
||||
panic!("#[modinit] can only be used with fn block");
|
||||
}
|
||||
|
@ -133,33 +129,38 @@ pub fn process_functions_in_module(ast: &mut syn::Item) {
|
|||
/// Transforms a rust fn arg parsed with syn into a method::FnArg
|
||||
fn wrap_fn_argument<'a>(input: &'a syn::FnArg, name: &'a syn::Ident) -> Option<method::FnArg<'a>> {
|
||||
match input {
|
||||
&syn::FnArg::SelfRef(_, _) | &syn::FnArg::SelfValue(_) => None,
|
||||
&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),
|
||||
&syn::FnArg::SelfRef(_) | &syn::FnArg::SelfValue(_) => None,
|
||||
&syn::FnArg::Captured(ref cap) => {
|
||||
|
||||
let (mutability, by_ref, ident) = match cap.pat {
|
||||
syn::Pat::Ident(ref patid) =>
|
||||
(&patid.mutability, &patid.by_ref, &patid.ident),
|
||||
_ =>
|
||||
panic!("unsupported argument: {:?}", cap.pat),
|
||||
};
|
||||
|
||||
let py = match ty {
|
||||
&syn::Ty::Path(_, ref path) => if let Some(segment) = path.segments.last() {
|
||||
segment.ident.as_ref() == "Python"
|
||||
} else {
|
||||
false
|
||||
},
|
||||
let py = match cap.ty {
|
||||
syn::Type::Path(ref typath) => {
|
||||
typath.path.segments.last()
|
||||
.map(|seg| seg.value().ident == "Python")
|
||||
.unwrap_or(false)
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let opt = method::check_arg_ty_and_optional(&name, ty);
|
||||
let opt = method::check_arg_ty_and_optional(&name, &cap.ty);
|
||||
Some(method::FnArg {
|
||||
name: ident,
|
||||
mode,
|
||||
ty,
|
||||
mutability: mutability,
|
||||
by_ref: by_ref,
|
||||
ty: &cap.ty,
|
||||
optional: opt,
|
||||
py,
|
||||
reference: method::is_ref(&name, ty),
|
||||
reference: method::is_ref(&name, &cap.ty),
|
||||
})
|
||||
}
|
||||
&syn::FnArg::Ignored(_) => panic!("ignored argument: {:?}", name),
|
||||
&syn::FnArg::Inferred(_) => panic!("inferred argument: {:?}", name),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,67 +174,64 @@ fn extract_pyfn_attrs(
|
|||
let mut fn_attrs = Vec::new();
|
||||
|
||||
for attr in attrs.iter() {
|
||||
if let syn::MetaItem::List(ref name, ref meta) = attr.value {
|
||||
if name.as_ref() == "pyfn" {
|
||||
match attr.interpret_meta() {
|
||||
Some(syn::Meta::List(ref list)) if list.ident == "pyfn" => {
|
||||
let meta: Vec<_> = list.nested.iter().cloned().collect();
|
||||
if meta.len() >= 2 {
|
||||
// read module name
|
||||
match meta[0] {
|
||||
syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref ident)) => {
|
||||
modname = Some(ident.clone());
|
||||
}
|
||||
syn::NestedMeta::Meta(syn::Meta::Word(ref ident)) =>
|
||||
modname = Some(ident.clone()),
|
||||
_ => panic!("The first parameter of pyfn must be a MetaItem"),
|
||||
}
|
||||
// read Python fonction name
|
||||
match meta[1] {
|
||||
syn::NestedMetaItem::Literal(syn::Lit::Str(ref s, _)) => {
|
||||
fnname = Some(syn::Ident::from(s.as_str()));
|
||||
syn::NestedMeta::Literal(syn::Lit::Str(ref lits)) => {
|
||||
fnname = Some(syn::parse_str(&lits.value()).unwrap());
|
||||
}
|
||||
_ => panic!("The second parameter of pyfn must be a Literal"),
|
||||
}
|
||||
if meta.len() >= 3 {
|
||||
// Read additional arguments
|
||||
if list.nested.len() >= 3 {
|
||||
fn_attrs = args::parse_arguments(&meta[2..meta.len()]);
|
||||
}
|
||||
} else {
|
||||
panic!("can not parse 'pyfn' params {:?}", attr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
new_attrs.push(attr.clone())
|
||||
_ => new_attrs.push(attr.clone()),
|
||||
}
|
||||
}
|
||||
attrs.clear();
|
||||
attrs.extend(new_attrs);
|
||||
|
||||
*attrs = new_attrs;
|
||||
// attrs.clear();
|
||||
// attrs.extend(new_attrs);
|
||||
Some((modname?, fnname?, fn_attrs))
|
||||
}
|
||||
|
||||
/// Coordinates the naming of a the add-function-to-python-module function
|
||||
fn function_wrapper_ident(name: &syn::Ident) -> syn::Ident {
|
||||
// Make sure this ident matches the one of wrap_function
|
||||
syn::Ident::new("__pyo3_get_function_".to_string() + &name.to_string())
|
||||
syn::parse_str(&format!("__pyo3_get_function_{}", &name)).unwrap()
|
||||
}
|
||||
|
||||
/// Generates python wrapper over a function that allows adding it to a python module as a python
|
||||
/// function
|
||||
pub fn add_fn_to_module(
|
||||
item: &mut syn::Item,
|
||||
func: &mut syn::ItemFn,
|
||||
python_name: &syn::Ident,
|
||||
pyfn_attrs: Vec<args::Argument>,
|
||||
) -> Tokens {
|
||||
let name = item.ident.clone();
|
||||
|
||||
let decl = if let syn::ItemKind::Fn(ref decl, _, _, _, _, _) = item.node {
|
||||
decl.clone()
|
||||
} else {
|
||||
panic!("Expected a function")
|
||||
};
|
||||
|
||||
let mut arguments = Vec::new();
|
||||
|
||||
for input in decl.inputs.iter() {
|
||||
if let Some(fn_arg) = wrap_fn_argument(input, &name) {
|
||||
for input in func.decl.inputs.iter() {
|
||||
if let Some(fn_arg) = wrap_fn_argument(input, &func.ident) {
|
||||
arguments.push(fn_arg);
|
||||
}
|
||||
}
|
||||
|
||||
let ty = method::get_return_info(&decl.output);
|
||||
let ty = method::get_return_info(&func.decl.output);
|
||||
|
||||
let spec = method::FnSpec {
|
||||
tp: method::FnType::Fn,
|
||||
|
@ -242,12 +240,12 @@ pub fn add_fn_to_module(
|
|||
output: ty,
|
||||
};
|
||||
|
||||
let function_wrapper_ident = function_wrapper_ident(&name);
|
||||
let function_wrapper_ident = function_wrapper_ident(&func.ident);
|
||||
|
||||
let wrapper = function_c_wrapper(&name, &spec);
|
||||
let doc = utils::get_doc(&item.attrs, true);
|
||||
let wrapper = function_c_wrapper(&func.ident, &spec);
|
||||
let doc = utils::get_doc(&func.attrs, true);
|
||||
|
||||
let tokens = quote! (
|
||||
let tokens = quote! {
|
||||
fn #function_wrapper_ident(py: ::pyo3::Python) -> ::pyo3::PyObject {
|
||||
use std;
|
||||
use pyo3 as _pyo3;
|
||||
|
@ -274,7 +272,7 @@ pub fn add_fn_to_module(
|
|||
|
||||
function
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
tokens
|
||||
}
|
||||
|
|
|
@ -11,17 +11,22 @@ use method::{FnType, FnSpec, FnArg};
|
|||
use py_method::{impl_wrap_getter, impl_wrap_setter, impl_py_getter_def, impl_py_setter_def};
|
||||
|
||||
|
||||
pub fn build_py_class(ast: &mut syn::DeriveInput, attr: String) -> Tokens {
|
||||
pub fn build_py_class(
|
||||
ast: &mut syn::DeriveInput,
|
||||
attr: &Vec<syn::Expr>
|
||||
) -> Tokens {
|
||||
|
||||
let (params, flags, base) = parse_attribute(attr);
|
||||
let doc = utils::get_doc(&ast.attrs, true);
|
||||
let mut token: Option<syn::Ident> = None;
|
||||
let mut descriptors = Vec::new();
|
||||
match ast.body {
|
||||
syn::Body::Struct(syn::VariantData::Struct(ref mut fields)) => {
|
||||
for field in fields.iter_mut() {
|
||||
|
||||
if let syn::Data::Struct(ref mut struc) = ast.data {
|
||||
if let syn::Fields::Named(ref mut fields) = struc.fields {
|
||||
for field in fields.named.iter_mut() {
|
||||
if is_python_token(field) {
|
||||
token = field.ident.clone();
|
||||
break
|
||||
break;
|
||||
} else {
|
||||
let field_descs = parse_descriptors(field);
|
||||
if !field_descs.is_empty() {
|
||||
|
@ -29,11 +34,14 @@ pub fn build_py_class(ast: &mut syn::DeriveInput, attr: String) -> Tokens {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => panic!("#[class] can only be used with normal structs"),
|
||||
} else {
|
||||
panic!("#[class] can only be used with C-style structs")
|
||||
}
|
||||
} else {
|
||||
panic!("#[class] can only be used with structs")
|
||||
}
|
||||
|
||||
let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_CLS_{}", ast.ident));
|
||||
let dummy_const = syn::Ident::from(format!("_IMPL_PYO3_CLS_{}", ast.ident));
|
||||
let tokens = impl_class(&ast.ident, &base, token, doc, params, flags, descriptors);
|
||||
|
||||
quote! {
|
||||
|
@ -52,37 +60,29 @@ fn parse_descriptors(item: &mut syn::Field) -> Vec<FnType> {
|
|||
let mut descs = Vec::new();
|
||||
let mut new_attrs = Vec::new();
|
||||
for attr in item.attrs.iter() {
|
||||
match attr.value {
|
||||
syn::MetaItem::List(ref name, ref metas) => {
|
||||
match name.as_ref() {
|
||||
"prop" => {
|
||||
for meta in metas.iter() {
|
||||
match *meta {
|
||||
syn::NestedMetaItem::MetaItem(ref metaitem) => {
|
||||
match metaitem.name() {
|
||||
"get" => {
|
||||
descs.push(FnType::Getter(None));
|
||||
}
|
||||
"set" => {
|
||||
descs.push(FnType::Setter(None));
|
||||
}
|
||||
_ => {
|
||||
panic!("Only getter and setter supported");
|
||||
}
|
||||
}
|
||||
if let Some(syn::Meta::List(ref list)) = attr.interpret_meta() {
|
||||
match list.ident.as_ref() {
|
||||
"prop" => {
|
||||
for meta in list.nested.iter() {
|
||||
if let &syn::NestedMeta::Meta(ref metaitem) = meta {
|
||||
match metaitem.name().as_ref() {
|
||||
"get" => {
|
||||
descs.push(FnType::Getter(None));
|
||||
}
|
||||
"set" => {
|
||||
descs.push(FnType::Setter(None));
|
||||
}
|
||||
_ => {
|
||||
panic!("Only getter and setter supported");
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
new_attrs.push(attr.clone());
|
||||
}
|
||||
}
|
||||
_ => new_attrs.push(attr.clone()),
|
||||
}
|
||||
_ => {
|
||||
new_attrs.push(attr.clone());
|
||||
}
|
||||
} else {
|
||||
new_attrs.push(attr.clone());
|
||||
}
|
||||
}
|
||||
item.attrs.clear();
|
||||
|
@ -90,14 +90,19 @@ fn parse_descriptors(item: &mut syn::Field) -> Vec<FnType> {
|
|||
descs
|
||||
}
|
||||
|
||||
fn impl_class(cls: &syn::Ident, base: &syn::Ident,
|
||||
token: Option<syn::Ident>, doc: syn::Lit,
|
||||
params: HashMap<&'static str, syn::Ident>,
|
||||
flags: Vec<syn::Ident>,
|
||||
descriptors: Vec<(syn::Field, Vec<FnType>)>) -> Tokens {
|
||||
fn impl_class(
|
||||
cls: &syn::Ident,
|
||||
base: &syn::TypePath,
|
||||
token: Option<syn::Ident>,
|
||||
doc: syn::Lit,
|
||||
params: HashMap<&'static str, syn::Expr>,
|
||||
flags: Vec<syn::Expr>,
|
||||
descriptors: Vec<(syn::Field, Vec<FnType>)>
|
||||
) -> Tokens {
|
||||
|
||||
let cls_name = match params.get("name") {
|
||||
Some(name) => quote! { #name }.as_str().to_string(),
|
||||
None => quote! { #cls }.as_str().to_string()
|
||||
Some(name) => quote! { #name }.to_string(),
|
||||
None => quote! { #cls }.to_string()
|
||||
};
|
||||
|
||||
let extra = if let Some(token) = token {
|
||||
|
@ -197,7 +202,7 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
|
|||
};
|
||||
|
||||
let extra = if !descriptors.is_empty() {
|
||||
let ty = syn::parse::ty(cls.as_ref()).expect("no name");
|
||||
let ty = syn::parse_str(cls.as_ref()).expect("no name");
|
||||
let desc_impls = impl_descriptors(&ty, descriptors);
|
||||
Some(quote! {
|
||||
#desc_impls
|
||||
|
@ -211,21 +216,23 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
|
|||
let mut has_weakref = false;
|
||||
let mut has_dict = false;
|
||||
for f in flags.iter() {
|
||||
if *f == syn::Ident::from("_pyo3::typeob::PY_TYPE_FLAG_WEAKREF") {
|
||||
has_weakref = true;
|
||||
} else if *f == syn::Ident::from("_pyo3::typeob::PY_TYPE_FLAG_DICT") {
|
||||
has_dict = true;
|
||||
if let syn::Expr::Path(ref epath) = f {
|
||||
if epath.path == parse_quote!{_pyo3::typeob::PY_TYPE_FLAG_WEAKREF} {
|
||||
has_weakref = true;
|
||||
} else if epath.path == parse_quote!{_pyo3::typeob::PY_TYPE_FLAG_DICT} {
|
||||
has_dict = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
let weakref = if has_weakref {
|
||||
syn::Ident::from("std::mem::size_of::<*const _pyo3::ffi::PyObject>()")
|
||||
quote!{std::mem::size_of::<*const _pyo3::ffi::PyObject>()}
|
||||
} else {
|
||||
syn::Ident::from("0")
|
||||
quote!{0}
|
||||
};
|
||||
let dict = if has_dict {
|
||||
syn::Ident::from("std::mem::size_of::<*const _pyo3::ffi::PyObject>()")
|
||||
quote!{std::mem::size_of::<*const _pyo3::ffi::PyObject>()}
|
||||
} else {
|
||||
syn::Ident::from("0")
|
||||
quote!{0}
|
||||
};
|
||||
|
||||
quote! {
|
||||
|
@ -239,7 +246,7 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
|
|||
|
||||
const SIZE: usize = {
|
||||
Self::OFFSET as usize +
|
||||
std::mem::size_of::<#cls>() + #weakref + #dict
|
||||
std::mem::size_of::<#cls>() + #weakref + #dict
|
||||
};
|
||||
const OFFSET: isize = {
|
||||
// round base_size up to next multiple of align
|
||||
|
@ -268,13 +275,11 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
|
|||
let gil = _pyo3::Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
|
||||
let error_message = "An error occurred while initializing class ".to_string() +
|
||||
<#cls as _pyo3::typeob::PyTypeInfo>::NAME.as_ref();
|
||||
|
||||
// automatically initialize the class on-demand
|
||||
_pyo3::typeob::initialize_type::<#cls>(py, None)
|
||||
.map_err(|e| e.print(py))
|
||||
.expect(&error_message);
|
||||
.expect(format!("An error occurred while initializing class {}",
|
||||
<#cls as _pyo3::typeob::PyTypeInfo>::NAME).as_ref());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -284,7 +289,11 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
|
|||
}
|
||||
}
|
||||
|
||||
fn impl_descriptors(cls: &syn::Ty, descriptors: Vec<(syn::Field, Vec<FnType>)>) -> Tokens {
|
||||
fn impl_descriptors(
|
||||
cls: &syn::Type,
|
||||
descriptors: Vec<(syn::Field, Vec<FnType>)>
|
||||
) -> Tokens {
|
||||
|
||||
let methods: Vec<Tokens> = descriptors.iter().flat_map(|&(ref field, ref fns)| {
|
||||
fns.iter().map(|desc| {
|
||||
let name = field.ident.clone().unwrap();
|
||||
|
@ -318,30 +327,37 @@ fn impl_descriptors(cls: &syn::Ty, descriptors: Vec<(syn::Field, Vec<FnType>)>)
|
|||
let py_methods: Vec<Tokens> = descriptors.iter().flat_map(|&(ref field, ref fns)| {
|
||||
fns.iter().map(|desc| {
|
||||
let name = field.ident.clone().unwrap();
|
||||
|
||||
// FIXME better doc?
|
||||
let doc = syn::Lit::from(name.as_ref());
|
||||
let doc: syn::Lit = syn::parse_str(&format!("\"{}\"", name)).unwrap();
|
||||
|
||||
let field_ty = &field.ty;
|
||||
match *desc {
|
||||
FnType::Getter(ref getter) => {
|
||||
impl_py_getter_def(&name, doc, getter, &impl_wrap_getter(&Box::new(cls.clone()), &name))
|
||||
}
|
||||
FnType::Setter(ref setter) => {
|
||||
let mode = syn::BindingMode::ByValue(syn::Mutability::Immutable);
|
||||
let setter_name = syn::Ident::from(format!("set_{}", name));
|
||||
let spec = FnSpec {
|
||||
tp: FnType::Setter(None),
|
||||
attrs: Vec::new(),
|
||||
args: vec![FnArg {
|
||||
name: &name,
|
||||
mode: &mode,
|
||||
mutability: &None,
|
||||
by_ref: &None,
|
||||
ty: field_ty,
|
||||
optional: None,
|
||||
py: true,
|
||||
reference: false
|
||||
}],
|
||||
output: syn::parse::ty("PyResult<()>").expect("error parse PyResult<()>"),
|
||||
output: syn::parse_str("PyResult<()>").unwrap()
|
||||
};
|
||||
impl_py_setter_def(&name, doc, setter, &impl_wrap_setter(&Box::new(cls.clone()), &setter_name, &spec))
|
||||
impl_py_setter_def(
|
||||
&name,
|
||||
doc,
|
||||
setter,
|
||||
&impl_wrap_setter(&Box::new(cls.clone()), &setter_name, &spec)
|
||||
)
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
|
@ -362,13 +378,13 @@ fn impl_descriptors(cls: &syn::Ty, descriptors: Vec<(syn::Field, Vec<FnType>)>)
|
|||
};
|
||||
|
||||
let n = match cls {
|
||||
&syn::Ty::Path(_, ref p) => {
|
||||
p.segments.last().as_ref().unwrap().ident.as_ref()
|
||||
&syn::Type::Path(ref typath) => {
|
||||
typath.path.segments.last().as_ref().unwrap().value().ident.as_ref()
|
||||
}
|
||||
_ => "CLS_METHODS"
|
||||
};
|
||||
|
||||
let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_DESCRIPTORS_{}", n));
|
||||
let dummy_const = syn::Ident::from(format!("_IMPL_PYO3_DESCRIPTORS_{}", n));
|
||||
quote! {
|
||||
#[feature(specialization)]
|
||||
#[allow(non_upper_case_globals, unused_attributes,
|
||||
|
@ -383,9 +399,9 @@ fn impl_descriptors(cls: &syn::Ty, descriptors: Vec<(syn::Field, Vec<FnType>)>)
|
|||
|
||||
fn is_python_token(field: &syn::Field) -> bool {
|
||||
match field.ty {
|
||||
syn::Ty::Path(_, ref path) => {
|
||||
if let Some(segment) = path.segments.last() {
|
||||
return segment.ident.as_ref() == "PyToken"
|
||||
syn::Type::Path(ref typath) => {
|
||||
if let Some(segment) = typath.path.segments.last() {
|
||||
return segment.value().ident.as_ref() == "PyToken"
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
|
@ -393,137 +409,95 @@ fn is_python_token(field: &syn::Field) -> bool {
|
|||
return false
|
||||
}
|
||||
|
||||
fn parse_attribute(mut attr: String) -> (HashMap<&'static str, syn::Ident>,
|
||||
Vec<syn::Ident>, syn::Ident) {
|
||||
fn parse_attribute(
|
||||
args: &Vec<syn::Expr>,
|
||||
) -> (
|
||||
HashMap<&'static str, syn::Expr>,
|
||||
Vec<syn::Expr>,
|
||||
syn::TypePath
|
||||
) {
|
||||
|
||||
let mut params = HashMap::new();
|
||||
let mut flags = vec![syn::Ident::from("0")];
|
||||
let mut base = syn::Ident::from("_pyo3::PyObjectRef");
|
||||
let mut flags = vec![syn::Expr::Lit(parse_quote!{0})];
|
||||
let mut base: syn::TypePath = parse_quote!{_pyo3::PyObjectRef};
|
||||
|
||||
// https://github.com/rust-lang/rust/pull/50120 removed the parantheses from
|
||||
// the attr TokenStream, so we need to re-add them manually
|
||||
// Old nightly (like 2018-04-05): ( name=CustomName )
|
||||
// New nightly (like 2018-04-28): name=CustomName
|
||||
|
||||
if attr.len() > 0 && !attr.starts_with("(") {
|
||||
attr = format!("({})", attr);
|
||||
}
|
||||
for expr in args.iter() {
|
||||
match expr {
|
||||
|
||||
if let Ok(tts) = syn::parse_token_trees(&attr) {
|
||||
let mut elem = Vec::new();
|
||||
let mut elems = Vec::new();
|
||||
|
||||
for tt in tts.iter() {
|
||||
match tt {
|
||||
&syn::TokenTree::Token(ref token) => {
|
||||
println!("Wrong format: Expected delimited, found token: {:?} {:?}", attr.to_string(), token);
|
||||
}
|
||||
&syn::TokenTree::Delimited(ref delimited) => {
|
||||
for tt in delimited.tts.iter() {
|
||||
match tt {
|
||||
&syn::TokenTree::Token(syn::Token::Comma) => {
|
||||
let el = std::mem::replace(&mut elem, Vec::new());
|
||||
elems.push(el);
|
||||
},
|
||||
_ => elem.push(tt.clone())
|
||||
}
|
||||
// Match a single flag
|
||||
syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => {
|
||||
match exp.path.segments.first().unwrap().value().ident.as_ref() {
|
||||
"gc" => {
|
||||
flags.push(syn::Expr::Path(parse_quote!{_pyo3::typeob::PY_TYPE_FLAG_GC}));
|
||||
}
|
||||
"weakref" => {
|
||||
flags.push(syn::Expr::Path(parse_quote!{_pyo3::typeob::PY_TYPE_FLAG_WEAKREF}));
|
||||
}
|
||||
"subclass" => {
|
||||
flags.push(syn::Expr::Path(parse_quote!{_pyo3::typeob::PY_TYPE_FLAG_BASETYPE}));
|
||||
}
|
||||
"dict" => {
|
||||
flags.push(syn::Expr::Path(parse_quote!{_pyo3::typeob::PY_TYPE_FLAG_DICT}));
|
||||
}
|
||||
param => {
|
||||
println!("Unsupported parameter: {}", param);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !elem.is_empty() {
|
||||
elems.push(elem);
|
||||
}
|
||||
|
||||
for elem in elems {
|
||||
let key = match elem[0] {
|
||||
syn::TokenTree::Token(syn::Token::Ident(ref ident)) => {
|
||||
ident.as_ref().to_owned().to_lowercase()
|
||||
},
|
||||
_ => {
|
||||
println!("Wrong format: Expected Token: {:?}", attr.to_string());
|
||||
continue
|
||||
}
|
||||
};
|
||||
// Match a key/value flag
|
||||
syn::Expr::Assign(ref ass) => {
|
||||
|
||||
if elem.len() == 1 {
|
||||
match key.as_ref() {
|
||||
"gc" => {
|
||||
flags.push(syn::Ident::from("_pyo3::typeob::PY_TYPE_FLAG_GC"));
|
||||
continue
|
||||
let key = match *ass.left {
|
||||
syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => {
|
||||
exp.path.segments.first().unwrap().value().ident.as_ref()
|
||||
}
|
||||
"weakref" => {
|
||||
flags.push(syn::Ident::from("_pyo3::typeob::PY_TYPE_FLAG_WEAKREF"));
|
||||
continue
|
||||
}
|
||||
"subclass" => {
|
||||
flags.push(syn::Ident::from("_pyo3::typeob::PY_TYPE_FLAG_BASETYPE"));
|
||||
continue
|
||||
}
|
||||
"dict" => {
|
||||
flags.push(syn::Ident::from("_pyo3::typeob::PY_TYPE_FLAG_DICT"));
|
||||
continue
|
||||
_ => panic!("could not parse argument: {:?}", ass)
|
||||
};
|
||||
|
||||
match key {
|
||||
"freelist" => {
|
||||
// TODO: check if int literal
|
||||
params.insert("freelist", *ass.right.clone());
|
||||
},
|
||||
"name" => {
|
||||
match *ass.right {
|
||||
syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => {
|
||||
params.insert("name", exp.clone().into());
|
||||
},
|
||||
_ => println!("Wrong 'name' format: {:?}", *ass.right),
|
||||
}
|
||||
},
|
||||
"base" => {
|
||||
match *ass.right {
|
||||
syn::Expr::Path(ref exp) => {
|
||||
base = syn::TypePath{
|
||||
path: exp.path.clone(),
|
||||
qself: None,
|
||||
};
|
||||
},
|
||||
_ => println!("Wrong 'base' format: {:?}", *ass.right),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("Unsupported parameter: {:?}", key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if elem.len() < 3 {
|
||||
println!("Wrong format: Less than three elements{:?}", elem);
|
||||
continue
|
||||
}
|
||||
|
||||
match elem[1] {
|
||||
syn::TokenTree::Token(syn::Token::Eq) => (),
|
||||
_ => {
|
||||
println!("Wrong format: Expected a Token as fist element: {:?}", attr.to_string());
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
match key.as_ref() {
|
||||
"freelist" => {
|
||||
if elem.len() != 3 {
|
||||
println!("Wrong 'freelist' format: {:?}", elem);
|
||||
} else {
|
||||
match elem[2] {
|
||||
syn::TokenTree::Token(
|
||||
syn::Token::Literal(
|
||||
syn::Lit::Int(val, _))) => {
|
||||
params.insert("freelist", syn::Ident::from(val.to_string()));
|
||||
}
|
||||
_ => println!("Wrong 'freelist' format: {:?}", elem)
|
||||
}
|
||||
}
|
||||
},
|
||||
"name" => {
|
||||
if elem.len() != 3 {
|
||||
println!("Wrong 'name' format: {:?}", elem);
|
||||
} else {
|
||||
match elem[2] {
|
||||
syn::TokenTree::Token(syn::Token::Ident(ref ident)) => {
|
||||
params.insert("name", ident.clone());
|
||||
},
|
||||
_ => println!("Wrong 'name' format: {:?}", elem)
|
||||
}
|
||||
}
|
||||
},
|
||||
"base" => {
|
||||
let mut m = String::new();
|
||||
for el in elem[2..elem.len()].iter() {
|
||||
let mut t = Tokens::new();
|
||||
el.to_tokens(&mut t);
|
||||
m += t.as_str().trim();
|
||||
}
|
||||
base = syn::Ident::from(m.as_str());
|
||||
},
|
||||
_ => {
|
||||
println!("Unsupported parameter: {:?}", key);
|
||||
}
|
||||
}
|
||||
_ => panic!("could not parse arguments"),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
(params, flags, base)
|
||||
}
|
||||
|
|
|
@ -7,29 +7,25 @@ use py_method;
|
|||
|
||||
|
||||
pub fn build_py_methods(ast: &mut syn::Item) -> Tokens {
|
||||
match ast.node {
|
||||
syn::ItemKind::Impl(_, _, _, ref path, ref ty, ref mut impl_items) => {
|
||||
if let &Some(_) = path {
|
||||
panic!("#[methods] can not be used only with trait impl block");
|
||||
} else {
|
||||
impl_methods(ty, impl_items)
|
||||
}
|
||||
},
|
||||
_ => panic!("#[methods] can only be used with Impl blocks"),
|
||||
if let syn::Item::Impl(ref mut iimpl) = ast {
|
||||
if iimpl.trait_.is_some() {
|
||||
panic!("#[methods] can not be used only with trait impl block");
|
||||
} else {
|
||||
impl_methods(&iimpl.self_ty, &mut iimpl.items)
|
||||
}
|
||||
} else {
|
||||
panic!("#[methods] can only be used with Impl blocks")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn impl_methods(ty: &Box<syn::Ty>, impls: &mut Vec<syn::ImplItem>) -> Tokens {
|
||||
pub fn impl_methods(ty: &Box<syn::Type>, impls: &mut Vec<syn::ImplItem>) -> Tokens {
|
||||
|
||||
// get method names in impl block
|
||||
let mut methods = Vec::new();
|
||||
for iimpl in impls.iter_mut() {
|
||||
match iimpl.node {
|
||||
syn::ImplItemKind::Method(ref mut sig, _) => {
|
||||
methods.push(py_method::gen_py_method(
|
||||
ty, &iimpl.ident, sig, &mut iimpl.attrs));
|
||||
},
|
||||
_ => (),
|
||||
if let syn::ImplItem::Method(ref mut meth) = iimpl {
|
||||
let name = meth.sig.ident.clone();
|
||||
methods.push(py_method::gen_py_method(ty, &name, &mut meth.sig, &mut meth.attrs));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,14 +40,13 @@ pub fn impl_methods(ty: &Box<syn::Ty>, impls: &mut Vec<syn::ImplItem>) -> Tokens
|
|||
}
|
||||
};
|
||||
|
||||
let n = match ty.as_ref() {
|
||||
&syn::Ty::Path(_, ref p) => {
|
||||
p.segments.last().as_ref().unwrap().ident.as_ref()
|
||||
}
|
||||
_ => "CLS_METHODS"
|
||||
let n = if let &syn::Type::Path(ref typath) = ty.as_ref() {
|
||||
typath.path.segments.last().as_ref().unwrap().value().ident.as_ref()
|
||||
} else {
|
||||
"CLS_METHODS"
|
||||
};
|
||||
|
||||
let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_METHODS_{}", n));
|
||||
let dummy_const = syn::Ident::from(format!("_IMPL_PYO3_METHODS_{}", n));
|
||||
quote! {
|
||||
#[feature(specialization)]
|
||||
#[allow(non_upper_case_globals, unused_attributes,
|
||||
|
|
|
@ -7,9 +7,12 @@ use method::{FnArg, FnSpec, FnType};
|
|||
use utils;
|
||||
|
||||
|
||||
pub fn gen_py_method<'a>(cls: &Box<syn::Ty>, name: &syn::Ident,
|
||||
sig: &mut syn::MethodSig, meth_attrs: &mut Vec<syn::Attribute>) -> Tokens
|
||||
{
|
||||
pub fn gen_py_method<'a>(
|
||||
cls: &Box<syn::Type>,
|
||||
name: &syn::Ident,
|
||||
sig: &mut syn::MethodSig,
|
||||
meth_attrs: &mut Vec<syn::Attribute>
|
||||
) -> Tokens {
|
||||
check_generic(name, sig);
|
||||
|
||||
let doc = utils::get_doc(&meth_attrs, true);
|
||||
|
@ -37,7 +40,7 @@ pub fn gen_py_method<'a>(cls: &Box<syn::Ty>, name: &syn::Ident,
|
|||
|
||||
|
||||
fn check_generic(name: &syn::Ident, sig: &syn::MethodSig) {
|
||||
if !sig.generics.ty_params.is_empty() {
|
||||
if !sig.decl.generics.params.is_empty() {
|
||||
panic!("python method can not be generic: {:?}", name);
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +57,7 @@ pub fn body_to_result(body: &Tokens, spec: &FnSpec) -> Tokens {
|
|||
}
|
||||
|
||||
/// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub fn impl_wrap(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec, noargs: bool) -> Tokens {
|
||||
pub fn impl_wrap(cls: &Box<syn::Type>, name: &syn::Ident, spec: &FnSpec, noargs: bool) -> Tokens {
|
||||
let body = impl_call(cls, name, &spec);
|
||||
|
||||
if spec.args.is_empty() && noargs {
|
||||
|
@ -103,7 +106,7 @@ pub fn impl_wrap(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec, noargs: b
|
|||
}
|
||||
|
||||
/// Generate function wrapper for protocol method (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub fn impl_proto_wrap(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
pub fn impl_proto_wrap(cls: &Box<syn::Type>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
let cb = impl_call(cls, name, &spec);
|
||||
let body = impl_arg_params(&spec, cb);
|
||||
|
||||
|
@ -131,7 +134,7 @@ pub fn impl_proto_wrap(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) ->
|
|||
}
|
||||
|
||||
/// Generate class method wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub fn impl_wrap_new(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
pub fn impl_wrap_new(cls: &Box<syn::Type>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
let names: Vec<syn::Ident> = spec.args.iter().enumerate().map(
|
||||
|item| if item.1.py {syn::Ident::from("_py")} else {
|
||||
syn::Ident::from(format!("arg{}", item.0))}).collect();
|
||||
|
@ -180,7 +183,7 @@ pub fn impl_wrap_new(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> To
|
|||
}
|
||||
|
||||
/// Generate function wrapper for ffi::initproc
|
||||
fn impl_wrap_init(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
fn impl_wrap_init(cls: &Box<syn::Type>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
let cb = impl_call(cls, name, &spec);
|
||||
let output = &spec.output;
|
||||
if quote! {#output} != quote! {PyResult<()>} || quote! {#output} != quote! {()}{
|
||||
|
@ -217,7 +220,7 @@ fn impl_wrap_init(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Token
|
|||
}
|
||||
|
||||
/// Generate class method wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub fn impl_wrap_class(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
pub fn impl_wrap_class(cls: &Box<syn::Type>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
let names: Vec<syn::Ident> = spec.args.iter().enumerate().map(
|
||||
|item| if item.1.py {syn::Ident::from("_py")} else {
|
||||
syn::Ident::from(format!("arg{}", item.0))}).collect();
|
||||
|
@ -250,7 +253,7 @@ pub fn impl_wrap_class(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) ->
|
|||
}
|
||||
|
||||
/// Generate static method wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub fn impl_wrap_static(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
pub fn impl_wrap_static(cls: &Box<syn::Type>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
let names: Vec<syn::Ident> = spec.args.iter().enumerate().map(
|
||||
|item| if item.1.py {syn::Ident::from("_py")} else {
|
||||
syn::Ident::from(format!("arg{}", item.0))}).collect();
|
||||
|
@ -282,7 +285,7 @@ pub fn impl_wrap_static(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) ->
|
|||
}
|
||||
|
||||
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub(crate) fn impl_wrap_getter(cls: &Box<syn::Ty>, name: &syn::Ident) -> Tokens {
|
||||
pub(crate) fn impl_wrap_getter(cls: &Box<syn::Type>, name: &syn::Ident) -> Tokens {
|
||||
quote! {
|
||||
unsafe extern "C" fn __wrap(
|
||||
_slf: *mut _pyo3::ffi::PyObject, _: *mut _pyo3::c_void) -> *mut _pyo3::ffi::PyObject
|
||||
|
@ -308,7 +311,7 @@ pub(crate) fn impl_wrap_getter(cls: &Box<syn::Ty>, name: &syn::Ident) -> Tokens
|
|||
}
|
||||
|
||||
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
pub(crate) fn impl_wrap_setter(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
pub(crate) fn impl_wrap_setter(cls: &Box<syn::Type>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
if spec.args.len() < 1 {
|
||||
println!("Not enough arguments for setter {}::{}", quote!{#cls}, name);
|
||||
}
|
||||
|
@ -342,7 +345,7 @@ pub(crate) fn impl_wrap_setter(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnS
|
|||
}
|
||||
|
||||
|
||||
fn impl_call(_cls: &Box<syn::Ty>, fname: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
fn impl_call(_cls: &Box<syn::Type>, fname: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||
let names: Vec<syn::Ident> = spec.args.iter().enumerate().map(
|
||||
|item| if item.1.py {
|
||||
syn::Ident::from("_py")
|
||||
|
|
|
@ -10,31 +10,33 @@ use func::impl_method_proto;
|
|||
|
||||
|
||||
pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
|
||||
match ast.node {
|
||||
syn::ItemKind::Impl(_, _, ref mut gen, ref mut path, ref ty, ref mut impl_items) => {
|
||||
if let &mut Some(ref mut path) = path {
|
||||
match ast {
|
||||
syn::Item::Impl(ref mut expr) => {
|
||||
if let Some((_, ref mut path, _)) = expr.trait_ {
|
||||
let tokens = if let Some(ref mut segment) = path.segments.last() {
|
||||
match segment.ident.as_ref() {
|
||||
let ty = &expr.self_ty;
|
||||
let items = &mut expr.items;
|
||||
match segment.value().ident.as_ref() {
|
||||
"PyObjectProtocol" =>
|
||||
impl_proto_impl(ty, impl_items, &defs::OBJECT),
|
||||
impl_proto_impl(ty, items, &defs::OBJECT),
|
||||
"PyAsyncProtocol" =>
|
||||
impl_proto_impl(ty, impl_items, &defs::ASYNC),
|
||||
impl_proto_impl(ty, items, &defs::ASYNC),
|
||||
"PyMappingProtocol" =>
|
||||
impl_proto_impl(ty, impl_items, &defs::MAPPING),
|
||||
impl_proto_impl(ty, items, &defs::MAPPING),
|
||||
"PyIterProtocol" =>
|
||||
impl_proto_impl(ty, impl_items, &defs::ITER),
|
||||
impl_proto_impl(ty, items, &defs::ITER),
|
||||
"PyContextProtocol" =>
|
||||
impl_proto_impl(ty, impl_items, &defs::CONTEXT),
|
||||
impl_proto_impl(ty, items, &defs::CONTEXT),
|
||||
"PySequenceProtocol" =>
|
||||
impl_proto_impl(ty, impl_items, &defs::SEQ),
|
||||
impl_proto_impl(ty, items, &defs::SEQ),
|
||||
"PyNumberProtocol" =>
|
||||
impl_proto_impl(ty, impl_items, &defs::NUM),
|
||||
impl_proto_impl(ty, items, &defs::NUM),
|
||||
"PyDescrProtocol" =>
|
||||
impl_proto_impl(ty, impl_items, &defs::DESCR),
|
||||
impl_proto_impl(ty, items, &defs::DESCR),
|
||||
"PyBufferProtocol" =>
|
||||
impl_proto_impl(ty, impl_items, &defs::BUFFER),
|
||||
impl_proto_impl(ty, items, &defs::BUFFER),
|
||||
"PyGCProtocol" =>
|
||||
impl_proto_impl(ty, impl_items, &defs::GC),
|
||||
impl_proto_impl(ty, items, &defs::GC),
|
||||
_ => {
|
||||
warn!("#[proto] can not be used with this block");
|
||||
return Tokens::new()
|
||||
|
@ -45,18 +47,10 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
|
|||
};
|
||||
|
||||
// attach lifetime
|
||||
gen.lifetimes = vec![syn::LifetimeDef {
|
||||
attrs: vec![], bounds: vec![],
|
||||
lifetime: syn::Lifetime { ident: syn::Ident::from("\'p") },
|
||||
}];
|
||||
|
||||
let seg = path.segments.pop().unwrap();
|
||||
path.segments.push(syn::PathSegment{
|
||||
ident: seg.ident.clone(),
|
||||
parameters: syn::PathParameters::AngleBracketed(
|
||||
syn::AngleBracketedParameterData {
|
||||
lifetimes: vec![syn::Lifetime { ident: syn::Ident::from("\'p") }],
|
||||
types: vec![], bindings: vec![] })});
|
||||
let mut seg = path.segments.pop().unwrap().into_value();
|
||||
seg.arguments = syn::PathArguments::AngleBracketed(parse_quote!{<'p>});
|
||||
path.segments.push(seg);
|
||||
expr.generics.params = parse_quote!{'p};
|
||||
|
||||
tokens
|
||||
} else {
|
||||
|
@ -67,27 +61,31 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
|
|||
}
|
||||
}
|
||||
|
||||
fn impl_proto_impl(ty: &Box<syn::Ty>,
|
||||
impls: &mut Vec<syn::ImplItem>, proto: &defs::Proto) -> Tokens
|
||||
{
|
||||
fn impl_proto_impl(
|
||||
ty: &Box<syn::Type>,
|
||||
impls: &mut Vec<syn::ImplItem>,
|
||||
proto: &defs::Proto
|
||||
) -> Tokens {
|
||||
let mut tokens = Tokens::new();
|
||||
let mut py_methods = Vec::new();
|
||||
|
||||
for iimpl in impls.iter_mut() {
|
||||
match iimpl.node {
|
||||
syn::ImplItemKind::Method(ref mut sig, _) => {
|
||||
match iimpl {
|
||||
syn::ImplItem::Method(ref mut met) => {
|
||||
for m in proto.methods {
|
||||
if m.eq(iimpl.ident.as_ref()) {
|
||||
impl_method_proto(ty, sig, m).to_tokens(&mut tokens);
|
||||
if m.eq(met.sig.ident.as_ref()) {
|
||||
impl_method_proto(ty, &mut met.sig, m).to_tokens(&mut tokens);
|
||||
}
|
||||
}
|
||||
for m in proto.py_methods {
|
||||
if m.name == iimpl.ident.as_ref() {
|
||||
let name = syn::Ident::from(m.name);
|
||||
let proto = syn::Ident::from(m.proto);
|
||||
let ident = met.sig.ident.clone();
|
||||
if m.name == ident.as_ref() {
|
||||
|
||||
let fn_spec = FnSpec::parse(&iimpl.ident, sig, &mut iimpl.attrs);
|
||||
let meth = py_method::impl_proto_wrap(ty, &iimpl.ident, &fn_spec);
|
||||
let name: syn::Ident = syn::parse_str(m.name).unwrap();
|
||||
let proto: syn::Path = syn::parse_str(m.proto).unwrap();
|
||||
|
||||
let fn_spec = FnSpec::parse(&ident, &mut met.sig, &mut met.attrs);
|
||||
let meth = py_method::impl_proto_wrap(ty, &ident, &fn_spec);
|
||||
|
||||
py_methods.push(
|
||||
quote! {
|
||||
|
@ -115,14 +113,13 @@ fn impl_proto_impl(ty: &Box<syn::Ty>,
|
|||
|
||||
// unique mod name
|
||||
let p = proto.name;
|
||||
let n = match ty.as_ref() {
|
||||
&syn::Ty::Path(_, ref p) => {
|
||||
p.segments.last().as_ref().unwrap().ident.as_ref()
|
||||
}
|
||||
_ => "PROTO_METHODS"
|
||||
let n = if let syn::Type::Path(ref typath) = ty.as_ref() {
|
||||
typath.path.segments.last().as_ref().unwrap().value().ident.as_ref()
|
||||
} else {
|
||||
"PROTO_METHODS"
|
||||
};
|
||||
|
||||
let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_{}_{}", n, p));
|
||||
let dummy_const: syn::Path = syn::parse_str(&format!("_IMPL_PYO3_{}_{}", n, p)).unwrap();
|
||||
quote! {
|
||||
#[feature(specialization)]
|
||||
#[allow(non_upper_case_globals, unused_attributes,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
use syn;
|
||||
use syn::spanned::Spanned;
|
||||
use quote::{Tokens, ToTokens};
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
|
@ -26,35 +27,37 @@ pub fn for_err_msg(i: &ToTokens) -> String {
|
|||
let mut tokens = Tokens::new();
|
||||
|
||||
i.to_tokens(&mut tokens);
|
||||
tokens.as_str().to_string()
|
||||
format!("{:?}", tokens).to_string()
|
||||
}
|
||||
|
||||
|
||||
// FIXME(althonos): not sure the docstring formatting is on par here.
|
||||
pub fn get_doc(attrs: &Vec<syn::Attribute>, null_terminated: bool) -> syn::Lit {
|
||||
|
||||
let mut doc = Vec::new();
|
||||
let mut span = None;
|
||||
|
||||
for attr in attrs.iter() {
|
||||
match attr.value {
|
||||
syn::MetaItem::NameValue(ref ident, ref lit) => {
|
||||
if ident.as_ref() == "doc" {
|
||||
let s = quote!{ #lit }.to_string();
|
||||
let mut s = s[1..s.len()-1].to_string();
|
||||
if s.starts_with("/// ") {
|
||||
// Remove leading whitespace and ///
|
||||
s = s[4..].to_string();
|
||||
} else {
|
||||
// Remove only ///
|
||||
s = s[3..].to_string();
|
||||
}
|
||||
doc.push(s)
|
||||
if let Some(syn::Meta::NameValue(ref metanv)) = attr.interpret_meta() {
|
||||
if metanv.ident == "doc" {
|
||||
span = Some(metanv.span());
|
||||
if let syn::Lit::Str(ref litstr) = metanv.lit {
|
||||
let d = litstr.value();
|
||||
doc.push(if d.starts_with(" ") { d[1..d.len()].to_string() } else {d});
|
||||
} else {
|
||||
panic!("could not parse doc");
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
let doc = doc.join("\n");
|
||||
if null_terminated {
|
||||
syn::Lit::Str(format!("{}\0", doc), syn::StrStyle::Cooked)
|
||||
|
||||
// FIXME: add span
|
||||
syn::parse_str(&if null_terminated {
|
||||
format!("\"{}\0\"", doc)
|
||||
} else {
|
||||
syn::Lit::Str(doc, syn::StrStyle::Cooked)
|
||||
}
|
||||
format!("\"{}\"", doc)
|
||||
}).unwrap()
|
||||
|
||||
}
|
||||
|
|
|
@ -13,12 +13,13 @@ license = "Apache-2.0"
|
|||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
quote="0.3"
|
||||
quote="0.5"
|
||||
|
||||
[dependencies.syn]
|
||||
version="0.11"
|
||||
features=["full"]
|
||||
version="0.13"
|
||||
features=["full", "parsing", "printing", "extra-traits"]
|
||||
|
||||
|
||||
[dependencies.pyo3-derive-backend]
|
||||
path = "../pyo3-derive-backend"
|
||||
version = "0.2.5"
|
||||
version = "0.2.5"
|
||||
|
|
Loading…
Reference in New Issue