Big proc macro refactoring

* Removed a lot of clutter, unified some code
 * Started using syn::parse::Parse for pyfunction attributes 
 * No more newlines between imports
 * Renamed `#[prop(get, set)]` to `#[pyo3(get, set)]`
 * `#[pyfunction]` now supports the same arguments as `#[pyfn()]`
 * Some macros now emit proper spanned errors instead of panics.
This commit is contained in:
konstin 2019-02-18 20:07:41 +01:00
parent f248aaddbe
commit d02f7c3aa5
39 changed files with 1131 additions and 1188 deletions

View File

@ -23,6 +23,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
* `PyRawObject::init` is now infallible, e.g. it returns `()` instead of `PyResult<()>`.
* Renamed `py_exception!` to `create_exception!` and refactored the error macros.
* Renamed `wrap_function!` to `wrap_pyfunction!`
* Renamed `#[prop(get, set)]` to `#[pyo3(get, set)]`
* `#[pyfunction]` now supports the same arguments as `#[pyfn()]`
* Some macros now emit proper spanned errors instead of panics.
* Migrated to the 2018 edition
* Replace `IntoPyTuple` with `IntoPy<Py<PyTuple>>`. Eventually `IntoPy<T>` should replace `ToPyObject` and be itself implemented through `FromPy<T>`
* PyTypeObject is now a direct subtrait PyTypeCreate, removing the old cyclical implementation in [#350](https://github.com/PyO3/pyo3/pull/350)

View File

@ -15,7 +15,7 @@ A comparison with rust-cpython can be found [in the guide](https://pyo3.rs/maste
## Usage
Pyo3 supports python 2.7 as well as python 3.5 and up. The minimum required rust version is 1.30.0-nightly 2018-08-18.
Pyo3 supports python 2.7 as well as python 3.5 and up. The minimum required rust version is 1.34.0-nightly 2019-02-06.
You can either write a native python module in rust or use python from a rust binary.

View File

@ -16,7 +16,7 @@ struct WordCounter {
#[pymethods]
impl WordCounter {
#[new]
fn __new__(obj: &PyRawObject, path: String) -> PyResult<()> {
fn new(obj: &PyRawObject, path: String) -> PyResult<()> {
Ok(obj.init(WordCounter {
path: PathBuf::from(path),
}))

View File

@ -279,7 +279,7 @@ For simple cases you can also define getters and setters in your Rust struct fie
# use pyo3::prelude::*;
#[pyclass]
struct MyClass {
#[prop(get, set)]
#[pyo3(get, set)]
num: i32
}
```

View File

@ -1,250 +0,0 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use quote::quote;
use syn;
#[derive(Debug, Clone, PartialEq)]
pub enum Argument {
VarArgsSeparator,
VarArgs(syn::Ident),
KeywordArgs(syn::Ident),
Arg(syn::Ident, Option<String>),
Kwarg(syn::Ident, String),
}
pub fn parse_arguments(items: &[syn::NestedMeta]) -> Vec<Argument> {
let mut arguments = Vec::new();
let mut has_kw = false;
let mut has_varargs = false;
let mut has_kwargs = false;
let args_str = quote! {
#(#items),*
}
.to_string();
for item in items.iter() {
match item {
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);
return Vec::new();
}
if has_kw {
println!(
"syntax error, argument is not allowed after keyword argument: {:?}",
args_str
);
return Vec::new();
}
arguments.push(Argument::Arg(ident.clone(), None))
}
syn::NestedMeta::Meta(syn::Meta::NameValue(ref nv)) => {
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
);
return Vec::new();
}
if has_varargs {
println!("*(var args) is defined: {:?}", args_str);
return Vec::new();
}
has_varargs = true;
arguments.push(Argument::VarArgs(nv.ident.clone()));
} else if litstr.value() == "**" {
// #[args(kwargs="**")]
if has_kwargs {
println!("arguments already define ** (kw args): {:?}", args_str);
return Vec::new();
}
has_kwargs = true;
arguments.push(Argument::KeywordArgs(nv.ident.clone()));
} else {
if has_varargs {
arguments
.push(Argument::Kwarg(nv.ident.clone(), litstr.value().clone()))
} else {
if has_kwargs {
println!(
"syntax error, keyword arguments is defined: {:?}",
args_str
);
return Vec::new();
}
has_kw = true;
arguments.push(Argument::Arg(
nv.ident.clone(),
Some(litstr.value().clone()),
))
}
}
}
syn::Lit::Int(ref litint) => {
if has_varargs {
arguments.push(Argument::Kwarg(
nv.ident.clone(),
format!("{}", litint.value()),
));
} else {
if has_kwargs {
println!(
"syntax error, keyword arguments is defined: {:?}",
args_str
);
return Vec::new();
}
has_kw = true;
arguments.push(Argument::Arg(
nv.ident.clone(),
Some(format!("{}", litint.value())),
));
}
}
syn::Lit::Bool(ref litb) => {
if has_varargs {
arguments
.push(Argument::Kwarg(nv.ident.clone(), format!("{}", litb.value)));
} else {
if has_kwargs {
println!(
"syntax error, keyword arguments is defined: {:?}",
args_str
);
return Vec::new();
}
has_kw = true;
arguments.push(Argument::Arg(
nv.ident.clone(),
Some(format!("{}", litb.value)),
));
}
}
_ => {
println!("Only string literal is supported, got: {:?}", nv.lit);
return Vec::new();
}
}
}
syn::NestedMeta::Literal(ref lit) => {
match lit {
&syn::Lit::Str(ref lits) => {
// #[args("*")]
if lits.value() == "*" {
if has_kwargs {
println!(
"syntax error, keyword arguments is defined: {:?}",
args_str
);
return Vec::new();
}
if has_varargs {
println!("arguments already define * (var args): {:?}", args_str);
return Vec::new();
}
has_varargs = true;
arguments.push(Argument::VarArgsSeparator);
} else {
println!(
"Unknown string literal, got: {:?} args: {:?}",
lits.value(),
args_str
);
return Vec::new();
}
}
_ => {
println!(
"Only string literal is supported, got: {:?} args: {:?}",
lit, args_str
);
return Vec::new();
}
}
}
_ => {
println!("Unknown argument, got: {:?} args: {:?}", item, args_str);
return Vec::new();
}
}
}
arguments
}
#[cfg(test)]
mod test {
use crate::args::{parse_arguments, Argument};
use proc_macro2::TokenStream;
use syn;
fn items(s: TokenStream) -> Vec<syn::NestedMeta> {
let dummy: syn::ItemFn = syn::parse_quote! {#s fn dummy() {}};
match dummy.attrs[0].interpret_meta() {
Some(syn::Meta::List(syn::MetaList { nested, .. })) => {
nested.iter().map(Clone::clone).collect()
}
_ => unreachable!(),
}
}
#[test]
fn test_errs() {
assert!(parse_arguments(&items(quote! {#[args(test="1", test2)]})).is_empty());
assert!(parse_arguments(&items(quote! {#[args(test=1, "*", args="*")]})).is_empty());
assert!(
parse_arguments(&items(quote! {#[args(test=1, kwargs="**", args="*")]})).is_empty()
);
assert!(parse_arguments(&items(quote! {#[args(test=1, kwargs="**", args)]})).is_empty());
}
#[test]
fn test_simple_args() {
let args = parse_arguments(&items(quote! {#[args(test1, test2, test3="None")]}));
assert!(
args == vec![
Argument::Arg(syn::parse_quote! {test1}, None),
Argument::Arg(syn::parse_quote! {test2}, None),
Argument::Arg(syn::parse_quote! {test3}, Some("None".to_owned())),
]
);
}
#[test]
fn test_varargs() {
let args = parse_arguments(&items(
quote! {#[args(test1, test2="None", "*", test3="None")]},
));
assert!(
args == vec![
Argument::Arg(syn::parse_quote! {test1}, None),
Argument::Arg(syn::parse_quote! {test2}, Some("None".to_owned())),
Argument::VarArgsSeparator,
Argument::Kwarg(syn::parse_quote! {test3}, "None".to_owned()),
]
);
}
#[test]
fn test_all() {
let args = parse_arguments(&items(
quote! {#[args(test1, test2="None", args="*", test3="None", kwargs="**")]},
));
assert!(
args == vec![
Argument::Arg(syn::parse_quote! {test1}, None),
Argument::Arg(syn::parse_quote! {test2}, Some("None".to_owned())),
Argument::VarArgs(syn::parse_quote! {args}),
Argument::Kwarg(syn::parse_quote! {test3}, "None".to_owned()),
Argument::KeywordArgs(syn::parse_quote! {kwargs}),
]
);
}
}

View File

@ -2,7 +2,6 @@
use crate::utils::print_err;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn;
// TODO:
// Add lifetime support for args with Rptr
@ -55,8 +54,8 @@ pub enum MethodProto {
},
}
impl MethodProto {
pub fn eq(&self, name: &str) -> bool {
impl PartialEq<str> for MethodProto {
fn eq(&self, name: &str) -> bool {
match *self {
MethodProto::Free { name: n, .. } => n == name,
MethodProto::Unary { name: n, .. } => n == name,
@ -81,274 +80,221 @@ pub fn impl_method_proto(
};
}
if let syn::ReturnType::Type(_, ref ty) = sig.decl.output.clone() {
match *meth {
MethodProto::Free { .. } => unreachable!(),
MethodProto::Unary { pyres, proto, .. } => {
let p: syn::Path = syn::parse_str(proto).unwrap();
let (ty, succ) = get_res_success(ty);
let ty = &*if let syn::ReturnType::Type(_, ref ty) = sig.decl.output {
ty.clone()
} else {
panic!("fn return type is not supported")
};
let tmp: syn::ItemFn = syn::parse_quote! {
fn test(&self) -> <#cls as #p<'p>>::Result {}
};
sig.decl.output = tmp.decl.output;
modify_self_ty(sig);
match *meth {
MethodProto::Free { .. } => unreachable!(),
MethodProto::Unary { pyres, proto, .. } => {
let p: syn::Path = syn::parse_str(proto).unwrap();
let (ty, succ) = get_res_success(ty);
if pyres {
quote! {
impl<'p> #p<'p> for #cls {
type Success = #succ;
type Result = #ty;
}
let tmp: syn::ItemFn = syn::parse_quote! {
fn test(&self) -> <#cls as #p<'p>>::Result {}
};
sig.decl.output = tmp.decl.output;
modify_self_ty(sig);
if pyres {
quote! {
impl<'p> #p<'p> for #cls {
type Success = #succ;
type Result = #ty;
}
} else {
quote! {
impl<'p> #p<'p> for #cls {
type Result = #ty;
}
}
} else {
quote! {
impl<'p> #p<'p> for #cls {
type Result = #ty;
}
}
}
MethodProto::Binary {
name,
arg,
pyres,
proto,
} => {
if sig.decl.inputs.len() <= 1 {
println!("Not enough arguments for {}", name);
return TokenStream::new();
}
}
MethodProto::Binary {
name,
arg,
pyres,
proto,
} => {
if sig.decl.inputs.len() <= 1 {
println!("Not enough arguments for {}", name);
return TokenStream::new();
}
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg_name = syn::Ident::new(arg, Span::call_site());
let arg_ty = get_arg_ty(sig, 1);
let (ty, succ) = get_res_success(ty);
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg_name = syn::Ident::new(arg, Span::call_site());
let arg_ty = get_arg_ty(sig, 1);
let (ty, succ) = get_res_success(ty);
let tmp = extract_decl(syn::parse_quote! {
fn test(&self,arg: <#cls as #p<'p>>::#arg_name)-> <#cls as #p<'p>>::Result {}
});
let tmp = extract_decl(syn::parse_quote! {
fn test(&self,arg: <#cls as #p<'p>>::#arg_name)-> <#cls as #p<'p>>::Result {}
});
let tmp2 = extract_decl(syn::parse_quote! {
fn test(&self, arg: Option<<#cls as #p<'p>>::#arg_name>) -> <#cls as #p<'p>>::Result {}
});
let tmp2 = extract_decl(syn::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);
modify_arg_ty(sig, 1, &tmp, &tmp2);
modify_self_ty(sig);
if pyres {
quote! {
impl<'p> #p<'p> for #cls {
type #arg_name = #arg_ty;
type Success = #succ;
type Result = #ty;
}
if pyres {
quote! {
impl<'p> #p<'p> for #cls {
type #arg_name = #arg_ty;
type Success = #succ;
type Result = #ty;
}
} else {
quote! {
impl<'p> #p<'p> for #cls {
type #arg_name = #arg_ty;
type Result = #ty;
}
}
} else {
quote! {
impl<'p> #p<'p> for #cls {
type #arg_name = #arg_ty;
type Result = #ty;
}
}
}
MethodProto::BinaryS {
name,
arg1,
arg2,
pyres,
proto,
} => {
if sig.decl.inputs.len() <= 1 {
print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new();
}
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg1_name = syn::Ident::new(arg1, Span::call_site());
let arg1_ty = get_arg_ty(sig, 0);
let arg2_name = syn::Ident::new(arg2, Span::call_site());
let arg2_ty = get_arg_ty(sig, 1);
let (ty, succ) = get_res_success(ty);
}
MethodProto::BinaryS {
name,
arg1,
arg2,
pyres,
proto,
} => {
if sig.decl.inputs.len() <= 1 {
print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new();
}
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg1_name = syn::Ident::new(arg1, Span::call_site());
let arg1_ty = get_arg_ty(sig, 0);
let arg2_name = syn::Ident::new(arg2, Span::call_site());
let arg2_ty = get_arg_ty(sig, 1);
let (ty, succ) = get_res_success(ty);
// rewrite ty
let tmp = extract_decl(syn::parse_quote! {fn test(
arg1: <#cls as #p<'p>>::#arg1_name,
arg2: <#cls as #p<'p>>::#arg2_name)
-> <#cls as #p<'p>>::Result {}});
let tmp2 = extract_decl(syn::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 {}});
modify_arg_ty(sig, 0, &tmp, &tmp2);
modify_arg_ty(sig, 1, &tmp, &tmp2);
// rewrite ty
let tmp = extract_decl(syn::parse_quote! {fn test(
arg1: <#cls as #p<'p>>::#arg1_name,
arg2: <#cls as #p<'p>>::#arg2_name)
-> <#cls as #p<'p>>::Result {}});
let tmp2 = extract_decl(syn::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 {}});
modify_arg_ty(sig, 0, &tmp, &tmp2);
modify_arg_ty(sig, 1, &tmp, &tmp2);
if pyres {
quote! {
impl<'p> #p<'p> for #cls {
type #arg1_name = #arg1_ty;
type #arg2_name = #arg2_ty;
type Success = #succ;
type Result = #ty;
}
if pyres {
quote! {
impl<'p> #p<'p> for #cls {
type #arg1_name = #arg1_ty;
type #arg2_name = #arg2_ty;
type Success = #succ;
type Result = #ty;
}
} else {
quote! {
impl<'p> #p<'p> for #cls {
type #arg1_name = #arg1_ty;
type #arg2_name = #arg2_ty;
type Result = #ty;
}
}
} else {
quote! {
impl<'p> #p<'p> for #cls {
type #arg1_name = #arg1_ty;
type #arg2_name = #arg2_ty;
type Result = #ty;
}
}
}
MethodProto::Ternary {
name,
arg1,
arg2,
pyres,
proto,
} => {
if sig.decl.inputs.len() <= 2 {
print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new();
}
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg1_name = syn::Ident::new(arg1, Span::call_site());
let arg1_ty = get_arg_ty(sig, 1);
let arg2_name = syn::Ident::new(arg2, Span::call_site());
let arg2_ty = get_arg_ty(sig, 2);
let (ty, succ) = get_res_success(ty);
}
MethodProto::Ternary {
name,
arg1,
arg2,
pyres,
proto,
} => {
if sig.decl.inputs.len() <= 2 {
print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new();
}
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg1_name = syn::Ident::new(arg1, Span::call_site());
let arg1_ty = get_arg_ty(sig, 1);
let arg2_name = syn::Ident::new(arg2, Span::call_site());
let arg2_ty = get_arg_ty(sig, 2);
let (ty, succ) = get_res_success(ty);
// rewrite ty
let tmp = extract_decl(syn::parse_quote! {fn test(
&self,
arg1: <#cls as #p<'p>>::#arg1_name,
arg2: <#cls as #p<'p>>::#arg2_name)
-> <#cls as #p<'p>>::Result {}});
let tmp2 = extract_decl(syn::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 {}});
modify_arg_ty(sig, 1, &tmp, &tmp2);
modify_arg_ty(sig, 2, &tmp, &tmp2);
modify_self_ty(sig);
// rewrite ty
let tmp = extract_decl(syn::parse_quote! {fn test(
&self,
arg1: <#cls as #p<'p>>::#arg1_name,
arg2: <#cls as #p<'p>>::#arg2_name)
-> <#cls as #p<'p>>::Result {}});
let tmp2 = extract_decl(syn::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 {}});
modify_arg_ty(sig, 1, &tmp, &tmp2);
modify_arg_ty(sig, 2, &tmp, &tmp2);
modify_self_ty(sig);
if pyres {
quote! {
impl<'p> #p<'p> for #cls {
type #arg1_name = #arg1_ty;
type #arg2_name = #arg2_ty;
type Success = #succ;
type Result = #ty;
}
if pyres {
quote! {
impl<'p> #p<'p> for #cls {
type #arg1_name = #arg1_ty;
type #arg2_name = #arg2_ty;
type Success = #succ;
type Result = #ty;
}
} else {
quote! {
impl<'p> #p<'p> for #cls {
type #arg1_name = #arg1_ty;
type #arg2_name = #arg2_ty;
type Result = #ty;
}
}
} else {
quote! {
impl<'p> #p<'p> for #cls {
type #arg1_name = #arg1_ty;
type #arg2_name = #arg2_ty;
type Result = #ty;
}
}
}
MethodProto::TernaryS {
name,
arg1,
arg2,
arg3,
pyres,
proto,
} => {
if sig.decl.inputs.len() <= 2 {
print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new();
}
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg1_name = syn::Ident::new(arg1, Span::call_site());
let arg1_ty = get_arg_ty(sig, 0);
let arg2_name = syn::Ident::new(arg2, Span::call_site());
let arg2_ty = get_arg_ty(sig, 1);
let arg3_name = syn::Ident::new(arg3, Span::call_site());
let arg3_ty = get_arg_ty(sig, 2);
let (ty, succ) = get_res_success(ty);
// rewrite ty
let tmp = extract_decl(syn::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 {}});
let tmp2 = extract_decl(syn::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 {}});
modify_arg_ty(sig, 0, &tmp, &tmp2);
modify_arg_ty(sig, 1, &tmp, &tmp2);
modify_arg_ty(sig, 2, &tmp, &tmp2);
if pyres {
quote! {
impl<'p> #p<'p> for #cls {
type #arg1_name = #arg1_ty;
type #arg2_name = #arg2_ty;
type #arg3_name = #arg3_ty;
type Success = #succ;
type Result = #ty;
}
}
} else {
quote! {
impl<'p> #p<'p> for #cls {
type #arg1_name = #arg1_ty;
type #arg2_name = #arg2_ty;
type #arg3_name = #arg3_ty;
type Result = #ty;
}
}
}
}
MethodProto::TernaryS {
name,
arg1,
arg2,
arg3,
pyres,
proto,
} => {
if sig.decl.inputs.len() <= 2 {
print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new();
}
MethodProto::Quaternary {
name,
arg1,
arg2,
arg3,
proto,
} => {
if sig.decl.inputs.len() <= 3 {
print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new();
}
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg1_name = syn::Ident::new(arg1, Span::call_site());
let arg1_ty = get_arg_ty(sig, 1);
let arg2_name = syn::Ident::new(arg2, Span::call_site());
let arg2_ty = get_arg_ty(sig, 2);
let arg3_name = syn::Ident::new(arg3, Span::call_site());
let arg3_ty = get_arg_ty(sig, 3);
let (ty, succ) = get_res_success(ty);
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg1_name = syn::Ident::new(arg1, Span::call_site());
let arg1_ty = get_arg_ty(sig, 0);
let arg2_name = syn::Ident::new(arg2, Span::call_site());
let arg2_ty = get_arg_ty(sig, 1);
let arg3_name = syn::Ident::new(arg3, Span::call_site());
let arg3_ty = get_arg_ty(sig, 2);
let (ty, succ) = get_res_success(ty);
// rewrite ty
let tmp = extract_decl(syn::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 {}});
let tmp2 = extract_decl(syn::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 {}});
modify_arg_ty(sig, 1, &tmp, &tmp2);
modify_arg_ty(sig, 2, &tmp, &tmp2);
modify_arg_ty(sig, 3, &tmp, &tmp2);
modify_self_ty(sig);
// rewrite ty
let tmp = extract_decl(syn::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 {}});
let tmp2 = extract_decl(syn::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 {}});
modify_arg_ty(sig, 0, &tmp, &tmp2);
modify_arg_ty(sig, 1, &tmp, &tmp2);
modify_arg_ty(sig, 2, &tmp, &tmp2);
if pyres {
quote! {
impl<'p> #p<'p> for #cls {
type #arg1_name = #arg1_ty;
@ -358,10 +304,65 @@ pub fn impl_method_proto(
type Result = #ty;
}
}
} else {
quote! {
impl<'p> #p<'p> for #cls {
type #arg1_name = #arg1_ty;
type #arg2_name = #arg2_ty;
type #arg3_name = #arg3_ty;
type Result = #ty;
}
}
}
}
MethodProto::Quaternary {
name,
arg1,
arg2,
arg3,
proto,
} => {
if sig.decl.inputs.len() <= 3 {
print_err(format!("Not enough arguments {}", name), quote!(sig));
return TokenStream::new();
}
let p: syn::Path = syn::parse_str(proto).unwrap();
let arg1_name = syn::Ident::new(arg1, Span::call_site());
let arg1_ty = get_arg_ty(sig, 1);
let arg2_name = syn::Ident::new(arg2, Span::call_site());
let arg2_ty = get_arg_ty(sig, 2);
let arg3_name = syn::Ident::new(arg3, Span::call_site());
let arg3_ty = get_arg_ty(sig, 3);
let (ty, succ) = get_res_success(ty);
// rewrite ty
let tmp = extract_decl(syn::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 {}});
let tmp2 = extract_decl(syn::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 {}});
modify_arg_ty(sig, 1, &tmp, &tmp2);
modify_arg_ty(sig, 2, &tmp, &tmp2);
modify_arg_ty(sig, 3, &tmp, &tmp2);
modify_self_ty(sig);
quote! {
impl<'p> #p<'p> for #cls {
type #arg1_name = #arg1_ty;
type #arg2_name = #arg2_ty;
type #arg3_name = #arg3_ty;
type Success = #succ;
type Result = #ty;
}
}
}
} else {
panic!("fn return type is not supported")
}
}
@ -372,18 +373,15 @@ fn get_arg_ty(sig: &syn::MethodSig, idx: usize) -> syn::Type {
match cap.ty {
syn::Type::Path(ref ty) => {
// use only last path segment for Option<>
let seg = ty.path.segments.last().unwrap().value().clone();
let seg = *ty.path.segments.last().unwrap().value();
if seg.ident == "Option" {
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"),
}
};
}
_ => (),
if let syn::PathArguments::AngleBracketed(ref data) = seg.arguments {
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"),
}
};
}
}
cap.ty.clone()
@ -408,7 +406,7 @@ fn get_res_success(ty: &syn::Type) -> (TokenStream, syn::GenericArgument) {
let mut succ;
match ty {
&syn::Type::Path(ref typath) => {
syn::Type::Path(ref typath) => {
if let Some(segment) = typath.path.segments.last() {
match segment.value().ident.to_string().as_str() {
// check for PyResult<T>
@ -418,23 +416,20 @@ fn get_res_success(ty: &syn::Type) -> (TokenStream, syn::GenericArgument) {
succ = data.args[0].clone();
// check for PyResult<Option<T>>
match data.args[0] {
syn::GenericArgument::Type(syn::Type::Path(ref typath)) => {
if let Some(segment) = typath.path.segments.last() {
match segment.value().ident.to_string().as_str() {
// get T from Option<T>
"Option" => match segment.value().arguments {
syn::PathArguments::AngleBracketed(ref data) => {
result = false;
succ = data.args[0].clone();
}
_ => (),
},
_ => (),
if let syn::GenericArgument::Type(syn::Type::Path(ref typath)) =
data.args[0]
{
if let Some(segment) = typath.path.segments.last() {
if "Option" == segment.value().ident.to_string().as_str() {
// get T from Option<T>
if let syn::PathArguments::AngleBracketed(ref data) =
segment.value().arguments
{
result = false;
succ = data.args[0].clone();
}
}
}
_ => (),
}
}
_ => panic!("fn result type is not supported"),
@ -474,7 +469,7 @@ fn modify_arg_ty(sig: &mut syn::MethodSig, idx: usize, decl1: &syn::FnDecl, decl
match arg {
syn::FnArg::Captured(ref cap) => match cap.ty {
syn::Type::Path(ref typath) => {
let seg = typath.path.segments.last().unwrap().value().clone();
let seg = *typath.path.segments.last().unwrap().value();
if seg.ident == "Option" {
sig.decl.inputs[idx] = fix_name(&cap.pat, &decl2.inputs[idx]);
} else {

View File

@ -3,13 +3,20 @@
#![recursion_limit = "1024"]
pub mod args;
pub mod defs;
pub mod func;
pub mod method;
pub mod module;
pub mod py_class;
pub mod py_impl;
pub mod py_method;
pub mod py_proto;
pub mod utils;
mod defs;
mod func;
mod method;
mod module;
mod pyclass;
mod pyfunction;
mod pyimpl;
mod pymethod;
mod pyproto;
mod utils;
pub use module::{add_fn_to_module, process_functions_in_module, py2_init, py3_init};
pub use pyclass::{build_py_class, PyClassArgs};
pub use pyfunction::PyFunctionAttr;
pub use pyimpl::build_py_methods;
pub use pyproto::build_py_proto;
pub use utils::get_doc;

View File

@ -1,10 +1,10 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::args::{parse_arguments, Argument};
use crate::pyfunction::Argument;
use crate::pyfunction::PyFunctionAttr;
use proc_macro2::TokenStream;
use quote::quote;
use quote::ToTokens;
use syn;
#[derive(Clone, PartialEq, Debug)]
pub struct FnArg<'a> {
@ -50,21 +50,21 @@ impl<'a> FnSpec<'a> {
name: &'a syn::Ident,
sig: &'a syn::MethodSig,
meth_attrs: &'a mut Vec<syn::Attribute>,
) -> FnSpec<'a> {
let (fn_type, fn_attrs) = parse_attributes(meth_attrs);
) -> syn::Result<FnSpec<'a>> {
let (fn_type, fn_attrs) = parse_attributes(meth_attrs)?;
let mut has_self = false;
let mut arguments = Vec::new();
for input in sig.decl.inputs.iter() {
match input {
&syn::FnArg::SelfRef(_) => {
syn::FnArg::SelfRef(_) => {
has_self = true;
}
&syn::FnArg::SelfValue(_) => {
syn::FnArg::SelfValue(_) => {
has_self = true;
}
&syn::FnArg::Captured(syn::ArgCaptured {
syn::FnArg::Captured(syn::ArgCaptured {
ref pat, ref ty, ..
}) => {
// skip first argument (cls)
@ -74,17 +74,19 @@ impl<'a> FnSpec<'a> {
}
let (ident, by_ref, mutability) = match pat {
&syn::Pat::Ident(syn::PatIdent {
syn::Pat::Ident(syn::PatIdent {
ref ident,
ref by_ref,
ref mutability,
..
}) => (ident, by_ref, mutability),
_ => panic!("unsupported argument: {:?}", pat),
_ => {
return Err(syn::Error::new_spanned(pat, "unsupported argument"));
}
};
let py = match ty {
&syn::Type::Path(syn::TypePath { ref path, .. }) => {
syn::Type::Path(syn::TypePath { ref path, .. }) => {
if let Some(segment) = path.segments.last() {
segment.value().ident == "Python"
} else {
@ -100,32 +102,35 @@ impl<'a> FnSpec<'a> {
by_ref,
mutability,
// mode: mode,
ty: ty,
ty,
optional: opt,
py: py,
py,
reference: is_ref(name, ty),
});
}
&syn::FnArg::Ignored(_) => panic!("ignored argument: {:?}", name),
&syn::FnArg::Inferred(_) => panic!("ingerred argument: {:?}", name),
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);
FnSpec {
Ok(FnSpec {
tp: fn_type,
attrs: fn_attrs,
args: arguments,
output: ty,
}
})
}
pub fn is_args(&self, name: &syn::Ident) -> bool {
for s in self.attrs.iter() {
match *s {
Argument::VarArgs(ref ident) => return name == ident,
_ => (),
if let Argument::VarArgs(ref ident) = s {
return name == ident;
}
}
false
@ -144,9 +149,8 @@ impl<'a> FnSpec<'a> {
pub fn is_kwargs(&self, name: &syn::Ident) -> bool {
for s in self.attrs.iter() {
match *s {
Argument::KeywordArgs(ref ident) => return name == ident,
_ => (),
if let Argument::KeywordArgs(ref ident) = s {
return name == ident;
}
}
false
@ -154,9 +158,8 @@ impl<'a> FnSpec<'a> {
pub fn accept_kwargs(&self) -> bool {
for s in self.attrs.iter() {
match *s {
Argument::KeywordArgs(_) => return true,
_ => (),
if let Argument::KeywordArgs(_) = s {
return true;
}
}
false
@ -167,7 +170,7 @@ impl<'a> FnSpec<'a> {
match *s {
Argument::Arg(ref ident, ref opt) => {
if ident == name {
if let &Some(ref val) = opt {
if let Some(ref val) = opt {
let i: syn::Expr = syn::parse_str(&val).unwrap();
return Some(i.into_token_stream());
}
@ -187,36 +190,33 @@ impl<'a> FnSpec<'a> {
pub fn is_kw_only(&self, name: &syn::Ident) -> bool {
for s in self.attrs.iter() {
match *s {
Argument::Kwarg(ref ident, _) => {
if ident == name {
return true;
}
if let Argument::Kwarg(ref ident, _) = s {
if ident == name {
return true;
}
_ => (),
}
}
false
}
}
pub fn is_ref<'a>(name: &'a syn::Ident, ty: &'a syn::Type) -> bool {
pub fn is_ref(name: &syn::Ident, ty: &syn::Type) -> bool {
match ty {
&syn::Type::Reference(_) => return true,
&syn::Type::Path(syn::TypePath { ref path, .. }) => {
syn::Type::Reference(_) => return true,
syn::Type::Path(syn::TypePath { ref path, .. }) => {
if let Some(segment) = path.segments.last() {
match segment.value().ident.to_string().as_str() {
"Option" => match segment.value().arguments {
if "Option" == segment.value().ident.to_string().as_str() {
match segment.value().arguments {
syn::PathArguments::AngleBracketed(ref params) => {
if params.args.len() != 1 {
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
name,
ty,
path);
name,
ty,
path);
}
match &params.args[params.args.len() - 1] {
&syn::GenericArgument::Type(syn::Type::Reference(_)) => return true,
_ => (),
let last = &params.args[params.args.len() - 1];
if let syn::GenericArgument::Type(syn::Type::Reference(_)) = last {
return true;
}
}
_ => {
@ -225,8 +225,7 @@ pub fn is_ref<'a>(name: &'a syn::Ident, ty: &'a syn::Type) -> bool {
name, ty, path
);
}
},
_ => (),
}
}
}
}
@ -240,8 +239,8 @@ pub fn check_arg_ty_and_optional<'a>(
ty: &'a syn::Type,
) -> Option<&'a syn::Type> {
match ty {
&syn::Type::Path(syn::TypePath { ref path, .. }) => {
//if let &Some(ref qs) = qs {
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);
//}
@ -252,18 +251,18 @@ pub fn check_arg_ty_and_optional<'a>(
syn::PathArguments::AngleBracketed(ref params) => {
if params.args.len() != 1 {
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
name,
ty,
path);
name,
ty,
path);
}
match &params.args[0] {
&syn::GenericArgument::Type(ref ty) => Some(ty),
_ => panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
name,
ty,
path),
}
syn::GenericArgument::Type(ref ty) => Some(ty),
_ => panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
name,
ty,
path),
}
}
_ => {
panic!(
@ -287,7 +286,7 @@ pub fn check_arg_ty_and_optional<'a>(
}
}
fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<Argument>) {
fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> syn::Result<(FnType, Vec<Argument>)> {
let mut new_attrs = Vec::new();
let mut spec = Vec::new();
let mut res: Option<FnType> = None;
@ -365,8 +364,8 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<Argument>)
}
}
"args" => {
let args = nested.iter().cloned().collect::<Vec<_>>();
spec.extend(parse_arguments(args.as_slice()))
let attrs = PyFunctionAttr::from_meta(nested)?;
spec.extend(attrs.arguments)
}
_ => new_attrs.push(attr.clone()),
},
@ -377,7 +376,7 @@ fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<Argument>)
attrs.extend(new_attrs);
match res {
Some(tp) => (tp, spec),
None => (FnType::Fn, spec),
Some(tp) => Ok((tp, spec)),
None => Ok((FnType::Fn, spec)),
}
}

View File

@ -1,18 +1,20 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
//! Code generation for the function that initializes a python module and adds classes and function.
use crate::args;
use crate::method;
use crate::py_method;
use crate::pyfunction;
use crate::pyfunction::PyFunctionAttr;
use crate::pymethod;
use crate::pymethod::get_arg_names;
use crate::utils;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn;
use syn::Ident;
/// Generates the function that is called by the python interpreter to initialize the native
/// module
pub fn py3_init(fnname: &syn::Ident, name: &syn::Ident, doc: syn::Lit) -> TokenStream {
let cb_name = syn::Ident::new(&format!("PyInit_{}", name), Span::call_site());
pub fn py3_init(fnname: &Ident, name: &Ident, doc: syn::Lit) -> TokenStream {
let cb_name = Ident::new(&format!("PyInit_{}", name), Span::call_site());
quote! {
#[no_mangle]
@ -25,8 +27,8 @@ pub fn py3_init(fnname: &syn::Ident, name: &syn::Ident, doc: syn::Lit) -> TokenS
}
}
pub fn py2_init(fnname: &syn::Ident, name: &syn::Ident, doc: syn::Lit) -> TokenStream {
let cb_name = syn::Ident::new(&format!("init{}", name), Span::call_site());
pub fn py2_init(fnname: &Ident, name: &Ident, doc: syn::Lit) -> TokenStream {
let cb_name = Ident::new(&format!("init{}", name), Span::call_site());
quote! {
#[no_mangle]
@ -64,10 +66,10 @@ pub fn process_functions_in_module(func: &mut syn::ItemFn) {
}
/// 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>> {
fn wrap_fn_argument<'a>(input: &'a syn::FnArg, name: &'a Ident) -> Option<method::FnArg<'a>> {
match input {
&syn::FnArg::SelfRef(_) | &syn::FnArg::SelfValue(_) => None,
&syn::FnArg::Captured(ref cap) => {
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),
@ -94,23 +96,23 @@ fn wrap_fn_argument<'a>(input: &'a syn::FnArg, name: &'a syn::Ident) -> Option<m
reference: method::is_ref(&name, &cap.ty),
})
}
&syn::FnArg::Ignored(_) => panic!("ignored argument: {:?}", name),
&syn::FnArg::Inferred(_) => panic!("inferred argument: {:?}", name),
syn::FnArg::Ignored(_) => panic!("ignored argument: {:?}", name),
syn::FnArg::Inferred(_) => panic!("inferred argument: {:?}", name),
}
}
/// Extracts the data from the #[pyfn(...)] attribute of a function
fn extract_pyfn_attrs(
attrs: &mut Vec<syn::Attribute>,
) -> Option<(syn::Ident, syn::Ident, Vec<args::Argument>)> {
) -> Option<(Ident, Ident, Vec<pyfunction::Argument>)> {
let mut new_attrs = Vec::new();
let mut fnname = None;
let mut modname = None;
let mut fn_attrs = Vec::new();
for attr in attrs.iter() {
match attr.interpret_meta() {
Some(syn::Meta::List(ref list)) if list.ident == "pyfn" => {
match attr.parse_meta() {
Ok(syn::Meta::List(ref list)) if list.ident == "pyfn" => {
let meta: Vec<_> = list.nested.iter().cloned().collect();
if meta.len() >= 2 {
// read module name
@ -129,7 +131,9 @@ fn extract_pyfn_attrs(
}
// Read additional arguments
if list.nested.len() >= 3 {
fn_attrs = args::parse_arguments(&meta[2..meta.len()]);
fn_attrs = PyFunctionAttr::from_meta(&meta[2..meta.len()])
.unwrap()
.arguments;
}
} else {
panic!("can not parse 'pyfn' params {:?}", attr);
@ -144,10 +148,10 @@ fn extract_pyfn_attrs(
}
/// Coordinates the naming of a the add-function-to-python-module function
fn function_wrapper_ident(name: &syn::Ident) -> syn::Ident {
fn function_wrapper_ident(name: &Ident) -> Ident {
// Make sure this ident matches the one of wrap_pyfunction
// The trim_start_matches("r#") is for https://github.com/dtolnay/syn/issues/478
syn::Ident::new(
Ident::new(
&format!(
"__pyo3_get_function_{}",
name.to_string().trim_start_matches("r#")
@ -160,8 +164,8 @@ fn function_wrapper_ident(name: &syn::Ident) -> syn::Ident {
/// function
pub fn add_fn_to_module(
func: &syn::ItemFn,
python_name: &syn::Ident,
pyfn_attrs: Vec<args::Argument>,
python_name: &Ident,
pyfn_attrs: Vec<pyfunction::Argument>,
) -> TokenStream {
let mut arguments = Vec::new();
@ -214,25 +218,13 @@ pub fn add_fn_to_module(
}
/// Generate static function wrapper (PyCFunction, PyCFunctionWithKeywords)
fn function_c_wrapper(name: &syn::Ident, spec: &method::FnSpec<'_>) -> TokenStream {
let names: Vec<syn::Ident> = spec
.args
.iter()
.enumerate()
.map(|item| {
if item.1.py {
syn::Ident::new("_py", Span::call_site())
} else {
syn::Ident::new(&format!("arg{}", item.0), Span::call_site())
}
})
.collect();
fn function_c_wrapper(name: &Ident, spec: &method::FnSpec<'_>) -> TokenStream {
let names: Vec<Ident> = get_arg_names(&spec);
let cb = quote! {
::pyo3::ReturnTypeIntoPyResult::return_type_into_py_result(#name(#(#names),*))
#name(#(#names),*)
};
let body = py_method::impl_arg_params(spec, cb);
let body_to_result = py_method::body_to_result(&body, spec);
let body = pymethod::impl_arg_params(spec, cb);
quote! {
unsafe extern "C" fn __wrap(
@ -247,7 +239,8 @@ fn function_c_wrapper(name: &syn::Ident, spec: &method::FnSpec<'_>) -> TokenStre
let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args);
let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
#body_to_result
#body
::pyo3::callback::cb_convert(
::pyo3::callback::PyObjectCallbackConverter, _py, _result)
}

View File

@ -1,98 +0,0 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::defs;
use crate::func::impl_method_proto;
use crate::method::FnSpec;
use crate::py_method;
use proc_macro2::TokenStream;
use quote::quote;
use quote::ToTokens;
use syn;
pub fn build_py_proto(ast: &mut syn::ItemImpl) -> TokenStream {
if let Some((_, ref mut path, _)) = ast.trait_ {
let proto = if let Some(ref mut segment) = path.segments.last() {
match segment.value().ident.to_string().as_str() {
"PyObjectProtocol" => &defs::OBJECT,
"PyAsyncProtocol" => &defs::ASYNC,
"PyMappingProtocol" => &defs::MAPPING,
"PyIterProtocol" => &defs::ITER,
"PyContextProtocol" => &defs::CONTEXT,
"PySequenceProtocol" => &defs::SEQ,
"PyNumberProtocol" => &defs::NUM,
"PyDescrProtocol" => &defs::DESCR,
"PyBufferProtocol" => &defs::BUFFER,
"PyGCProtocol" => &defs::GC,
_ => panic!("#[pyproto] can not be used with this block"),
}
} else {
panic!("#[pyproto] can only be used with protocol trait implementations")
};
let tokens = impl_proto_impl(&ast.self_ty, &mut ast.items, proto);
// attach lifetime
let mut seg = path.segments.pop().unwrap().into_value();
seg.arguments = syn::PathArguments::AngleBracketed(syn::parse_quote! {<'p>});
path.segments.push(seg);
ast.generics.params = syn::parse_quote! {'p};
tokens
} else {
panic!("#[pyproto] can only be used with protocol trait implementations")
}
}
fn impl_proto_impl(
ty: &syn::Type,
impls: &mut Vec<syn::ImplItem>,
proto: &defs::Proto,
) -> TokenStream {
let mut tokens = TokenStream::new();
let mut py_methods = Vec::new();
for iimpl in impls.iter_mut() {
match iimpl {
syn::ImplItem::Method(ref mut met) => {
for m in proto.methods {
if m.eq(met.sig.ident.to_string().as_str()) {
impl_method_proto(ty, &mut met.sig, m).to_tokens(&mut tokens);
}
}
for m in proto.py_methods {
let ident = met.sig.ident.clone();
if m.name == ident.to_string().as_str() {
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! {
impl #proto for #ty
{
#[inline]
fn #name() -> Option<::pyo3::class::methods::PyMethodDef> {
#meth
Some(::pyo3::class::PyMethodDef {
ml_name: stringify!(#name),
ml_meth: ::pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
ml_flags: ::pyo3::ffi::METH_VARARGS | ::pyo3::ffi::METH_KEYWORDS,
ml_doc: ""})
}
}
});
}
}
}
_ => (),
}
}
quote! {
#tokens
#(#py_methods)*
}
}

View File

@ -1,43 +1,170 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::method::{FnArg, FnSpec, FnType};
use crate::py_method::{
impl_py_getter_def, impl_py_setter_def, impl_wrap_getter, impl_wrap_setter,
};
use crate::pymethod::{impl_py_getter_def, impl_py_setter_def, impl_wrap_getter, impl_wrap_setter};
use crate::utils;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use std::collections::HashMap;
use syn;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::{parse_quote, Expr, Token};
pub fn build_py_class(class: &mut syn::ItemStruct, attr: &Vec<syn::Expr>) -> TokenStream {
let (params, flags, base) = parse_attribute(attr);
/// The parsed arguments of the pyclass macro
pub struct PyClassArgs {
pub freelist: Option<syn::Expr>,
pub name: Option<syn::Expr>,
pub flags: Vec<syn::Expr>,
pub base: syn::TypePath,
}
impl Parse for PyClassArgs {
fn parse(input: ParseStream) -> syn::parse::Result<Self> {
let mut slf = PyClassArgs::default();
let vars = Punctuated::<Expr, Token![,]>::parse_terminated(input)?;
for expr in vars {
slf.add_expr(&expr)?;
}
Ok(slf)
}
}
impl Default for PyClassArgs {
fn default() -> Self {
PyClassArgs {
freelist: None,
name: None,
// We need the 0 as value for the constant we're later building using quote for when there
// are no other flags
flags: vec![parse_quote! {0}],
base: parse_quote! {::pyo3::types::PyObjectRef},
}
}
}
impl PyClassArgs {
/// Adda single expression from the comma separated list in the attribute, which is
/// either a single word or an assignment expression
fn add_expr(&mut self, expr: &Expr) -> syn::parse::Result<()> {
match expr {
syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => self.add_path(exp),
syn::Expr::Assign(ref assign) => self.add_assign(assign),
_ => Err(syn::Error::new_spanned(expr, "Could not parse arguments")),
}
}
/// Match a single flag
fn add_assign(&mut self, assign: &syn::ExprAssign) -> syn::Result<()> {
let key = match *assign.left {
syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => {
exp.path.segments.first().unwrap().value().ident.to_string()
}
_ => {
return Err(syn::Error::new_spanned(assign, "could not parse argument"));
}
};
match key.as_str() {
"freelist" => {
// We allow arbitrary expressions here so you can e.g. use `8*64`
self.freelist = Some(*assign.right.clone());
}
"name" => match *assign.right {
syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => {
self.name = Some(exp.clone().into());
}
_ => {
return Err(syn::Error::new_spanned(
*assign.right.clone(),
"Wrong 'name' format",
));
}
},
"extends" => match *assign.right {
syn::Expr::Path(ref exp) => {
self.base = syn::TypePath {
path: exp.path.clone(),
qself: None,
};
}
_ => {
return Err(syn::Error::new_spanned(
*assign.right.clone(),
"Wrong format for extends",
));
}
},
_ => {
return Err(syn::Error::new_spanned(
*assign.left.clone(),
"Unsupported parameter",
));
}
};
Ok(())
}
/// Match a key/value flag
fn add_path(&mut self, exp: &syn::ExprPath) -> syn::Result<()> {
let flag = exp.path.segments.first().unwrap().value().ident.to_string();
let path = match flag.as_str() {
"gc" => {
parse_quote! {::pyo3::typeob::PY_TYPE_FLAG_GC}
}
"weakref" => {
parse_quote! {::pyo3::typeob::PY_TYPE_FLAG_WEAKREF}
}
"subclass" => {
parse_quote! {::pyo3::typeob::PY_TYPE_FLAG_BASETYPE}
}
"dict" => {
parse_quote! {::pyo3::typeob::PY_TYPE_FLAG_DICT}
}
_ => {
return Err(syn::Error::new_spanned(
exp.path.clone(),
"Unsupported parameter",
));
}
};
self.flags.push(syn::Expr::Path(path));
Ok(())
}
}
pub fn build_py_class(class: &mut syn::ItemStruct, attr: &PyClassArgs) -> syn::Result<TokenStream> {
let doc = utils::get_doc(&class.attrs, true);
let mut descriptors = Vec::new();
if let syn::Fields::Named(ref mut fields) = class.fields {
for field in fields.named.iter_mut() {
let field_descs = parse_descriptors(field);
let field_descs = parse_descriptors(field)?;
if !field_descs.is_empty() {
descriptors.push((field.clone(), field_descs));
}
}
} else {
panic!("#[pyclass] can only be used with C-style structs")
return Err(syn::Error::new_spanned(
&class.fields,
"#[pyclass] can only be used with C-style structs",
));
}
impl_class(&class.ident, &base, doc, params, flags, descriptors)
Ok(impl_class(&class.ident, &attr, doc, descriptors))
}
fn parse_descriptors(item: &mut syn::Field) -> Vec<FnType> {
/// Parses `#[pyo3(get, set)]`
fn parse_descriptors(item: &mut syn::Field) -> syn::Result<Vec<FnType>> {
let mut descs = Vec::new();
let mut new_attrs = Vec::new();
for attr in item.attrs.iter() {
if let Some(syn::Meta::List(ref list)) = attr.interpret_meta() {
if let Ok(syn::Meta::List(ref list)) = attr.parse_meta() {
match list.ident.to_string().as_str() {
"prop" => {
"pyo3" => {
for meta in list.nested.iter() {
if let &syn::NestedMeta::Meta(ref metaitem) = meta {
if let syn::NestedMeta::Meta(ref metaitem) = meta {
match metaitem.name().to_string().as_str() {
"get" => {
descs.push(FnType::Getter(None));
@ -45,8 +172,11 @@ fn parse_descriptors(item: &mut syn::Field) -> Vec<FnType> {
"set" => {
descs.push(FnType::Setter(None));
}
x => {
panic!(r#"Only "get" and "set" supported are, not "{}""#, x);
_ => {
return Err(syn::Error::new_spanned(
metaitem,
"Only get and set are supported",
));
}
}
}
@ -60,7 +190,7 @@ fn parse_descriptors(item: &mut syn::Field) -> Vec<FnType> {
}
item.attrs.clear();
item.attrs.extend(new_attrs);
descs
Ok(descs)
}
/// The orphan rule disallows using a generic inventory struct, so we create the whole boilerplate
@ -99,19 +229,17 @@ fn impl_inventory(cls: &syn::Ident) -> TokenStream {
fn impl_class(
cls: &syn::Ident,
base: &syn::TypePath,
attr: &PyClassArgs,
doc: syn::Lit,
params: HashMap<&'static str, syn::Expr>,
flags: Vec<syn::Expr>,
descriptors: Vec<(syn::Field, Vec<FnType>)>,
) -> TokenStream {
let cls_name = match params.get("name") {
let cls_name = match &attr.name {
Some(name) => quote! { #name }.to_string(),
None => quote! { #cls }.to_string(),
None => cls.to_string(),
};
let extra = {
if let Some(freelist) = params.get("freelist") {
if let Some(freelist) = &attr.freelist {
quote! {
impl ::pyo3::freelist::PyObjectWithFreeList for #cls {
#[inline]
@ -150,11 +278,11 @@ fn impl_class(
// insert space for weak ref
let mut has_weakref = false;
let mut has_dict = false;
for f in flags.iter() {
for f in attr.flags.iter() {
if let syn::Expr::Path(ref epath) = f {
if epath.path == syn::parse_quote! {::pyo3::typeob::PY_TYPE_FLAG_WEAKREF} {
if epath.path == parse_quote! {::pyo3::typeob::PY_TYPE_FLAG_WEAKREF} {
has_weakref = true;
} else if epath.path == syn::parse_quote! {::pyo3::typeob::PY_TYPE_FLAG_DICT} {
} else if epath.path == parse_quote! {::pyo3::typeob::PY_TYPE_FLAG_DICT} {
has_dict = true;
}
}
@ -172,6 +300,9 @@ fn impl_class(
let inventory_impl = impl_inventory(&cls);
let base = &attr.base;
let flags = &attr.flags;
quote! {
impl ::pyo3::typeob::PyTypeInfo for #cls {
type Type = #cls;
@ -280,7 +411,7 @@ fn impl_descriptors(cls: &syn::Type, descriptors: Vec<(syn::Field, Vec<FnType>)>
py: true,
reference: false,
}],
output: syn::parse_quote!(PyResult<()>),
output: parse_quote!(PyResult<()>),
};
impl_py_setter_def(
&name,
@ -307,82 +438,3 @@ fn impl_descriptors(cls: &syn::Type, descriptors: Vec<(syn::Field, Vec<FnType>)>
}
}
}
fn parse_attribute(
args: &Vec<syn::Expr>,
) -> (
HashMap<&'static str, syn::Expr>,
Vec<syn::Expr>,
syn::TypePath,
) {
let mut params = HashMap::new();
// We need the 0 as value for the constant we're later building using quote for when there
// are no other flags
let mut flags = vec![syn::parse_quote! {0}];
let mut base: syn::TypePath = syn::parse_quote! {::pyo3::types::PyObjectRef};
for expr in args.iter() {
match expr {
// Match a single flag
syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => {
let flag = exp.path.segments.first().unwrap().value().ident.to_string();
let path = match flag.as_str() {
"gc" => {
syn::parse_quote! {::pyo3::typeob::PY_TYPE_FLAG_GC}
}
"weakref" => {
syn::parse_quote! {::pyo3::typeob::PY_TYPE_FLAG_WEAKREF}
}
"subclass" => {
syn::parse_quote! {::pyo3::typeob::PY_TYPE_FLAG_BASETYPE}
}
"dict" => {
syn::parse_quote! {::pyo3::typeob::PY_TYPE_FLAG_DICT}
}
param => panic!("Unsupported parameter: {}", param),
};
flags.push(syn::Expr::Path(path));
}
// Match a key/value flag
syn::Expr::Assign(ref ass) => {
let key = match *ass.left {
syn::Expr::Path(ref exp) if exp.path.segments.len() == 1 => {
exp.path.segments.first().unwrap().value().ident.to_string()
}
_ => panic!("could not parse argument: {:?}", ass),
};
match key.as_str() {
"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());
}
_ => panic!("Wrong 'name' format: {:?}", *ass.right),
},
"extends" => match *ass.right {
syn::Expr::Path(ref exp) => {
base = syn::TypePath {
path: exp.path.clone(),
qself: None,
};
}
_ => panic!("Wrong 'base' format: {:?}", *ass.right),
},
_ => {
panic!("Unsupported parameter: {:?}", key);
}
}
}
_ => panic!("could not parse arguments"),
}
}
(params, flags, base)
}

View File

@ -0,0 +1,261 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use syn::parse::ParseBuffer;
use syn::punctuated::Punctuated;
use syn::{Ident, NestedMeta};
#[derive(Debug, Clone, PartialEq)]
pub enum Argument {
VarArgsSeparator,
VarArgs(syn::Ident),
KeywordArgs(syn::Ident),
Arg(syn::Ident, Option<String>),
Kwarg(syn::Ident, String),
}
/// The attributes of the pyfunction macro
#[derive(Default)]
pub struct PyFunctionAttr {
pub arguments: Vec<Argument>,
has_kw: bool,
has_varargs: bool,
has_kwargs: bool,
}
impl syn::parse::Parse for PyFunctionAttr {
fn parse(input: &ParseBuffer) -> syn::Result<Self> {
let attr = Punctuated::<NestedMeta, syn::Token![,]>::parse_terminated(input)?;
Self::from_meta(&attr)
}
}
impl PyFunctionAttr {
pub fn from_meta<'a>(iter: impl IntoIterator<Item = &'a NestedMeta>) -> syn::Result<Self> {
let mut slf = PyFunctionAttr::default();
for item in iter {
slf.add_item(item)?
}
Ok(slf)
}
pub fn add_item(&mut self, item: &NestedMeta) -> syn::Result<()> {
match item {
NestedMeta::Meta(syn::Meta::Word(ref ident)) => self.add_work(item, ident)?,
NestedMeta::Meta(syn::Meta::NameValue(ref nv)) => {
self.add_name_value(item, nv)?;
}
NestedMeta::Literal(ref lit) => {
self.add_literal(item, lit)?;
}
_ => {
return Err(syn::Error::new_spanned(item, "Unknown argument"));
}
}
Ok(())
}
fn add_literal(&mut self, item: &NestedMeta, lit: &syn::Lit) -> syn::Result<()> {
match lit {
syn::Lit::Str(ref lits) => {
// "*"
if lits.value() == "*" {
if self.has_kwargs {
return Err(syn::Error::new_spanned(
item,
"syntax error, keyword self.arguments is defined",
));
}
if self.has_varargs {
return Err(syn::Error::new_spanned(
item,
"self.arguments already define * (var args)",
));
}
self.has_varargs = true;
self.arguments.push(Argument::VarArgsSeparator);
} else {
return Err(syn::Error::new_spanned(lits, "Unknown string literal"));
}
}
_ => {
return Err(syn::Error::new_spanned(
item,
format!("Only string literal is supported, got: {:?}", lit),
));
}
};
Ok(())
}
fn add_work(&mut self, item: &NestedMeta, ident: &Ident) -> syn::Result<()> {
// self.arguments in form somename
if self.has_kwargs {
return Err(syn::Error::new_spanned(
item,
"syntax error, keyword self.arguments is defined",
));
}
if self.has_kw {
return Err(syn::Error::new_spanned(
item,
"syntax error, argument is not allowed after keyword argument",
));
}
self.arguments.push(Argument::Arg(ident.clone(), None));
Ok(())
}
fn add_name_value(&mut self, item: &NestedMeta, nv: &syn::MetaNameValue) -> syn::Result<()> {
match nv.lit {
syn::Lit::Str(ref litstr) => {
if litstr.value() == "*" {
// args="*"
if self.has_kwargs {
return Err(syn::Error::new_spanned(
item,
"* - syntax error, keyword self.arguments is defined",
));
}
if self.has_varargs {
return Err(syn::Error::new_spanned(item, "*(var args) is defined"));
}
self.has_varargs = true;
self.arguments.push(Argument::VarArgs(nv.ident.clone()));
} else if litstr.value() == "**" {
// kwargs="**"
if self.has_kwargs {
return Err(syn::Error::new_spanned(
item,
"self.arguments already define ** (kw args)",
));
}
self.has_kwargs = true;
self.arguments.push(Argument::KeywordArgs(nv.ident.clone()));
} else if self.has_varargs {
self.arguments
.push(Argument::Kwarg(nv.ident.clone(), litstr.value().clone()))
} else {
if self.has_kwargs {
return Err(syn::Error::new_spanned(
item,
"syntax error, keyword self.arguments is defined",
));
}
self.has_kw = true;
self.arguments.push(Argument::Arg(
nv.ident.clone(),
Some(litstr.value().clone()),
))
}
}
syn::Lit::Int(ref litint) => {
if self.has_varargs {
self.arguments.push(Argument::Kwarg(
nv.ident.clone(),
format!("{}", litint.value()),
));
} else {
if self.has_kwargs {
return Err(syn::Error::new_spanned(
item,
"syntax error, keyword self.arguments is defined",
));
}
self.has_kw = true;
self.arguments.push(Argument::Arg(
nv.ident.clone(),
Some(format!("{}", litint.value())),
));
}
}
syn::Lit::Bool(ref litb) => {
if self.has_varargs {
self.arguments
.push(Argument::Kwarg(nv.ident.clone(), format!("{}", litb.value)));
} else {
if self.has_kwargs {
return Err(syn::Error::new_spanned(
item,
"syntax error, keyword self.arguments is defined",
));
}
self.has_kw = true;
self.arguments.push(Argument::Arg(
nv.ident.clone(),
Some(format!("{}", litb.value)),
));
}
}
_ => {
return Err(syn::Error::new_spanned(
nv.lit.clone(),
"Only string literal is supported",
));
}
};
Ok(())
}
}
#[cfg(test)]
mod test {
use super::{Argument, PyFunctionAttr};
use proc_macro2::TokenStream;
use quote::quote;
use syn::parse_quote;
fn items(input: TokenStream) -> syn::Result<Vec<Argument>> {
let py_fn_attr: PyFunctionAttr = syn::parse2(input)?;
Ok(py_fn_attr.arguments)
}
#[test]
fn test_errs() {
assert!(items(quote! {test="1", test2}).is_err());
assert!(items(quote! {test, "*", args="*"}).is_err());
assert!(items(quote! {test, kwargs="**", args="*"}).is_err());
assert!(items(quote! {test, kwargs="**", args}).is_err());
}
#[test]
fn test_simple_args() {
let args = items(quote! {test1, test2, test3="None"}).unwrap();
assert!(
args == vec![
Argument::Arg(parse_quote! {test1}, None),
Argument::Arg(parse_quote! {test2}, None),
Argument::Arg(parse_quote! {test3}, Some("None".to_owned())),
]
);
}
#[test]
fn test_varargs() {
let args = items(quote! {test1, test2="None", "*", test3="None"}).unwrap();
assert!(
args == vec![
Argument::Arg(parse_quote! {test1}, None),
Argument::Arg(parse_quote! {test2}, Some("None".to_owned())),
Argument::VarArgsSeparator,
Argument::Kwarg(parse_quote! {test3}, "None".to_owned()),
]
);
}
#[test]
fn test_all() {
let args =
items(quote! {test1, test2="None", args="*", test3="None", kwargs="**"}).unwrap();
assert!(
args == vec![
Argument::Arg(parse_quote! {test1}, None),
Argument::Arg(parse_quote! {test2}, Some("None".to_owned())),
Argument::VarArgs(parse_quote! {args}),
Argument::Kwarg(parse_quote! {test3}, "None".to_owned()),
Argument::KeywordArgs(parse_quote! {kwargs}),
]
);
}
}

View File

@ -1,17 +1,22 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::py_method;
use crate::pymethod;
use proc_macro2::TokenStream;
use quote::quote;
use syn;
pub fn build_py_methods(ast: &mut syn::ItemImpl) -> TokenStream {
if ast.trait_.is_some() {
panic!("#[pymethods] can not be used only with trait impl block");
pub fn build_py_methods(ast: &mut syn::ItemImpl) -> syn::Result<TokenStream> {
if let Some((_, ref path, _)) = ast.trait_ {
Err(syn::Error::new_spanned(
path,
"#[pymethods] can not be used only with trait impl block",
))
} else if ast.generics != Default::default() {
panic!("#[pymethods] can not ve used with lifetime parameters or generics");
Err(syn::Error::new_spanned(
ast.generics.clone(),
"#[pymethods] can not ve used with lifetime parameters or generics",
))
} else {
impl_methods(&ast.self_ty, &mut ast.items)
Ok(impl_methods(&ast.self_ty, &mut ast.items))
}
}
@ -21,7 +26,7 @@ pub fn impl_methods(ty: &syn::Type, impls: &mut Vec<syn::ImplItem>) -> TokenStre
for iimpl in impls.iter_mut() {
if let syn::ImplItem::Method(ref mut meth) = iimpl {
let name = meth.sig.ident.clone();
methods.push(py_method::gen_py_method(
methods.push(pymethod::gen_py_method(
ty,
&name,
&mut meth.sig,

View File

@ -4,10 +4,8 @@ use crate::method::{FnArg, FnSpec, FnType};
use crate::utils;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use quote::ToTokens;
use syn;
pub fn gen_py_method<'a>(
pub fn gen_py_method(
cls: &syn::Type,
name: &syn::Ident,
sig: &mut syn::MethodSig,
@ -16,7 +14,7 @@ pub fn gen_py_method<'a>(
check_generic(name, sig);
let doc = utils::get_doc(&meth_attrs, true);
let spec = FnSpec::parse(name, sig, meth_attrs);
let spec = FnSpec::parse(name, sig, meth_attrs).unwrap();
match spec.tp {
FnType::Fn => impl_py_method_def(name, doc, &spec, &impl_wrap(cls, name, &spec, true)),
@ -42,15 +40,6 @@ fn check_generic(name: &syn::Ident, sig: &syn::MethodSig) {
}
}
pub fn body_to_result(body: &TokenStream, spec: &FnSpec<'_>) -> TokenStream {
let output = &spec.output;
quote! {
let _result: ::pyo3::PyResult<<#output as ::pyo3::ReturnTypeIntoPyResult>::Inner> = {
#body
};
}
}
/// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords)
pub fn impl_wrap(
cls: &syn::Type,
@ -61,11 +50,10 @@ pub fn impl_wrap(
let body = impl_call(cls, name, &spec);
if spec.args.is_empty() && noargs {
let body_to_result = body_to_result(&body, spec);
quote! {
unsafe extern "C" fn __wrap(
_slf: *mut ::pyo3::ffi::PyObject) -> *mut ::pyo3::ffi::PyObject
_slf: *mut ::pyo3::ffi::PyObject
) -> *mut ::pyo3::ffi::PyObject
{
const _LOCATION: &'static str = concat!(
stringify!(#cls), ".", stringify!(#name), "()");
@ -73,14 +61,16 @@ pub fn impl_wrap(
let _py = ::pyo3::Python::assume_gil_acquired();
let _slf = _py.mut_from_borrowed_ptr::<#cls>(_slf);
#body_to_result
let _result = {
::pyo3::IntoPyResult::into_py_result(#body)
};
::pyo3::callback::cb_convert(
::pyo3::callback::PyObjectCallbackConverter, _py, _result)
}
}
} else {
let body = impl_arg_params(&spec, body);
let body_to_result = body_to_result(&body, spec);
quote! {
unsafe extern "C" fn __wrap(
@ -96,7 +86,8 @@ pub fn impl_wrap(
let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args);
let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
#body_to_result
#body
::pyo3::callback::cb_convert(
::pyo3::callback::PyObjectCallbackConverter, _py, _result)
}
@ -123,9 +114,8 @@ pub fn impl_proto_wrap(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) ->
let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args);
let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
let _result = {
#body
};
#body
::pyo3::callback::cb_convert(
::pyo3::callback::PyObjectCallbackConverter, _py, _result)
}
@ -134,24 +124,10 @@ pub fn impl_proto_wrap(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) ->
/// Generate class method wrapper (PyCFunction, PyCFunctionWithKeywords)
pub fn impl_wrap_new(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -> TokenStream {
let names: Vec<syn::Ident> = spec
.args
.iter()
.enumerate()
.map(|item| {
if item.1.py {
syn::Ident::new("_py", Span::call_site())
} else {
syn::Ident::new(&format!("arg{}", item.0), Span::call_site())
}
})
.collect();
let cb = quote! {
::pyo3::ReturnTypeIntoPyResult::return_type_into_py_result(#cls::#name(&_obj, #(#names),*))
};
let names: Vec<syn::Ident> = get_arg_names(&spec);
let cb = quote! { #cls::#name(&_obj, #(#names),*) };
let body = impl_arg_params(spec, cb);
let body_to_result = body_to_result(&body, spec);
quote! {
#[allow(unused_mut)]
@ -170,7 +146,7 @@ pub fn impl_wrap_new(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -> T
let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args);
let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
#body_to_result
#body
match _result {
Ok(_) => ::pyo3::IntoPyPointer::into_ptr(_obj),
@ -200,7 +176,6 @@ fn impl_wrap_init(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -> Toke
}
let body = impl_arg_params(&spec, cb);
let body_to_result = body_to_result(&body, spec);
quote! {
#[allow(unused_mut)]
@ -216,7 +191,8 @@ fn impl_wrap_init(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -> Toke
let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args);
let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
#body_to_result
#body
match _result {
Ok(_) => 0,
Err(e) => {
@ -230,24 +206,10 @@ fn impl_wrap_init(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -> Toke
/// Generate class method wrapper (PyCFunction, PyCFunctionWithKeywords)
pub fn impl_wrap_class(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -> TokenStream {
let names: Vec<syn::Ident> = spec
.args
.iter()
.enumerate()
.map(|item| {
if item.1.py {
syn::Ident::new("_py", Span::call_site())
} else {
syn::Ident::new(&format!("arg{}", item.0), Span::call_site())
}
})
.collect();
let cb = quote! {
::pyo3::ReturnTypeIntoPyResult::return_type_into_py_result(#cls::#name(&_cls, #(#names),*))
};
let names: Vec<syn::Ident> = get_arg_names(&spec);
let cb = quote! { #cls::#name(&_cls, #(#names),*) };
let body = impl_arg_params(spec, cb);
let body_to_result = body_to_result(&body, spec);
quote! {
#[allow(unused_mut)]
@ -263,7 +225,8 @@ pub fn impl_wrap_class(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) ->
let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args);
let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
#body_to_result
#body
::pyo3::callback::cb_convert(
::pyo3::callback::PyObjectCallbackConverter, _py, _result)
}
@ -272,24 +235,10 @@ pub fn impl_wrap_class(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) ->
/// Generate static method wrapper (PyCFunction, PyCFunctionWithKeywords)
pub fn impl_wrap_static(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -> TokenStream {
let names: Vec<syn::Ident> = spec
.args
.iter()
.enumerate()
.map(|item| {
if item.1.py {
syn::Ident::new("_py", Span::call_site())
} else {
syn::Ident::new(&format!("arg{}", item.0), Span::call_site())
}
})
.collect();
let cb = quote! {
::pyo3::ReturnTypeIntoPyResult::return_type_into_py_result(#cls::#name(#(#names),*))
};
let names: Vec<syn::Ident> = get_arg_names(&spec);
let cb = quote! { #cls::#name(#(#names),*) };
let body = impl_arg_params(spec, cb);
let body_to_result = body_to_result(&body, spec);
quote! {
#[allow(unused_mut)]
@ -304,7 +253,8 @@ pub fn impl_wrap_static(cls: &syn::Type, name: &syn::Ident, spec: &FnSpec<'_>) -
let _args = _py.from_borrowed_ptr::<::pyo3::types::PyTuple>(_args);
let _kwargs: Option<&::pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
#body_to_result
#body
::pyo3::callback::cb_convert(
::pyo3::callback::PyObjectCallbackConverter, _py, _result)
}
@ -342,7 +292,7 @@ pub(crate) fn impl_wrap_setter(
name: &syn::Ident,
spec: &FnSpec<'_>,
) -> TokenStream {
if spec.args.len() < 1 {
if spec.args.is_empty() {
println!(
"Not enough arguments for setter {}::{}",
quote! {#cls},
@ -378,197 +328,162 @@ pub(crate) fn impl_wrap_setter(
}
}
fn impl_call(_cls: &syn::Type, fname: &syn::Ident, spec: &FnSpec<'_>) -> TokenStream {
let names: Vec<syn::Ident> = spec
.args
/// This function abstracts away some copied code and can propably be simplified itself
pub fn get_arg_names(spec: &FnSpec) -> Vec<syn::Ident> {
spec.args
.iter()
.enumerate()
.map(|item| {
if item.1.py {
syn::Ident::new("_py", Span::call_site())
} else {
syn::Ident::new(&format!("arg{}", item.0), Span::call_site())
}
})
.collect();
quote! {
::pyo3::ReturnTypeIntoPyResult::return_type_into_py_result(_slf.#fname(#(#names),*))
.map(|(pos, _)| syn::Ident::new(&format!("arg{}", pos), Span::call_site()))
.collect()
}
fn impl_call(_cls: &syn::Type, fname: &syn::Ident, spec: &FnSpec<'_>) -> TokenStream {
let names = get_arg_names(spec);
quote! { _slf.#fname(#(#names),*) }
}
/// Converts a bool to "true" or "false"
fn bool_to_ident(condition: bool) -> syn::Ident {
if condition {
syn::Ident::new("true", Span::call_site())
} else {
syn::Ident::new("false", Span::call_site())
}
}
pub fn impl_arg_params(spec: &FnSpec<'_>, body: TokenStream) -> TokenStream {
let args: Vec<FnArg<'_>> = spec
.args
.iter()
.filter(|item| !item.py)
.map(|item| item.clone())
.collect();
if args.is_empty() {
return body;
if spec.args.is_empty() {
return quote! {
let _result = {
::pyo3::IntoPyResult::into_py_result(#body)
};
};
}
let mut params = Vec::new();
for arg in spec.args.iter() {
if arg.py {
if arg.py || spec.is_args(&arg.name) || spec.is_kwargs(&arg.name) {
continue;
}
if !(spec.is_args(&arg.name) || spec.is_kwargs(&arg.name)) {
let name = arg.name;
let kwonly = if spec.is_kw_only(&arg.name) {
syn::Ident::new("true", Span::call_site())
} else {
syn::Ident::new("false", Span::call_site())
};
let name = arg.name;
let kwonly = bool_to_ident(spec.is_kw_only(&arg.name));
let opt = bool_to_ident(arg.optional.is_some() || spec.default_value(&arg.name).is_some());
let opt = if let Some(_) = arg.optional {
syn::Ident::new("true", Span::call_site())
} else if let Some(_) = spec.default_value(&arg.name) {
syn::Ident::new("true", Span::call_site())
} else {
syn::Ident::new("false", Span::call_site())
};
params.push(quote! {
::pyo3::derive_utils::ParamDescription{
name: stringify!(#name), is_optional: #opt, kw_only: #kwonly}
});
}
params.push(quote! {
::pyo3::derive_utils::ParamDescription {
name: stringify!(#name),
is_optional: #opt,
kw_only: #kwonly
}
});
}
let placeholders: Vec<syn::Ident> = params
.iter()
.map(|_| syn::Ident::new("None", Span::call_site()))
.collect();
// generate extrat args
let len = spec.args.len();
let mut rargs = spec.args.clone();
rargs.reverse();
let mut body = body;
for (idx, arg) in rargs.iter().enumerate() {
body = impl_arg_param(&arg, &spec, &body, len - idx - 1);
let mut param_conversion = Vec::new();
let mut option_pos = 0;
for (idx, arg) in spec.args.iter().enumerate() {
param_conversion.push(impl_arg_param(&arg, &spec, idx, &mut option_pos));
}
let accept_args = syn::Ident::new(
if spec.accept_args() { "true" } else { "false" },
Span::call_site(),
);
let accept_kwargs = syn::Ident::new(
if spec.accept_kwargs() {
"true"
} else {
"false"
},
Span::call_site(),
);
let accept_args = bool_to_ident(spec.accept_args());
let accept_kwargs = bool_to_ident(spec.accept_kwargs());
// create array of arguments, and then parse
quote! {
const _PARAMS: &'static [::pyo3::derive_utils::ParamDescription<'static>] = &[
use ::pyo3::ObjectProtocol;
const PARAMS: &'static [::pyo3::derive_utils::ParamDescription] = &[
#(#params),*
];
let mut _output = [#(#placeholders),*];
match ::pyo3::derive_utils::parse_fn_args(Some(_LOCATION), _PARAMS, &_args,
_kwargs, #accept_args, #accept_kwargs, &mut _output)
{
Ok(_) => {
let mut _iter = _output.iter();
let mut output = [#(#placeholders),*];
#body
},
Err(err) => Err(err)
}
// Workaround to use the question mark operator without rewriting everything
let _result = (|| {
::pyo3::derive_utils::parse_fn_args(
Some(_LOCATION),
PARAMS,
&_args,
_kwargs,
#accept_args,
#accept_kwargs,
&mut output
)?;
#(#param_conversion)*
::pyo3::IntoPyResult::into_py_result(#body)
})();
}
}
/// Re option_pos: The option slice doesn't contain the py: Python argument, so the argument
/// index and the index in option diverge when using py: Python
fn impl_arg_param(
arg: &FnArg<'_>,
spec: &FnSpec<'_>,
body: &TokenStream,
idx: usize,
option_pos: &mut usize,
) -> TokenStream {
if arg.py {
return body.clone();
}
let ty = arg.ty;
let name = arg.name;
let arg_name = syn::Ident::new(&format!("arg{}", idx), Span::call_site());
// First unwrap() asserts the iterated sequence is long enough (which should be guaranteed);
// second unwrap() asserts the parameter was not missing (which fn
// parse_args already checked for).
if arg.py {
return quote! {
let #arg_name = _py;
};
}
let arg_value = quote!(output[#option_pos]);
*option_pos += 1;
let ty = arg.ty;
let name = arg.name;
if spec.is_args(&name) {
quote! {
<#ty as ::pyo3::FromPyObject>::extract(_args.as_ref())
.and_then(|#arg_name| {
#body
})
let #arg_name = <#ty as ::pyo3::FromPyObject>::extract(_args.as_ref())?;
}
} else if spec.is_kwargs(&name) {
quote! {{
quote! {
let #arg_name = _kwargs;
#body
}}
} else {
if let Some(_) = arg.optional {
// default value
let mut default = TokenStream::new();
if let Some(d) = spec.default_value(name) {
let dt = quote! { Some(#d) };
dt.to_tokens(&mut default);
} else {
syn::Ident::new("None", Span::call_site()).to_tokens(&mut default);
}
quote! {
match
match _iter.next().unwrap().as_ref() {
Some(_obj) => {
if _obj.is_none() {
Ok(#default)
} else {
match _obj.extract() {
Ok(_obj) => Ok(Some(_obj)),
Err(e) => Err(e)
}
}
},
None => Ok(#default)
}
{
Ok(#arg_name) => #body,
Err(e) => Err(e)
}
}
} else if let Some(default) = spec.default_value(name) {
quote! {
match match _iter.next().unwrap().as_ref() {
Some(_obj) => {
if _obj.is_none() {
Ok(#default)
} else {
match _obj.extract() {
Ok(_obj) => Ok(_obj),
Err(e) => Err(e),
}
}
},
None => Ok(#default)
} {
Ok(#arg_name) => #body,
Err(e) => Err(e)
}
}
}
} else if arg.optional.is_some() {
let default = if let Some(d) = spec.default_value(name) {
quote! { Some(#d) }
} else {
quote! {
::pyo3::ObjectProtocol::extract(_iter.next().unwrap().unwrap())
.and_then(|#arg_name| {
#body
})
}
quote! { None }
};
quote! {
let #arg_name = match #arg_value.as_ref() {
Some(_obj) => {
if _obj.is_none() {
#default
} else {
Some(_obj.extract()?)
}
},
None => #default
};
}
} else if let Some(default) = spec.default_value(name) {
quote! {
let #arg_name = match #arg_value.as_ref() {
Some(_obj) => {
if _obj.is_none() {
#default
} else {
_obj.extract()?
}
},
None => #default
};
}
} else {
quote! {
let #arg_name = #arg_value.unwrap().extract()?;
}
}
}
@ -710,7 +625,7 @@ pub(crate) fn impl_py_setter_def(
setter: &Option<String>,
wrapper: &TokenStream,
) -> TokenStream {
let n = if let &Some(ref name) = setter {
let n = if let Some(ref name) = setter {
name.to_string()
} else {
let n = name.to_string();
@ -740,7 +655,7 @@ pub(crate) fn impl_py_getter_def(
getter: &Option<String>,
wrapper: &TokenStream,
) -> TokenStream {
let n = if let &Some(ref name) = getter {
let n = if let Some(ref name) = getter {
name.to_string()
} else {
let n = name.to_string();

View File

@ -0,0 +1,109 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use crate::defs;
use crate::func::impl_method_proto;
use crate::method::FnSpec;
use crate::pymethod;
use proc_macro2::TokenStream;
use quote::quote;
use quote::ToTokens;
pub fn build_py_proto(ast: &mut syn::ItemImpl) -> syn::Result<TokenStream> {
if let Some((_, ref mut path, _)) = ast.trait_ {
let proto = if let Some(ref mut segment) = path.segments.last() {
match segment.value().ident.to_string().as_str() {
"PyObjectProtocol" => &defs::OBJECT,
"PyAsyncProtocol" => &defs::ASYNC,
"PyMappingProtocol" => &defs::MAPPING,
"PyIterProtocol" => &defs::ITER,
"PyContextProtocol" => &defs::CONTEXT,
"PySequenceProtocol" => &defs::SEQ,
"PyNumberProtocol" => &defs::NUM,
"PyDescrProtocol" => &defs::DESCR,
"PyBufferProtocol" => &defs::BUFFER,
"PyGCProtocol" => &defs::GC,
_ => {
return Err(syn::Error::new_spanned(
path,
"#[pyproto] can not be used with this block",
));
}
}
} else {
return Err(syn::Error::new_spanned(
path,
"#[pyproto] can only be used with protocol trait implementations",
));
};
let tokens = impl_proto_impl(&ast.self_ty, &mut ast.items, proto);
// attach lifetime
let mut seg = path.segments.pop().unwrap().into_value();
seg.arguments = syn::PathArguments::AngleBracketed(syn::parse_quote! {<'p>});
path.segments.push(seg);
ast.generics.params = syn::parse_quote! {'p};
Ok(tokens)
} else {
return Err(syn::Error::new_spanned(
ast,
"#[pyproto] can only be used with protocol trait implementations",
));
}
}
fn impl_proto_impl(
ty: &syn::Type,
impls: &mut Vec<syn::ImplItem>,
proto: &defs::Proto,
) -> TokenStream {
let mut tokens = TokenStream::new();
let mut py_methods = Vec::new();
for iimpl in impls.iter_mut() {
if let syn::ImplItem::Method(ref mut met) = iimpl {
for m in proto.methods {
if m == met.sig.ident.to_string().as_str() {
impl_method_proto(ty, &mut met.sig, m).to_tokens(&mut tokens);
}
}
for m in proto.py_methods {
let ident = met.sig.ident.clone();
if m.name == ident.to_string().as_str() {
let name: syn::Ident = syn::parse_str(m.name).unwrap();
let proto: syn::Path = syn::parse_str(m.proto).unwrap();
let fn_spec = match FnSpec::parse(&ident, &met.sig, &mut met.attrs) {
Ok(fn_spec) => fn_spec,
Err(err) => return err.to_compile_error(),
};
let meth = pymethod::impl_proto_wrap(ty, &ident, &fn_spec);
py_methods.push(quote! {
impl #proto for #ty
{
#[inline]
fn #name() -> Option<::pyo3::class::methods::PyMethodDef> {
#meth
Some(::pyo3::class::PyMethodDef {
ml_name: stringify!(#name),
ml_meth: ::pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
ml_flags: ::pyo3::ffi::METH_VARARGS | ::pyo3::ffi::METH_KEYWORDS,
ml_doc: ""
})
}
}
});
}
}
}
}
quote! {
#tokens
#(#py_methods)*
}
}

View File

@ -1,5 +1,4 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use syn;
use proc_macro2::TokenStream;
@ -8,7 +7,7 @@ pub fn print_err(msg: String, t: TokenStream) {
}
// FIXME(althonos): not sure the docstring formatting is on par here.
pub fn get_doc(attrs: &Vec<syn::Attribute>, null_terminated: bool) -> syn::Lit {
pub fn get_doc(attrs: &[syn::Attribute], null_terminated: bool) -> syn::Lit {
let mut doc = Vec::new();
// TODO(althonos): set span on produced doc str literal
@ -20,13 +19,13 @@ pub fn get_doc(attrs: &Vec<syn::Attribute>, null_terminated: bool) -> syn::Lit {
// span = Some(metanv.span());
if let syn::Lit::Str(ref litstr) = metanv.lit {
let d = litstr.value();
doc.push(if d.starts_with(" ") {
doc.push(if d.starts_with(' ') {
d[1..d.len()].to_string()
} else {
d
});
} else {
panic!("could not parse doc");
panic!("Invalid doc comment");
}
}
}

View File

@ -5,30 +5,26 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::Span;
use pyo3_derive_backend::{module, py_class, py_impl, py_proto, utils};
use pyo3_derive_backend::{
add_fn_to_module, build_py_class, build_py_methods, build_py_proto, get_doc,
process_functions_in_module, py2_init, py3_init, PyClassArgs, PyFunctionAttr,
};
use quote::quote;
use syn;
use syn::parse::Parser;
use syn::punctuated::Punctuated;
use syn::Token;
use syn::parse_macro_input;
#[proc_macro_attribute]
pub fn pymodule2(attr: TokenStream, input: TokenStream) -> TokenStream {
// Parse the token stream into a syntax tree
let mut ast: syn::ItemFn = syn::parse(input).expect("#[pymodule] must be used on a function");
let mut ast = parse_macro_input!(input as syn::ItemFn);
let modname: syn::Ident;
if attr.is_empty() {
modname = ast.ident.clone();
let modname = if attr.is_empty() {
ast.ident.clone()
} else {
modname = syn::parse(attr).expect("could not parse module name");
}
parse_macro_input!(attr as syn::Ident)
};
// Process the functions within the module
module::process_functions_in_module(&mut ast);
process_functions_in_module(&mut ast);
// Create the module initialisation function
let expanded = module::py2_init(&ast.ident, &modname, utils::get_doc(&ast.attrs, false));
let expanded = py2_init(&ast.ident, &modname, get_doc(&ast.attrs, false));
quote!(
#ast
@ -37,23 +33,21 @@ pub fn pymodule2(attr: TokenStream, input: TokenStream) -> TokenStream {
.into()
}
/// Internally, this proc macro create a new c function called `PyInit_{my_module}`
/// that then calls the init function you provided
#[proc_macro_attribute]
pub fn pymodule3(attr: TokenStream, input: TokenStream) -> TokenStream {
// Parse the token stream into a syntax tree
let mut ast: syn::ItemFn = syn::parse(input).expect("#[pymodule] must be used on a `fn` block");
let mut ast = parse_macro_input!(input as syn::ItemFn);
let modname: syn::Ident;
if attr.is_empty() {
modname = ast.ident.clone();
let modname = if attr.is_empty() {
ast.ident.clone()
} else {
modname = syn::parse(attr).expect("could not parse module name");
}
parse_macro_input!(attr as syn::Ident)
};
// Process the functions within the module
module::process_functions_in_module(&mut ast);
process_functions_in_module(&mut ast);
// Create the module initialisation function
let expanded = module::py3_init(&ast.ident, &modname, utils::get_doc(&ast.attrs, false));
let expanded = py3_init(&ast.ident, &modname, get_doc(&ast.attrs, false));
quote!(
#ast
@ -64,12 +58,8 @@ pub fn pymodule3(attr: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
pub fn pyproto(_: TokenStream, input: TokenStream) -> TokenStream {
// Parse the token stream into a syntax tree
let mut ast: syn::ItemImpl =
syn::parse(input).expect("#[pyproto] must be used on an `impl` block");
// Build the output
let expanded = py_proto::build_py_proto(&mut ast);
let mut ast = parse_macro_input!(input as syn::ItemImpl);
let expanded = build_py_proto(&mut ast).unwrap_or_else(|e| e.to_compile_error());
quote!(
#ast
@ -80,21 +70,9 @@ pub fn pyproto(_: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
pub fn pyclass(attr: TokenStream, input: TokenStream) -> TokenStream {
// Parse the token stream into a syntax tree
let mut ast: syn::ItemStruct =
syn::parse(input).expect("#[pyclass] must be used on a `struct`");
// Parse the macro arguments into a list of expressions
let parser = Punctuated::<syn::Expr, Token![,]>::parse_terminated;
let error_message = "The macro attributes should be a list of comma separated expressions";
let args = parser
.parse(attr)
.expect(error_message)
.into_iter()
.collect();
// Build the output
let expanded = py_class::build_py_class(&mut ast, &args);
let mut ast = parse_macro_input!(input as syn::ItemStruct);
let args = parse_macro_input!(attr as PyClassArgs);
let expanded = build_py_class(&mut ast, &args).unwrap_or_else(|e| e.to_compile_error());
quote!(
#ast
@ -105,12 +83,8 @@ pub fn pyclass(attr: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
pub fn pymethods(_: TokenStream, input: TokenStream) -> TokenStream {
// Parse the token stream into a syntax tree
let mut ast: syn::ItemImpl =
syn::parse(input.clone()).expect("#[pymethods] must be used on an `impl` block");
// Build the output
let expanded = py_impl::build_py_methods(&mut ast);
let mut ast = parse_macro_input!(input as syn::ItemImpl);
let expanded = build_py_methods(&mut ast).unwrap_or_else(|e| e.to_compile_error());
quote!(
#ast
@ -120,15 +94,16 @@ pub fn pymethods(_: TokenStream, input: TokenStream) -> TokenStream {
}
#[proc_macro_attribute]
pub fn pyfunction(_: TokenStream, input: TokenStream) -> TokenStream {
let mut ast: syn::ItemFn = syn::parse(input).expect("#[function] must be used on a `fn` block");
pub fn pyfunction(attr: TokenStream, input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as syn::ItemFn);
let args = parse_macro_input!(attr as PyFunctionAttr);
// Workaround for https://github.com/dtolnay/syn/issues/478
let python_name = syn::Ident::new(
&ast.ident.to_string().trim_start_matches("r#"),
Span::call_site(),
);
let expanded = module::add_fn_to_module(&mut ast, &python_name, Vec::new());
let expanded = add_fn_to_module(&ast, &python_name, args.arguments);
quote!(
#ast

View File

@ -17,16 +17,15 @@
// DEALINGS IN THE SOFTWARE.
//! `PyBuffer` implementation
use libc;
use std::ffi::CStr;
use std::os::raw;
use std::{cell, mem, slice};
use crate::err::{self, PyResult};
use crate::exceptions;
use crate::ffi;
use crate::python::{Python, ToPyPointer};
use crate::types::PyObjectRef;
use libc;
use std::ffi::CStr;
use std::os::raw;
use std::{cell, mem, slice};
/// Allows access to the underlying buffer used by a python object such as `bytes`, `bytearray` or `array.array`.
#[repr(transparent)]

View File

@ -2,14 +2,13 @@
//! Utilities for a Python callable object that invokes a Rust function.
use std::os::raw::c_int;
use std::{isize, ptr};
use crate::conversion::IntoPyObject;
use crate::err::PyResult;
use crate::ffi::{self, Py_hash_t};
use crate::python::{IntoPyPointer, Python};
use crate::types::exceptions::OverflowError;
use std::os::raw::c_int;
use std::{isize, ptr};
pub trait CallbackConverter<S> {
type R;

View File

@ -8,9 +8,6 @@
//! Parts of the documentation are copied from the respective methods from the
//! [typeobj docs](https://docs.python.org/3/c-api/typeobj.html)
use std::os::raw::c_int;
use std::ptr;
use crate::callback::{BoolCallbackConverter, HashConverter, PyObjectCallbackConverter};
use crate::class::methods::PyMethodDef;
use crate::conversion::{FromPyObject, IntoPyObject};
@ -21,6 +18,8 @@ use crate::python::{IntoPyPointer, Python};
use crate::typeob::PyTypeInfo;
use crate::types::{exceptions, PyObjectRef};
use crate::CompareOp;
use std::os::raw::c_int;
use std::ptr;
/// Basic python class customization
#[allow(unused_variables)]

View File

@ -4,12 +4,11 @@
//!
//! For more information check [buffer protocol](https://docs.python.org/3/c-api/buffer.html)
//! c-api
use std::os::raw::c_int;
use crate::callback::UnitCallbackConverter;
use crate::err::PyResult;
use crate::ffi;
use crate::typeob::PyTypeInfo;
use std::os::raw::c_int;
/// Buffer protocol interface
///

View File

@ -5,8 +5,6 @@
//! [Python information](
//! https://docs.python.org/3/reference/datamodel.html#implementing-descriptors)
use std::os::raw::c_int;
use crate::callback::{PyObjectCallbackConverter, UnitCallbackConverter};
use crate::class::methods::PyMethodDef;
use crate::conversion::{FromPyObject, IntoPyObject};
@ -14,6 +12,7 @@ use crate::err::PyResult;
use crate::ffi;
use crate::typeob::PyTypeInfo;
use crate::types::{PyObjectRef, PyType};
use std::os::raw::c_int;
/// Descriptor interface
#[allow(unused_variables)]

View File

@ -3,11 +3,10 @@
//! Python GC support
//!
use std::os::raw::{c_int, c_void};
use crate::ffi;
use crate::python::{Python, ToPyPointer};
use crate::typeob::PyTypeInfo;
use std::os::raw::{c_int, c_void};
#[repr(transparent)]
pub struct PyTraverseError(c_int);

View File

@ -2,8 +2,6 @@
//! Python Iterator Interface.
//! Trait and support implementation for implementing iterators
use std::ptr;
use crate::callback::{CallbackConverter, PyObjectCallbackConverter};
use crate::conversion::IntoPyObject;
use crate::err::PyResult;
@ -11,6 +9,7 @@ use crate::ffi;
use crate::instance::PyRefMut;
use crate::python::{IntoPyPointer, Python};
use crate::typeob::PyTypeInfo;
use std::ptr;
/// Python Iterator Interface.
///

View File

@ -29,7 +29,6 @@ pub use self::sequence::PySequenceProtocol;
pub use self::gc::{PyGCProtocol, PyTraverseError, PyVisit};
pub use self::methods::{PyGetterDef, PyMethodDef, PyMethodDefType, PyMethodType, PySetterDef};
use crate::ffi;
/// Operators for the __richcmp__ method

View File

@ -339,29 +339,24 @@ where
}
}
#[doc(hidden)]
/// This trait wraps a T: IntoPyObject into PyResult<T> while PyResult<T> remains PyResult<T>.
///
/// This is necessaty because proc macros run before typechecking and can't decide
/// This is necessary because proc macros run before typechecking and can't decide
/// whether a return type is a (possibly aliased) PyResult or not. It is also quite handy because
/// the codegen is currently built on the assumption that all functions return a PyResult.
pub trait ReturnTypeIntoPyResult {
type Inner;
fn return_type_into_py_result(self) -> PyResult<Self::Inner>;
pub trait IntoPyResult<T> {
fn into_py_result(self) -> PyResult<T>;
}
impl<T: IntoPyObject> ReturnTypeIntoPyResult for T {
type Inner = T;
fn return_type_into_py_result(self) -> PyResult<Self::Inner> {
impl<T: IntoPyObject> IntoPyResult<T> for T {
fn into_py_result(self) -> PyResult<T> {
Ok(self)
}
}
impl<T: IntoPyObject> ReturnTypeIntoPyResult for PyResult<T> {
type Inner = T;
fn return_type_into_py_result(self) -> PyResult<Self::Inner> {
impl<T: IntoPyObject> IntoPyResult<T> for PyResult<T> {
fn into_py_result(self) -> PyResult<T> {
self
}
}

View File

@ -15,11 +15,11 @@ use crate::GILPool;
use crate::Python;
use std::ptr;
#[derive(Debug)]
/// Description of a python parameter; used for `parse_args()`.
pub struct ParamDescription<'a> {
#[derive(Debug)]
pub struct ParamDescription {
/// The name of the parameter.
pub name: &'a str,
pub name: &'static str,
/// Whether the parameter is optional.
pub is_optional: bool,
/// Whether the parameter is optional.

View File

@ -129,8 +129,8 @@
pub use crate::class::*;
pub use crate::conversion::{
FromPyObject, IntoPy, IntoPyObject, PyTryFrom, PyTryInto, ReturnTypeIntoPyResult,
ToBorrowedObject, ToPyObject,
FromPyObject, IntoPy, IntoPyObject, IntoPyResult, PyTryFrom, PyTryInto, ToBorrowedObject,
ToPyObject,
};
pub use crate::err::{PyDowncastError, PyErr, PyErrArguments, PyErrValue, PyResult};
pub use crate::instance::{AsPyRef, Py, PyNativeType, PyObjectWithGIL, PyRef, PyRefMut};

View File

@ -1,7 +1,5 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use std::ptr::NonNull;
use crate::conversion::{
FromPyObject, IntoPy, IntoPyObject, PyTryFrom, ToBorrowedObject, ToPyObject,
};
@ -12,6 +10,7 @@ use crate::python::{IntoPyPointer, Python, ToPyPointer};
use crate::pythonrun;
use crate::types::{PyDict, PyObjectRef, PyTuple};
use crate::Py;
use std::ptr::NonNull;
/// A python object
///

View File

@ -83,8 +83,6 @@ impl<'p> Drop for PyIterator<'p> {
#[cfg(test)]
mod tests {
use indoc::indoc;
use crate::conversion::ToPyObject;
use crate::instance::AsPyRef;
use crate::objectprotocol::ObjectProtocol;
@ -92,6 +90,7 @@ mod tests {
use crate::pythonrun::GILPool;
use crate::types::{PyDict, PyList};
use crate::GILGuard;
use indoc::indoc;
#[test]
fn vec_iter() {

View File

@ -1,9 +1,8 @@
//! common macros for num2.rs and num3.rs
use std::os::raw::c_int;
use crate::err::{PyErr, PyResult};
use crate::python::Python;
use std::os::raw::c_int;
pub(super) fn err_if_invalid_value<T: PartialEq + Copy>(
py: Python,

View File

@ -1,13 +1,12 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
use std::os::raw::c_long;
use crate::conversion::ToPyObject;
use crate::err::{PyErr, PyResult};
use crate::ffi::{self, Py_ssize_t};
use crate::instance::PyObjectWithGIL;
use crate::object::PyObject;
use crate::python::{Python, ToPyPointer};
use std::os::raw::c_long;
/// Represents a Python `slice`.
///

View File

@ -203,14 +203,12 @@ impl std::convert::From<Py<PyUnicode>> for Py<PyString> {
#[cfg(test)]
mod test {
use std::borrow::Cow;
use super::PyString;
use crate::conversion::{FromPyObject, PyTryFrom, ToPyObject};
use crate::instance::AsPyRef;
use crate::object::PyObject;
use crate::python::Python;
use super::PyString;
use std::borrow::Cow;
#[test]
fn test_non_bmp() {

View File

@ -2,15 +2,14 @@
//
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
use std::borrow::Cow;
use std::ffi::CStr;
use crate::err::{PyErr, PyResult};
use crate::ffi;
use crate::instance::{Py, PyObjectWithGIL};
use crate::object::PyObject;
use crate::python::{Python, ToPyPointer};
use crate::typeob::{PyTypeInfo, PyTypeObject};
use std::borrow::Cow;
use std::ffi::CStr;
/// Represents a reference to a Python `type object`.
#[repr(transparent)]

View File

@ -1,5 +1,4 @@
use docmatic;
use std::default::Default;
use std::path::{Path, PathBuf};

View File

@ -1,7 +1,5 @@
#![feature(specialization)]
use std::{isize, iter};
use pyo3::class::{
PyContextProtocol, PyIterProtocol, PyMappingProtocol, PyObjectProtocol, PySequenceProtocol,
};
@ -10,6 +8,7 @@ use pyo3::ffi;
use pyo3::prelude::*;
use pyo3::python::ToPyPointer;
use pyo3::types::{PyBytes, PyDict, PyObjectRef, PySlice, PyString, PyType};
use std::{isize, iter};
#[macro_use]
mod common;

View File

@ -42,9 +42,9 @@ fn class_with_properties() {
#[pyclass]
struct GetterSetter {
#[prop(get, set)]
#[pyo3(get, set)]
num: i32,
#[prop(get, set)]
#[pyo3(get, set)]
text: String,
}

View File

@ -7,7 +7,7 @@ mod common;
#[pyclass]
struct BaseClass {
#[prop(get)]
#[pyo3(get)]
val1: usize,
}
@ -41,7 +41,7 @@ impl BaseClass {
#[pyclass(extends=BaseClass)]
struct SubClass {
#[prop(get)]
#[pyo3(get)]
val2: usize,
}

View File

@ -40,7 +40,7 @@ fn mut_ref_arg() {
#[pyclass]
struct PyUsize {
#[prop(get)]
#[pyo3(get)]
pub value: usize,
}