Upgrade to syn 1.0

This commit is contained in:
konstin 2019-08-11 14:16:02 +02:00
parent be22d9e5bb
commit 39f151c3bd
12 changed files with 185 additions and 199 deletions

View file

@ -19,25 +19,25 @@ travis-ci = { repository = "PyO3/pyo3", branch = "master" }
appveyor = { repository = "fafhrd91/pyo3" } appveyor = { repository = "fafhrd91/pyo3" }
[dependencies] [dependencies]
libc = "0.2.54" libc = "0.2.62"
spin = "0.5.0" spin = "0.5.1"
num-traits = "0.2.6" num-traits = "0.2.8"
pyo3cls = { path = "pyo3cls", version = "=0.7.0" } pyo3cls = { path = "pyo3cls", version = "=0.7.0" }
num-complex = { version = "0.2.1", optional = true } num-complex = { version = "0.2.3", optional = true }
inventory = "0.1.3" inventory = "0.1.4"
indoc = "0.3.3" indoc = "0.3.4"
unindent = "0.1.3" unindent = "0.1.4"
paste = "0.1.5" paste = "0.1.6"
[dev-dependencies] [dev-dependencies]
assert_approx_eq = "1.1.0" assert_approx_eq = "1.1.0"
trybuild = "1.0" trybuild = "1.0.11"
[build-dependencies] [build-dependencies]
regex = "1.1.6" regex = "1.2.1"
version_check = "0.9.1" version_check = "0.9.1"
serde = { version = "1.0.91", features = ["derive"] } serde = { version = "1.0.99", features = ["derive"] }
serde_json = "1.0.39" serde_json = "1.0.40"
[features] [features]
default = [] default = []

View file

@ -11,9 +11,9 @@ license = "Apache-2.0"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
quote = "0.6.12" quote = "1"
proc-macro2 = "0.4.30" proc-macro2 = "1"
syn = { version = "0.15.34", features = ["full", "extra-traits"] } syn = { version = "1", features = ["full", "extra-traits"] }
[features] [features]
unsound-subclass = [] unsound-subclass = []

View file

@ -2,6 +2,7 @@
use crate::utils::print_err; use crate::utils::print_err;
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::quote; use quote::quote;
use syn::Token;
// TODO: // TODO:
// Add lifetime support for args with Rptr // Add lifetime support for args with Rptr
@ -70,7 +71,7 @@ impl PartialEq<str> for MethodProto {
pub fn impl_method_proto( pub fn impl_method_proto(
cls: &syn::Type, cls: &syn::Type,
sig: &mut syn::MethodSig, sig: &mut syn::Signature,
meth: &MethodProto, meth: &MethodProto,
) -> TokenStream { ) -> TokenStream {
if let MethodProto::Free { proto, .. } = meth { if let MethodProto::Free { proto, .. } = meth {
@ -80,7 +81,7 @@ pub fn impl_method_proto(
}; };
} }
let ty = &*if let syn::ReturnType::Type(_, ref ty) = sig.decl.output { let ty = &*if let syn::ReturnType::Type(_, ref ty) = sig.output {
ty.clone() ty.clone()
} else { } else {
panic!("fn return type is not supported") panic!("fn return type is not supported")
@ -95,7 +96,7 @@ pub fn impl_method_proto(
let tmp: syn::ItemFn = syn::parse_quote! { let tmp: syn::ItemFn = syn::parse_quote! {
fn test(&self) -> <#cls as #p<'p>>::Result {} fn test(&self) -> <#cls as #p<'p>>::Result {}
}; };
sig.decl.output = tmp.decl.output; sig.output = tmp.sig.output;
modify_self_ty(sig); modify_self_ty(sig);
if pyres { if pyres {
@ -119,7 +120,7 @@ pub fn impl_method_proto(
pyres, pyres,
proto, proto,
} => { } => {
if sig.decl.inputs.len() <= 1 { if sig.inputs.len() <= 1 {
println!("Not enough arguments for {}", name); println!("Not enough arguments for {}", name);
return TokenStream::new(); return TokenStream::new();
} }
@ -164,7 +165,7 @@ pub fn impl_method_proto(
pyres, pyres,
proto, proto,
} => { } => {
if sig.decl.inputs.len() <= 1 { if sig.inputs.len() <= 1 {
print_err(format!("Not enough arguments {}", name), quote!(sig)); print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new(); return TokenStream::new();
} }
@ -213,7 +214,7 @@ pub fn impl_method_proto(
pyres, pyres,
proto, proto,
} => { } => {
if sig.decl.inputs.len() <= 2 { if sig.inputs.len() <= 2 {
print_err(format!("Not enough arguments {}", name), quote!(sig)); print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new(); return TokenStream::new();
} }
@ -266,7 +267,7 @@ pub fn impl_method_proto(
pyres, pyres,
proto, proto,
} => { } => {
if sig.decl.inputs.len() <= 2 { if sig.inputs.len() <= 2 {
print_err(format!("Not enough arguments {}", name), quote!(sig)); print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new(); return TokenStream::new();
} }
@ -322,7 +323,7 @@ pub fn impl_method_proto(
arg3, arg3,
proto, proto,
} => { } => {
if sig.decl.inputs.len() <= 3 { if sig.inputs.len() <= 3 {
print_err(format!("Not enough arguments {}", name), quote!(sig)); print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new(); return TokenStream::new();
} }
@ -367,26 +368,26 @@ pub fn impl_method_proto(
} }
// TODO: better arg ty detection // TODO: better arg ty detection
fn get_arg_ty(sig: &syn::MethodSig, idx: usize) -> syn::Type { fn get_arg_ty(sig: &syn::Signature, idx: usize) -> syn::Type {
let mut ty = match sig.decl.inputs[idx] { let mut ty = match sig.inputs[idx] {
syn::FnArg::Captured(ref cap) => { syn::FnArg::Typed(ref cap) => {
match cap.ty { match *cap.ty {
syn::Type::Path(ref ty) => { syn::Type::Path(ref ty) => {
// use only last path segment for Option<> // use only last path segment for Option<>
let seg = *ty.path.segments.last().unwrap().value(); let seg = ty.path.segments.last().unwrap().clone();
if seg.ident == "Option" { if seg.ident == "Option" {
if let syn::PathArguments::AngleBracketed(ref data) = seg.arguments { if let syn::PathArguments::AngleBracketed(ref data) = seg.arguments {
if let Some(pair) = data.args.last() { if let Some(pair) = data.args.last() {
match pair.value() { match pair {
syn::GenericArgument::Type(ref ty) => return ty.clone(), syn::GenericArgument::Type(ref ty) => return ty.clone(),
_ => panic!("Option only accepted for concrete types"), _ => panic!("Option only accepted for concrete types"),
} }
}; };
} }
} }
cap.ty.clone() *cap.ty.clone()
} }
_ => cap.ty.clone(), _ => *cap.ty.clone(),
} }
} }
_ => panic!("fn arg type is not supported"), _ => panic!("fn arg type is not supported"),
@ -408,9 +409,9 @@ fn get_res_success(ty: &syn::Type) -> (TokenStream, syn::GenericArgument) {
match ty { match ty {
syn::Type::Path(ref typath) => { syn::Type::Path(ref typath) => {
if let Some(segment) = typath.path.segments.last() { if let Some(segment) = typath.path.segments.last() {
match segment.value().ident.to_string().as_str() { match segment.ident.to_string().as_str() {
// check for PyResult<T> // check for PyResult<T>
"PyResult" => match segment.value().arguments { "PyResult" => match segment.arguments {
syn::PathArguments::AngleBracketed(ref data) => { syn::PathArguments::AngleBracketed(ref data) => {
result = true; result = true;
succ = data.args[0].clone(); succ = data.args[0].clone();
@ -420,10 +421,10 @@ fn get_res_success(ty: &syn::Type) -> (TokenStream, syn::GenericArgument) {
data.args[0] data.args[0]
{ {
if let Some(segment) = typath.path.segments.last() { if let Some(segment) = typath.path.segments.last() {
if "Option" == segment.value().ident.to_string().as_str() { if "Option" == segment.ident.to_string().as_str() {
// get T from Option<T> // get T from Option<T>
if let syn::PathArguments::AngleBracketed(ref data) = if let syn::PathArguments::AngleBracketed(ref data) =
segment.value().arguments segment.arguments
{ {
result = false; result = false;
succ = data.args[0].clone(); succ = data.args[0].clone();
@ -436,7 +437,7 @@ fn get_res_success(ty: &syn::Type) -> (TokenStream, syn::GenericArgument) {
}, },
_ => panic!( _ => panic!(
"fn result type has to be PyResult or (), got {:?}", "fn result type has to be PyResult or (), got {:?}",
segment.value().ident segment.ident
), ),
} }
} else { } else {
@ -456,50 +457,55 @@ fn get_res_success(ty: &syn::Type) -> (TokenStream, syn::GenericArgument) {
(res, succ) (res, succ)
} }
fn extract_decl(spec: syn::Item) -> syn::FnDecl { fn extract_decl(spec: syn::Item) -> syn::Signature {
match spec { match spec {
syn::Item::Fn(f) => *f.decl, syn::Item::Fn(f) => f.sig,
_ => panic!(), _ => panic!(),
} }
} }
// modify method signature // modify method signature
fn modify_arg_ty(sig: &mut syn::MethodSig, idx: usize, decl1: &syn::FnDecl, decl2: &syn::FnDecl) { fn modify_arg_ty(
let arg = sig.decl.inputs[idx].clone(); sig: &mut syn::Signature,
idx: usize,
decl1: &syn::Signature,
decl2: &syn::Signature,
) {
let arg = sig.inputs[idx].clone();
match arg { match arg {
syn::FnArg::Captured(ref cap) => match cap.ty { syn::FnArg::Typed(ref cap) => match *cap.ty {
syn::Type::Path(ref typath) => { syn::Type::Path(ref typath) => {
let seg = *typath.path.segments.last().unwrap().value(); let seg = typath.path.segments.last().unwrap().clone();
if seg.ident == "Option" { if seg.ident == "Option" {
sig.decl.inputs[idx] = fix_name(&cap.pat, &decl2.inputs[idx]); sig.inputs[idx] = fix_name(&cap.pat, &decl2.inputs[idx]);
} else { } else {
sig.decl.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx]); sig.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx]);
} }
} }
_ => { _ => {
sig.decl.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx]); sig.inputs[idx] = fix_name(&cap.pat, &decl1.inputs[idx]);
} }
}, },
_ => panic!("not supported"), _ => panic!("not supported"),
} }
sig.decl.output = decl1.output.clone(); sig.output = decl1.output.clone();
} }
fn modify_self_ty(sig: &mut syn::MethodSig) { fn modify_self_ty(sig: &mut syn::Signature) {
match sig.decl.inputs[0] { match sig.inputs[0] {
syn::FnArg::SelfRef(ref mut slf) => { syn::FnArg::Receiver(ref mut slf) => {
slf.lifetime = Some(syn::parse_quote! {'p}); slf.reference = Some((Token![&](Span::call_site()), syn::parse_quote! {'p}));
} }
syn::FnArg::Captured(_) => {} syn::FnArg::Typed(_) => {}
_ => panic!("not supported"),
} }
} }
fn fix_name(pat: &syn::Pat, arg: &syn::FnArg) -> syn::FnArg { fn fix_name(pat: &syn::Pat, arg: &syn::FnArg) -> syn::FnArg {
if let syn::FnArg::Captured(ref cap) = arg { if let syn::FnArg::Typed(ref cap) = arg {
syn::FnArg::Captured(syn::ArgCaptured { syn::FnArg::Typed(syn::PatType {
pat: pat.clone(), attrs: cap.attrs.clone(),
pat: Box::new(pat.clone()),
colon_token: cap.colon_token, colon_token: cap.colon_token,
ty: cap.ty.clone(), ty: cap.ty.clone(),
}) })

View file

@ -49,22 +49,19 @@ impl<'a> FnSpec<'a> {
/// Parser function signature and function attributes /// Parser function signature and function attributes
pub fn parse( pub fn parse(
name: &'a syn::Ident, name: &'a syn::Ident,
sig: &'a syn::MethodSig, sig: &'a syn::Signature,
meth_attrs: &'a mut Vec<syn::Attribute>, meth_attrs: &'a mut Vec<syn::Attribute>,
) -> syn::Result<FnSpec<'a>> { ) -> syn::Result<FnSpec<'a>> {
let (mut fn_type, fn_attrs) = parse_attributes(meth_attrs)?; let (mut fn_type, fn_attrs) = parse_attributes(meth_attrs)?;
let mut has_self = false; let mut has_self = false;
let mut arguments = Vec::new(); let mut arguments = Vec::new();
for input in sig.decl.inputs.iter() { for input in sig.inputs.iter() {
match input { match input {
syn::FnArg::SelfRef(_) => { syn::FnArg::Receiver(_) => {
has_self = true; has_self = true;
} }
syn::FnArg::SelfValue(_) => { syn::FnArg::Typed(syn::PatType {
has_self = true;
}
syn::FnArg::Captured(syn::ArgCaptured {
ref pat, ref ty, .. ref pat, ref ty, ..
}) => { }) => {
// skip first argument (cls) // skip first argument (cls)
@ -73,7 +70,7 @@ impl<'a> FnSpec<'a> {
continue; continue;
} }
let (ident, by_ref, mutability) = match pat { let (ident, by_ref, mutability) = match **pat {
syn::Pat::Ident(syn::PatIdent { syn::Pat::Ident(syn::PatIdent {
ref ident, ref ident,
ref by_ref, ref by_ref,
@ -99,16 +96,10 @@ impl<'a> FnSpec<'a> {
reference: is_ref(name, ty), reference: is_ref(name, ty),
}); });
} }
syn::FnArg::Ignored(_) => {
return Err(syn::Error::new_spanned(name, "ignored argument"));
}
syn::FnArg::Inferred(_) => {
return Err(syn::Error::new_spanned(name, "inferred argument"));
}
} }
} }
let ty = get_return_info(&sig.decl.output); let ty = get_return_info(&sig.output);
if fn_type == FnType::Fn && !has_self { if fn_type == FnType::Fn && !has_self {
if arguments.is_empty() { if arguments.is_empty() {
@ -131,8 +122,8 @@ impl<'a> FnSpec<'a> {
pub fn is_args(&self, name: &syn::Ident) -> bool { pub fn is_args(&self, name: &syn::Ident) -> bool {
for s in self.attrs.iter() { for s in self.attrs.iter() {
if let Argument::VarArgs(ref ident) = s { if let Argument::VarArgs(ref path) = s {
return name == ident; return path.is_ident(name);
} }
} }
false false
@ -151,8 +142,8 @@ impl<'a> FnSpec<'a> {
pub fn is_kwargs(&self, name: &syn::Ident) -> bool { pub fn is_kwargs(&self, name: &syn::Ident) -> bool {
for s in self.attrs.iter() { for s in self.attrs.iter() {
if let Argument::KeywordArgs(ref ident) = s { if let Argument::KeywordArgs(ref path) = s {
return name == ident; return path.is_ident(name);
} }
} }
false false
@ -170,16 +161,16 @@ impl<'a> FnSpec<'a> {
pub fn default_value(&self, name: &syn::Ident) -> Option<TokenStream> { pub fn default_value(&self, name: &syn::Ident) -> Option<TokenStream> {
for s in self.attrs.iter() { for s in self.attrs.iter() {
match *s { match *s {
Argument::Arg(ref ident, ref opt) => { Argument::Arg(ref path, ref opt) => {
if ident == name { if path.is_ident(name) {
if let Some(ref val) = opt { if let Some(ref val) = opt {
let i: syn::Expr = syn::parse_str(&val).unwrap(); let i: syn::Expr = syn::parse_str(&val).unwrap();
return Some(i.into_token_stream()); return Some(i.into_token_stream());
} }
} }
} }
Argument::Kwarg(ref ident, ref opt) => { Argument::Kwarg(ref path, ref opt) => {
if ident == name { if path.is_ident(name) {
let i: syn::Expr = syn::parse_str(&opt).unwrap(); let i: syn::Expr = syn::parse_str(&opt).unwrap();
return Some(quote!(#i)); return Some(quote!(#i));
} }
@ -192,8 +183,8 @@ impl<'a> FnSpec<'a> {
pub fn is_kw_only(&self, name: &syn::Ident) -> bool { pub fn is_kw_only(&self, name: &syn::Ident) -> bool {
for s in self.attrs.iter() { for s in self.attrs.iter() {
if let Argument::Kwarg(ref ident, _) = s { if let Argument::Kwarg(ref path, _) = s {
if ident == name { if path.is_ident(name) {
return true; return true;
} }
} }
@ -207,8 +198,8 @@ pub fn is_ref(name: &syn::Ident, ty: &syn::Type) -> bool {
syn::Type::Reference(_) => return true, syn::Type::Reference(_) => return true,
syn::Type::Path(syn::TypePath { ref path, .. }) => { syn::Type::Path(syn::TypePath { ref path, .. }) => {
if let Some(segment) = path.segments.last() { if let Some(segment) = path.segments.last() {
if "Option" == segment.value().ident.to_string().as_str() { if "Option" == segment.ident.to_string().as_str() {
match segment.value().arguments { match segment.arguments {
syn::PathArguments::AngleBracketed(ref params) => { syn::PathArguments::AngleBracketed(ref params) => {
if params.args.len() != 1 { if params.args.len() != 1 {
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}", panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
@ -248,8 +239,8 @@ pub fn check_arg_ty_and_optional<'a>(
//} //}
if let Some(segment) = path.segments.last() { if let Some(segment) = path.segments.last() {
match segment.value().ident.to_string().as_str() { match segment.ident.to_string().as_str() {
"Option" => match segment.value().arguments { "Option" => match segment.arguments {
syn::PathArguments::AngleBracketed(ref params) => { syn::PathArguments::AngleBracketed(ref params) => {
if params.args.len() != 1 { if params.args.len() != 1 {
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}", panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
@ -294,40 +285,46 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> syn::Result<(FnType, Vec
let mut res: Option<FnType> = None; let mut res: Option<FnType> = None;
for attr in attrs.iter() { for attr in attrs.iter() {
match attr.interpret_meta().unwrap() { match attr.parse_meta().unwrap() {
syn::Meta::Word(ref name) => match name.to_string().as_ref() { syn::Meta::Path(ref name) => {
"new" | "__new__" => res = Some(FnType::FnNew), if name.is_ident("new") || name.is_ident("__new__") {
"init" | "__init__" => res = Some(FnType::FnInit), res = Some(FnType::FnNew)
"call" | "__call__" => res = Some(FnType::FnCall), } else if name.is_ident("init") || name.is_ident("__init__") {
"classmethod" => res = Some(FnType::FnClass), res = Some(FnType::FnInit)
"staticmethod" => res = Some(FnType::FnStatic), } else if name.is_ident("call") || name.is_ident("__call__") {
"setter" | "getter" => { res = Some(FnType::FnCall)
} else if name.is_ident("classmethod") {
res = Some(FnType::FnClass)
} else if name.is_ident("staticmethod") {
res = Some(FnType::FnStatic)
} else if name.is_ident("setter") || name.is_ident("getter") {
if let syn::AttrStyle::Inner(_) = attr.style { if let syn::AttrStyle::Inner(_) = attr.style {
panic!( panic!("Inner style attribute is not supported for setter and getter");
"Inner style attribute is not
supported for setter and getter"
);
} }
if res != None { if res != None {
panic!("setter/getter attribute can not be used mutiple times"); panic!("setter/getter attribute can not be used mutiple times");
} }
if name == "setter" { if name.is_ident("setter") {
res = Some(FnType::Setter(None)) res = Some(FnType::Setter(None))
} else { } else {
res = Some(FnType::Getter(None)) res = Some(FnType::Getter(None))
} }
} else {
new_attrs.push(attr.clone())
} }
_ => new_attrs.push(attr.clone()), }
},
syn::Meta::List(syn::MetaList { syn::Meta::List(syn::MetaList {
ref ident, ref path,
ref nested, ref nested,
.. ..
}) => match ident.to_string().as_str() { }) => {
"new" => res = Some(FnType::FnNew), if path.is_ident("new") {
"init" => res = Some(FnType::FnInit), res = Some(FnType::FnNew)
"call" => res = Some(FnType::FnCall), } else if path.is_ident("init") {
"setter" | "getter" => { res = Some(FnType::FnInit)
} else if path.is_ident("call") {
res = Some(FnType::FnCall)
} else if path.is_ident("setter") || path.is_ident("getter") {
if let syn::AttrStyle::Inner(_) = attr.style { if let syn::AttrStyle::Inner(_) = attr.style {
panic!( panic!(
"Inner style attribute is not "Inner style attribute is not
@ -340,17 +337,17 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> syn::Result<(FnType, Vec
if nested.len() != 1 { if nested.len() != 1 {
panic!("setter/getter requires one value"); panic!("setter/getter requires one value");
} }
match nested.first().unwrap().value() { match nested.first().unwrap() {
syn::NestedMeta::Meta(syn::Meta::Word(ref w)) => { syn::NestedMeta::Meta(syn::Meta::Path(ref w)) => {
if ident == "setter" { if path.is_ident("setter") {
res = Some(FnType::Setter(Some(w.to_string()))) res = Some(FnType::Setter(Some(w.segments[0].ident.to_string())))
} else { } else {
res = Some(FnType::Getter(Some(w.to_string()))) res = Some(FnType::Getter(Some(w.segments[0].ident.to_string())))
} }
} }
syn::NestedMeta::Literal(ref lit) => match *lit { syn::NestedMeta::Lit(ref lit) => match *lit {
syn::Lit::Str(ref s) => { syn::Lit::Str(ref s) => {
if ident == "setter" { if path.is_ident("setter") {
res = Some(FnType::Setter(Some(s.value()))) res = Some(FnType::Setter(Some(s.value())))
} else { } else {
res = Some(FnType::Getter(Some(s.value()))) res = Some(FnType::Getter(Some(s.value())))
@ -361,16 +358,16 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> syn::Result<(FnType, Vec
} }
}, },
_ => { _ => {
println!("cannot parse {:?} attribute: {:?}", ident, nested); println!("cannot parse {:?} attribute: {:?}", path, nested);
} }
} }
} } else if path.is_ident("args") {
"args" => {
let attrs = PyFunctionAttr::from_meta(nested)?; let attrs = PyFunctionAttr::from_meta(nested)?;
spec.extend(attrs.arguments) spec.extend(attrs.arguments)
} else {
new_attrs.push(attr.clone())
} }
_ => new_attrs.push(attr.clone()), }
},
syn::Meta::NameValue(_) => new_attrs.push(attr.clone()), syn::Meta::NameValue(_) => new_attrs.push(attr.clone()),
} }
} }

View file

@ -8,8 +8,7 @@ use crate::pymethod;
use crate::pymethod::get_arg_names; use crate::pymethod::get_arg_names;
use crate::utils; use crate::utils;
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::quote; use quote::{format_ident, quote};
use syn::ext::IdentExt;
use syn::Ident; use syn::Ident;
/// Generates the function that is called by the python interpreter to initialize the native /// Generates the function that is called by the python interpreter to initialize the native
@ -38,7 +37,7 @@ pub fn process_functions_in_module(func: &mut syn::ItemFn) {
extract_pyfn_attrs(&mut func.attrs) extract_pyfn_attrs(&mut func.attrs)
{ {
let function_to_python = add_fn_to_module(func, &python_name, pyfn_attrs); let function_to_python = add_fn_to_module(func, &python_name, pyfn_attrs);
let function_wrapper_ident = function_wrapper_ident(&func.ident); let function_wrapper_ident = function_wrapper_ident(&func.sig.ident);
let item: syn::ItemFn = syn::parse_quote! { let item: syn::ItemFn = syn::parse_quote! {
fn block_wrapper() { fn block_wrapper() {
#function_to_python #function_to_python
@ -57,9 +56,9 @@ pub fn process_functions_in_module(func: &mut syn::ItemFn) {
/// Transforms a rust fn arg parsed with syn into a method::FnArg /// Transforms a rust fn arg parsed with syn into a method::FnArg
fn wrap_fn_argument<'a>(input: &'a syn::FnArg, name: &'a Ident) -> Option<method::FnArg<'a>> { fn wrap_fn_argument<'a>(input: &'a syn::FnArg, name: &'a Ident) -> Option<method::FnArg<'a>> {
match input { match input {
syn::FnArg::SelfRef(_) | &syn::FnArg::SelfValue(_) => None, syn::FnArg::Receiver(_) => None,
syn::FnArg::Captured(ref cap) => { syn::FnArg::Typed(ref cap) => {
let (mutability, by_ref, ident) = match cap.pat { let (mutability, by_ref, ident) = match *cap.pat {
syn::Pat::Ident(ref patid) => (&patid.mutability, &patid.by_ref, &patid.ident), syn::Pat::Ident(ref patid) => (&patid.mutability, &patid.by_ref, &patid.ident),
_ => panic!("unsupported argument: {:?}", cap.pat), _ => panic!("unsupported argument: {:?}", cap.pat),
}; };
@ -76,15 +75,13 @@ fn wrap_fn_argument<'a>(input: &'a syn::FnArg, name: &'a Ident) -> Option<method
reference: method::is_ref(&name, &cap.ty), reference: method::is_ref(&name, &cap.ty),
}) })
} }
syn::FnArg::Ignored(_) => panic!("ignored argument: {:?}", name),
syn::FnArg::Inferred(_) => panic!("inferred argument: {:?}", name),
} }
} }
/// Extracts the data from the #[pyfn(...)] attribute of a function /// Extracts the data from the #[pyfn(...)] attribute of a function
fn extract_pyfn_attrs( fn extract_pyfn_attrs(
attrs: &mut Vec<syn::Attribute>, attrs: &mut Vec<syn::Attribute>,
) -> Option<(Ident, Ident, Vec<pyfunction::Argument>)> { ) -> Option<(syn::Path, Ident, Vec<pyfunction::Argument>)> {
let mut new_attrs = Vec::new(); let mut new_attrs = Vec::new();
let mut fnname = None; let mut fnname = None;
let mut modname = None; let mut modname = None;
@ -92,19 +89,19 @@ fn extract_pyfn_attrs(
for attr in attrs.iter() { for attr in attrs.iter() {
match attr.parse_meta() { match attr.parse_meta() {
Ok(syn::Meta::List(ref list)) if list.ident == "pyfn" => { Ok(syn::Meta::List(ref list)) if list.path.is_ident("pyfn") => {
let meta: Vec<_> = list.nested.iter().cloned().collect(); let meta: Vec<_> = list.nested.iter().cloned().collect();
if meta.len() >= 2 { if meta.len() >= 2 {
// read module name // read module name
match meta[0] { match meta[0] {
syn::NestedMeta::Meta(syn::Meta::Word(ref ident)) => { syn::NestedMeta::Meta(syn::Meta::Path(ref path)) => {
modname = Some(ident.clone()) modname = Some(path.clone())
} }
_ => panic!("The first parameter of pyfn must be a MetaItem"), _ => panic!("The first parameter of pyfn must be a MetaItem"),
} }
// read Python function name // read Python function name
match meta[1] { match meta[1] {
syn::NestedMeta::Literal(syn::Lit::Str(ref lits)) => { syn::NestedMeta::Lit(syn::Lit::Str(ref lits)) => {
fnname = Some(syn::Ident::new(&lits.value(), lits.span())); fnname = Some(syn::Ident::new(&lits.value(), lits.span()));
} }
_ => panic!("The second parameter of pyfn must be a Literal"), _ => panic!("The second parameter of pyfn must be a Literal"),
@ -130,10 +127,7 @@ fn extract_pyfn_attrs(
/// Coordinates the naming of a the add-function-to-python-module function /// Coordinates the naming of a the add-function-to-python-module function
fn function_wrapper_ident(name: &Ident) -> Ident { fn function_wrapper_ident(name: &Ident) -> Ident {
// Make sure this ident matches the one of wrap_pyfunction // Make sure this ident matches the one of wrap_pyfunction
Ident::new( format_ident!("__pyo3_get_function_{}", name)
&format!("__pyo3_get_function_{}", name.unraw().to_string()),
Span::call_site(),
)
} }
/// Generates python wrapper over a function that allows adding it to a python module as a python /// Generates python wrapper over a function that allows adding it to a python module as a python
@ -145,13 +139,13 @@ pub fn add_fn_to_module(
) -> TokenStream { ) -> TokenStream {
let mut arguments = Vec::new(); let mut arguments = Vec::new();
for input in func.decl.inputs.iter() { for input in func.sig.inputs.iter() {
if let Some(fn_arg) = wrap_fn_argument(input, &func.ident) { if let Some(fn_arg) = wrap_fn_argument(input, &func.sig.ident) {
arguments.push(fn_arg); arguments.push(fn_arg);
} }
} }
let ty = method::get_return_info(&func.decl.output); let ty = method::get_return_info(&func.sig.output);
let spec = method::FnSpec { let spec = method::FnSpec {
tp: method::FnType::Fn, tp: method::FnType::Fn,
@ -160,9 +154,9 @@ pub fn add_fn_to_module(
output: ty, output: ty,
}; };
let function_wrapper_ident = function_wrapper_ident(&func.ident); let function_wrapper_ident = function_wrapper_ident(&func.sig.ident);
let wrapper = function_c_wrapper(&func.ident, &spec); let wrapper = function_c_wrapper(&func.sig.ident, &spec);
let doc = utils::get_doc(&func.attrs, true); let doc = utils::get_doc(&func.attrs, true);
let tokens = quote! { let tokens = quote! {

View file

@ -59,7 +59,7 @@ impl PyClassArgs {
fn add_assign(&mut self, assign: &syn::ExprAssign) -> syn::Result<()> { fn add_assign(&mut self, assign: &syn::ExprAssign) -> syn::Result<()> {
let key = match *assign.left { let key = match *assign.left {
syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => { syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => {
exp.path.segments.first().unwrap().value().ident.to_string() exp.path.segments.first().unwrap().ident.to_string()
} }
_ => { _ => {
return Err(syn::Error::new_spanned(assign, "could not parse argument")); return Err(syn::Error::new_spanned(assign, "could not parse argument"));
@ -123,7 +123,7 @@ impl PyClassArgs {
/// Match a key/value flag /// Match a key/value flag
fn add_path(&mut self, exp: &syn::ExprPath) -> syn::Result<()> { fn add_path(&mut self, exp: &syn::ExprPath) -> syn::Result<()> {
let flag = exp.path.segments.first().unwrap().value().ident.to_string(); let flag = exp.path.segments.first().unwrap().ident.to_string();
let path = match flag.as_str() { let path = match flag.as_str() {
"gc" => { "gc" => {
parse_quote! {pyo3::type_object::PY_TYPE_FLAG_GC} parse_quote! {pyo3::type_object::PY_TYPE_FLAG_GC}
@ -184,28 +184,23 @@ fn parse_descriptors(item: &mut syn::Field) -> syn::Result<Vec<FnType>> {
let mut new_attrs = Vec::new(); let mut new_attrs = Vec::new();
for attr in item.attrs.iter() { for attr in item.attrs.iter() {
if let Ok(syn::Meta::List(ref list)) = attr.parse_meta() { if let Ok(syn::Meta::List(ref list)) = attr.parse_meta() {
match list.ident.to_string().as_str() { if list.path.is_ident("pyo3") {
"pyo3" => { for meta in list.nested.iter() {
for meta in list.nested.iter() { if let syn::NestedMeta::Meta(ref metaitem) = meta {
if let syn::NestedMeta::Meta(ref metaitem) = meta { if metaitem.path().is_ident("get") {
match metaitem.name().to_string().as_str() { descs.push(FnType::Getter(None));
"get" => { } else if metaitem.path().is_ident("set") {
descs.push(FnType::Getter(None)); descs.push(FnType::Setter(None));
} } else {
"set" => { return Err(syn::Error::new_spanned(
descs.push(FnType::Setter(None)); metaitem,
} "Only get and set are supported",
_ => { ));
return Err(syn::Error::new_spanned(
metaitem,
"Only get and set are supported",
));
}
}
} }
} }
} }
_ => new_attrs.push(attr.clone()), } else {
new_attrs.push(attr.clone())
} }
} else { } else {
new_attrs.push(attr.clone()); new_attrs.push(attr.clone());

View file

@ -2,15 +2,15 @@
use syn::parse::ParseBuffer; use syn::parse::ParseBuffer;
use syn::punctuated::Punctuated; use syn::punctuated::Punctuated;
use syn::{Ident, NestedMeta}; use syn::{NestedMeta, Path};
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Argument { pub enum Argument {
VarArgsSeparator, VarArgsSeparator,
VarArgs(syn::Ident), VarArgs(syn::Path),
KeywordArgs(syn::Ident), KeywordArgs(syn::Path),
Arg(syn::Ident, Option<String>), Arg(syn::Path, Option<String>),
Kwarg(syn::Ident, String), Kwarg(syn::Path, String),
} }
/// The attributes of the pyfunction macro /// The attributes of the pyfunction macro
@ -41,11 +41,11 @@ impl PyFunctionAttr {
pub fn add_item(&mut self, item: &NestedMeta) -> syn::Result<()> { pub fn add_item(&mut self, item: &NestedMeta) -> syn::Result<()> {
match item { match item {
NestedMeta::Meta(syn::Meta::Word(ref ident)) => self.add_work(item, ident)?, NestedMeta::Meta(syn::Meta::Path(ref ident)) => self.add_work(item, ident)?,
NestedMeta::Meta(syn::Meta::NameValue(ref nv)) => { NestedMeta::Meta(syn::Meta::NameValue(ref nv)) => {
self.add_name_value(item, nv)?; self.add_name_value(item, nv)?;
} }
NestedMeta::Literal(ref lit) => { NestedMeta::Lit(ref lit) => {
self.add_literal(item, lit)?; self.add_literal(item, lit)?;
} }
_ => { _ => {
@ -89,7 +89,7 @@ impl PyFunctionAttr {
Ok(()) Ok(())
} }
fn add_work(&mut self, item: &NestedMeta, ident: &Ident) -> syn::Result<()> { fn add_work(&mut self, item: &NestedMeta, path: &Path) -> syn::Result<()> {
// self.arguments in form somename // self.arguments in form somename
if self.has_kwargs { if self.has_kwargs {
return Err(syn::Error::new_spanned( return Err(syn::Error::new_spanned(
@ -103,7 +103,7 @@ impl PyFunctionAttr {
"syntax error, argument is not allowed after keyword argument", "syntax error, argument is not allowed after keyword argument",
)); ));
} }
self.arguments.push(Argument::Arg(ident.clone(), None)); self.arguments.push(Argument::Arg(path.clone(), None));
Ok(()) Ok(())
} }
@ -122,7 +122,7 @@ impl PyFunctionAttr {
return Err(syn::Error::new_spanned(item, "*(var args) is defined")); return Err(syn::Error::new_spanned(item, "*(var args) is defined"));
} }
self.has_varargs = true; self.has_varargs = true;
self.arguments.push(Argument::VarArgs(nv.ident.clone())); self.arguments.push(Argument::VarArgs(nv.path.clone()));
} else if litstr.value() == "**" { } else if litstr.value() == "**" {
// kwargs="**" // kwargs="**"
if self.has_kwargs { if self.has_kwargs {
@ -132,10 +132,10 @@ impl PyFunctionAttr {
)); ));
} }
self.has_kwargs = true; self.has_kwargs = true;
self.arguments.push(Argument::KeywordArgs(nv.ident.clone())); self.arguments.push(Argument::KeywordArgs(nv.path.clone()));
} else if self.has_varargs { } else if self.has_varargs {
self.arguments self.arguments
.push(Argument::Kwarg(nv.ident.clone(), litstr.value().clone())) .push(Argument::Kwarg(nv.path.clone(), litstr.value()))
} else { } else {
if self.has_kwargs { if self.has_kwargs {
return Err(syn::Error::new_spanned( return Err(syn::Error::new_spanned(
@ -144,18 +144,14 @@ impl PyFunctionAttr {
)); ));
} }
self.has_kw = true; self.has_kw = true;
self.arguments.push(Argument::Arg( self.arguments
nv.ident.clone(), .push(Argument::Arg(nv.path.clone(), Some(litstr.value())))
Some(litstr.value().clone()),
))
} }
} }
syn::Lit::Int(ref litint) => { syn::Lit::Int(ref litint) => {
if self.has_varargs { if self.has_varargs {
self.arguments.push(Argument::Kwarg( self.arguments
nv.ident.clone(), .push(Argument::Kwarg(nv.path.clone(), format!("{}", litint)));
format!("{}", litint.value()),
));
} else { } else {
if self.has_kwargs { if self.has_kwargs {
return Err(syn::Error::new_spanned( return Err(syn::Error::new_spanned(
@ -164,16 +160,14 @@ impl PyFunctionAttr {
)); ));
} }
self.has_kw = true; self.has_kw = true;
self.arguments.push(Argument::Arg( self.arguments
nv.ident.clone(), .push(Argument::Arg(nv.path.clone(), Some(format!("{}", litint))));
Some(format!("{}", litint.value())),
));
} }
} }
syn::Lit::Bool(ref litb) => { syn::Lit::Bool(ref litb) => {
if self.has_varargs { if self.has_varargs {
self.arguments self.arguments
.push(Argument::Kwarg(nv.ident.clone(), format!("{}", litb.value))); .push(Argument::Kwarg(nv.path.clone(), format!("{}", litb.value)));
} else { } else {
if self.has_kwargs { if self.has_kwargs {
return Err(syn::Error::new_spanned( return Err(syn::Error::new_spanned(
@ -183,7 +177,7 @@ impl PyFunctionAttr {
} }
self.has_kw = true; self.has_kw = true;
self.arguments.push(Argument::Arg( self.arguments.push(Argument::Arg(
nv.ident.clone(), nv.path.clone(),
Some(format!("{}", litb.value)), Some(format!("{}", litb.value)),
)); ));
} }

View file

@ -7,7 +7,7 @@ use quote::quote;
pub fn gen_py_method( pub fn gen_py_method(
cls: &syn::Type, cls: &syn::Type,
name: &syn::Ident, name: &syn::Ident,
sig: &mut syn::MethodSig, sig: &mut syn::Signature,
meth_attrs: &mut Vec<syn::Attribute>, meth_attrs: &mut Vec<syn::Attribute>,
) -> syn::Result<TokenStream> { ) -> syn::Result<TokenStream> {
check_generic(name, sig)?; check_generic(name, sig)?;
@ -49,14 +49,14 @@ pub fn gen_py_method(
}) })
} }
fn check_generic(name: &syn::Ident, sig: &syn::MethodSig) -> syn::Result<()> { fn check_generic(name: &syn::Ident, sig: &syn::Signature) -> syn::Result<()> {
let err_msg = |typ| { let err_msg = |typ| {
format!( format!(
"A Python method can't have a generic {} parameter: {}", "A Python method can't have a generic {} parameter: {}",
name, typ name, typ
) )
}; };
for param in &sig.decl.generics.params { for param in &sig.generics.params {
match param { match param {
syn::GenericParam::Lifetime(_) => {} syn::GenericParam::Lifetime(_) => {}
syn::GenericParam::Type(_) => { syn::GenericParam::Type(_) => {

View file

@ -12,7 +12,7 @@ use quote::ToTokens;
pub fn build_py_proto(ast: &mut syn::ItemImpl) -> syn::Result<TokenStream> { pub fn build_py_proto(ast: &mut syn::ItemImpl) -> syn::Result<TokenStream> {
if let Some((_, ref mut path, _)) = ast.trait_ { if let Some((_, ref mut path, _)) = ast.trait_ {
let proto = if let Some(ref mut segment) = path.segments.last() { let proto = if let Some(ref mut segment) = path.segments.last() {
match segment.value().ident.to_string().as_str() { match segment.ident.to_string().as_str() {
"PyObjectProtocol" => &defs::OBJECT, "PyObjectProtocol" => &defs::OBJECT,
"PyAsyncProtocol" => &defs::ASYNC, "PyAsyncProtocol" => &defs::ASYNC,
"PyMappingProtocol" => &defs::MAPPING, "PyMappingProtocol" => &defs::MAPPING,

View file

@ -14,7 +14,7 @@ pub fn if_type_is_python(ty: &syn::Type) -> bool {
.path .path
.segments .segments
.last() .last()
.map(|seg| seg.value().ident == "Python") .map(|seg| seg.ident == "Python")
.unwrap_or(false), .unwrap_or(false),
_ => false, _ => false,
} }
@ -28,8 +28,8 @@ pub fn get_doc(attrs: &[syn::Attribute], null_terminated: bool) -> syn::Lit {
// let mut span = None; // let mut span = None;
for attr in attrs.iter() { for attr in attrs.iter() {
if let Some(syn::Meta::NameValue(ref metanv)) = attr.interpret_meta() { if let Ok(syn::Meta::NameValue(ref metanv)) = attr.parse_meta() {
if metanv.ident == "doc" { if metanv.path.is_ident("doc") {
// span = Some(metanv.span()); // span = Some(metanv.span());
if let syn::Lit::Str(ref litstr) = metanv.lit { if let syn::Lit::Str(ref litstr) = metanv.lit {
let d = litstr.value(); let d = litstr.value();

View file

@ -14,9 +14,9 @@ edition = "2018"
proc-macro = true proc-macro = true
[dependencies] [dependencies]
quote= "0.6.12" quote = "1"
proc-macro2 = "0.4.30" proc-macro2 = "1"
syn = { version = "0.15.34", features = ["full", "extra-traits"] } syn = { version = "1", features = ["full", "extra-traits"] }
pyo3-derive-backend = { path = "../pyo3-derive-backend", version = "=0.7.0" } pyo3-derive-backend = { path = "../pyo3-derive-backend", version = "=0.7.0" }
[features] [features]

View file

@ -20,14 +20,14 @@ pub fn pymodule(attr: TokenStream, input: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(input as syn::ItemFn); let mut ast = parse_macro_input!(input as syn::ItemFn);
let modname = if attr.is_empty() { let modname = if attr.is_empty() {
ast.ident.clone() ast.sig.ident.clone()
} else { } else {
parse_macro_input!(attr as syn::Ident) parse_macro_input!(attr as syn::Ident)
}; };
process_functions_in_module(&mut ast); process_functions_in_module(&mut ast);
let expanded = py_init(&ast.ident, &modname, get_doc(&ast.attrs, false)); let expanded = py_init(&ast.sig.ident, &modname, get_doc(&ast.attrs, false));
quote!( quote!(
#ast #ast
@ -78,7 +78,7 @@ pub fn pyfunction(attr: TokenStream, input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as syn::ItemFn); let ast = parse_macro_input!(input as syn::ItemFn);
let args = parse_macro_input!(attr as PyFunctionAttr); let args = parse_macro_input!(attr as PyFunctionAttr);
let python_name = syn::Ident::new(&ast.ident.unraw().to_string(), Span::call_site()); let python_name = syn::Ident::new(&ast.sig.ident.unraw().to_string(), Span::call_site());
let expanded = add_fn_to_module(&ast, &python_name, args.arguments); let expanded = add_fn_to_module(&ast, &python_name, args.arguments);
quote!( quote!(