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:
parent
f248aaddbe
commit
d02f7c3aa5
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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),
|
||||
}))
|
||||
|
|
|
@ -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
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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}),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ¶ms.args[params.args.len() - 1] {
|
||||
&syn::GenericArgument::Type(syn::Type::Reference(_)) => return true,
|
||||
_ => (),
|
||||
let last = ¶ms.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 ¶ms.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)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)*
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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}),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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,
|
|
@ -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();
|
|
@ -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)*
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
///
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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
|
||||
///
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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`.
|
||||
///
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use docmatic;
|
||||
|
||||
use std::default::Default;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ fn mut_ref_arg() {
|
|||
|
||||
#[pyclass]
|
||||
struct PyUsize {
|
||||
#[prop(get)]
|
||||
#[pyo3(get)]
|
||||
pub value: usize,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue