add __new__ and __call__ support
This commit is contained in:
parent
eb64aa11ac
commit
201289d6ca
|
@ -17,6 +17,7 @@ mod py_impl;
|
||||||
mod py_proto;
|
mod py_proto;
|
||||||
mod py_method;
|
mod py_method;
|
||||||
mod func;
|
mod func;
|
||||||
|
mod method;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
|
||||||
|
|
340
pyo3cls/src/method.rs
Normal file
340
pyo3cls/src/method.rs
Normal file
|
@ -0,0 +1,340 @@
|
||||||
|
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||||
|
|
||||||
|
use syn;
|
||||||
|
use quote::{Tokens, ToTokens};
|
||||||
|
use utils::for_err_msg;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct FnArg<'a> {
|
||||||
|
pub name: &'a syn::Ident,
|
||||||
|
pub mode: &'a syn::BindingMode,
|
||||||
|
pub ty: &'a syn::Ty,
|
||||||
|
pub optional: Option<&'a syn::Ty>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
|
pub enum FnType {
|
||||||
|
Getter(Option<String>),
|
||||||
|
Setter(Option<String>),
|
||||||
|
Fn,
|
||||||
|
FnNew,
|
||||||
|
FnCall,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum FnAttr {
|
||||||
|
Args(syn::Ident),
|
||||||
|
Kwargs(syn::Ident),
|
||||||
|
Default(syn::Ident, Tokens),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FnSpec<'a> {
|
||||||
|
pub tp: FnType,
|
||||||
|
pub attrs: Vec<FnAttr>,
|
||||||
|
pub args: Vec<FnArg<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FnSpec<'a> {
|
||||||
|
|
||||||
|
pub fn parse(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);
|
||||||
|
|
||||||
|
//let mut has_self = false;
|
||||||
|
let mut py = false;
|
||||||
|
let mut arguments = Vec::new();
|
||||||
|
|
||||||
|
for input in sig.decl.inputs[1..].iter() {
|
||||||
|
match input {
|
||||||
|
&syn::FnArg::SelfRef(_, _) => {
|
||||||
|
//has_self = true;
|
||||||
|
},
|
||||||
|
&syn::FnArg::SelfValue(_) => {
|
||||||
|
//has_self = true;
|
||||||
|
}
|
||||||
|
&syn::FnArg::Captured(ref pat, ref ty) => {
|
||||||
|
let (mode, ident) = match pat {
|
||||||
|
&syn::Pat::Ident(ref mode, ref ident, _) =>
|
||||||
|
(mode, ident),
|
||||||
|
_ =>
|
||||||
|
panic!("unsupported argument: {:?}", pat),
|
||||||
|
};
|
||||||
|
// TODO add check for first py: Python arg
|
||||||
|
if py {
|
||||||
|
let opt = check_arg_ty_and_optional(name, ty);
|
||||||
|
arguments.push(FnArg{name: ident, mode: mode, ty: ty, optional: opt});
|
||||||
|
} else {
|
||||||
|
py = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&syn::FnArg::Ignored(_) =>
|
||||||
|
panic!("ignored argument: {:?}", name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FnSpec {
|
||||||
|
tp: fn_type,
|
||||||
|
attrs: fn_attrs,
|
||||||
|
args: arguments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_args(&self, name: &syn::Ident) -> bool {
|
||||||
|
for s in self.attrs.iter() {
|
||||||
|
match *s {
|
||||||
|
FnAttr::Args(ref ident) =>
|
||||||
|
return name == ident,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn accept_args(&self) -> bool {
|
||||||
|
for s in self.attrs.iter() {
|
||||||
|
match *s {
|
||||||
|
FnAttr::Args(_) => return true,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_kwargs(&self, name: &syn::Ident) -> bool {
|
||||||
|
for s in self.attrs.iter() {
|
||||||
|
match *s {
|
||||||
|
FnAttr::Kwargs(ref ident) =>
|
||||||
|
return name == ident,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn accept_kwargs(&self) -> bool {
|
||||||
|
for s in self.attrs.iter() {
|
||||||
|
match *s {
|
||||||
|
FnAttr::Kwargs(_) => return true,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_value(&self, name: &syn::Ident) -> Option<Tokens> {
|
||||||
|
for s in self.attrs.iter() {
|
||||||
|
match *s {
|
||||||
|
FnAttr::Default(ref ident, ref val) => {
|
||||||
|
if ident == name {
|
||||||
|
return Some(val.clone())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_arg_ty_and_optional<'a>(name: &'a syn::Ident, ty: &'a syn::Ty) -> Option<&'a syn::Ty> {
|
||||||
|
match ty {
|
||||||
|
&syn::Ty::Path(ref qs, ref path) => {
|
||||||
|
if let &Some(ref qs) = qs {
|
||||||
|
panic!("explicit Self type in a 'qualified path' is not supported: {:?} - {:?}",
|
||||||
|
name, qs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(segment) = path.segments.last() {
|
||||||
|
match segment.ident.as_ref() {
|
||||||
|
"Option" => {
|
||||||
|
match segment.parameters {
|
||||||
|
syn::PathParameters::AngleBracketed(ref params) => {
|
||||||
|
if params.types.len() != 1 {
|
||||||
|
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
||||||
|
for_err_msg(name),
|
||||||
|
for_err_msg(ty),
|
||||||
|
for_err_msg(path));
|
||||||
|
}
|
||||||
|
Some(¶ms.types[0])
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
||||||
|
for_err_msg(name),
|
||||||
|
for_err_msg(ty),
|
||||||
|
for_err_msg(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
None
|
||||||
|
//panic!("argument type is not supported by python method: {:?} ({:?})",
|
||||||
|
//for_err_msg(name),
|
||||||
|
//for_err_msg(ty));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<FnAttr>) {
|
||||||
|
let mut new_attrs = Vec::new();
|
||||||
|
let mut spec = Vec::new();
|
||||||
|
let mut res: Option<FnType> = None;
|
||||||
|
|
||||||
|
for attr in attrs.iter() {
|
||||||
|
match attr.value {
|
||||||
|
syn::MetaItem::Word(ref name) => {
|
||||||
|
match name.as_ref() {
|
||||||
|
"new" => {
|
||||||
|
res = Some(FnType::FnNew)
|
||||||
|
},
|
||||||
|
"call" => {
|
||||||
|
res = Some(FnType::FnCall)
|
||||||
|
},
|
||||||
|
"setter" | "getter" => {
|
||||||
|
if attr.style == syn::AttrStyle::Inner {
|
||||||
|
panic!("Inner style attribute is not
|
||||||
|
supported for setter and getter");
|
||||||
|
}
|
||||||
|
if res != None {
|
||||||
|
panic!("setter/getter attribute can not be used mutiple times");
|
||||||
|
}
|
||||||
|
if name.as_ref() == "setter" {
|
||||||
|
res = Some(FnType::Setter(None))
|
||||||
|
} else {
|
||||||
|
res = Some(FnType::Getter(None))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
new_attrs.push(attr.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
syn::MetaItem::List(ref name, ref meta) => {
|
||||||
|
match name.as_ref() {
|
||||||
|
"new" => {
|
||||||
|
res = Some(FnType::FnNew)
|
||||||
|
},
|
||||||
|
"call" => {
|
||||||
|
res = Some(FnType::FnCall)
|
||||||
|
},
|
||||||
|
"setter" | "getter" => {
|
||||||
|
if attr.style == syn::AttrStyle::Inner {
|
||||||
|
panic!("Inner style attribute is not
|
||||||
|
supported for setter and getter");
|
||||||
|
}
|
||||||
|
if res != None {
|
||||||
|
panic!("setter/getter attribute can not be used mutiple times");
|
||||||
|
}
|
||||||
|
if meta.len() != 1 {
|
||||||
|
panic!("setter/getter requires one value");
|
||||||
|
}
|
||||||
|
match *meta.first().unwrap() {
|
||||||
|
syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref w)) => {
|
||||||
|
if name.as_ref() == "setter" {
|
||||||
|
res = Some(FnType::Setter(Some(w.to_string())))
|
||||||
|
} else {
|
||||||
|
res = Some(FnType::Getter(Some(w.to_string())))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
syn::NestedMetaItem::Literal(ref lit) => {
|
||||||
|
match *lit {
|
||||||
|
syn::Lit::Str(ref s, syn::StrStyle::Cooked) => {
|
||||||
|
if name.as_ref() == "setter" {
|
||||||
|
res = Some(FnType::Setter(Some(s.clone())))
|
||||||
|
} else {
|
||||||
|
res = Some(FnType::Getter(Some(s.clone())))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("setter/getter attribute requires str value");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("cannot parse {:?} attribute: {:?}", name, meta);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args" => {
|
||||||
|
spec.extend(parse_args(meta))
|
||||||
|
}
|
||||||
|
"defaults" => {
|
||||||
|
// parse: #[defaults(param2=12, param3=12)]
|
||||||
|
for item in meta.iter() {
|
||||||
|
if let Some(el) = parse_args_default(item) {
|
||||||
|
spec.push(el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
new_attrs.push(attr.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
syn::MetaItem::NameValue(_, _) => {
|
||||||
|
new_attrs.push(attr.clone())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attrs.clear();
|
||||||
|
attrs.extend(new_attrs);
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Some(tp) => (tp, spec),
|
||||||
|
None => (FnType::Fn, spec),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// parse: #[args(args="args", kw="kwargs")]
|
||||||
|
fn parse_args(items: &Vec<syn::NestedMetaItem>) -> Vec<FnAttr> {
|
||||||
|
let mut spec = Vec::new();
|
||||||
|
|
||||||
|
for item in items.iter() {
|
||||||
|
match item {
|
||||||
|
&syn::NestedMetaItem::MetaItem(syn::MetaItem::NameValue(ref ident, ref name)) => {
|
||||||
|
match *name {
|
||||||
|
syn::Lit::Str(ref name, _) => match ident.as_ref() {
|
||||||
|
"args" =>
|
||||||
|
spec.push(FnAttr::Args(syn::Ident::from(name.clone()))),
|
||||||
|
"kw" =>
|
||||||
|
spec.push(FnAttr::Kwargs(syn::Ident::from(name.clone()))),
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spec
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_args_default(item: &syn::NestedMetaItem) -> Option<FnAttr> {
|
||||||
|
match *item {
|
||||||
|
syn::NestedMetaItem::MetaItem(syn::MetaItem::NameValue(ref name, ref lit)) => {
|
||||||
|
let mut t = Tokens::new();
|
||||||
|
match lit {
|
||||||
|
&syn::Lit::Str(ref val, _) => {
|
||||||
|
syn::Ident::from(val.as_str()).to_tokens(&mut t);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
lit.to_tokens(&mut t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(FnAttr::Default(name.clone(), t))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("expected name value {:?}", item);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,223 +4,30 @@ use syn;
|
||||||
use quote::{Tokens, ToTokens};
|
use quote::{Tokens, ToTokens};
|
||||||
use utils::for_err_msg;
|
use utils::for_err_msg;
|
||||||
|
|
||||||
|
use method::{FnArg, FnSpec, FnType};
|
||||||
#[derive(Debug)]
|
|
||||||
struct Arg<'a> {
|
|
||||||
pub name: &'a syn::Ident,
|
|
||||||
pub mode: &'a syn::BindingMode,
|
|
||||||
pub ty: &'a syn::Ty,
|
|
||||||
pub optional: Option<&'a syn::Ty>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
enum FnType {
|
|
||||||
Getter(Option<String>),
|
|
||||||
Setter(Option<String>),
|
|
||||||
Fn,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum FnSpec {
|
|
||||||
Args(syn::Ident),
|
|
||||||
Kwargs(syn::Ident),
|
|
||||||
Default(syn::Ident, Tokens),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn gen_py_method<'a>(cls: &Box<syn::Ty>, name: &syn::Ident,
|
pub fn gen_py_method<'a>(cls: &Box<syn::Ty>, name: &syn::Ident,
|
||||||
sig: &mut syn::MethodSig,
|
sig: &mut syn::MethodSig, meth_attrs: &mut Vec<syn::Attribute>) -> Tokens
|
||||||
meth_attrs: &mut Vec<syn::Attribute>) -> Tokens
|
|
||||||
{
|
{
|
||||||
check_generic(name, sig);
|
check_generic(name, sig);
|
||||||
|
|
||||||
let (fn_type, fn_spec) = parse_attributes(meth_attrs);
|
let spec = FnSpec::parse(name, sig, meth_attrs);
|
||||||
|
|
||||||
//let mut has_self = false;
|
match spec.tp {
|
||||||
let mut py = false;
|
|
||||||
let mut arguments: Vec<Arg> = Vec::new();
|
|
||||||
|
|
||||||
for input in sig.decl.inputs.iter() {
|
|
||||||
match input {
|
|
||||||
&syn::FnArg::SelfRef(_, _) => {
|
|
||||||
//has_self = true;
|
|
||||||
},
|
|
||||||
&syn::FnArg::SelfValue(_) => {
|
|
||||||
//has_self = true;
|
|
||||||
}
|
|
||||||
&syn::FnArg::Captured(ref pat, ref ty) => {
|
|
||||||
let (mode, ident) = match pat {
|
|
||||||
&syn::Pat::Ident(ref mode, ref ident, _) =>
|
|
||||||
(mode, ident),
|
|
||||||
_ =>
|
|
||||||
panic!("unsupported argument: {:?}", pat),
|
|
||||||
};
|
|
||||||
// TODO add check for first py: Python arg
|
|
||||||
if py {
|
|
||||||
let opt = check_arg_ty_and_optional(name, ty);
|
|
||||||
arguments.push(Arg{name: ident, mode: mode, ty: ty, optional: opt});
|
|
||||||
} else {
|
|
||||||
py = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&syn::FnArg::Ignored(_) =>
|
|
||||||
panic!("ignored argument: {:?}", name),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match fn_type {
|
|
||||||
FnType::Fn =>
|
FnType::Fn =>
|
||||||
impl_py_method_def(name, &impl_wrap(cls, name, arguments, fn_spec)),
|
impl_py_method_def(name, &impl_wrap(cls, name, &spec)),
|
||||||
FnType::Getter(getter) =>
|
FnType::FnNew =>
|
||||||
impl_py_getter_def(name, getter, &impl_wrap_getter(cls, name, arguments, fn_spec)),
|
impl_py_method_def_new(name, &impl_wrap_new(cls, name, &spec)),
|
||||||
FnType::Setter(setter) =>
|
FnType::FnCall =>
|
||||||
impl_py_setter_def(name, setter, &impl_wrap_setter(cls, name, arguments, fn_spec)),
|
impl_py_method_def_call(name, &impl_wrap(cls, name, &spec)),
|
||||||
|
FnType::Getter(ref getter) =>
|
||||||
|
impl_py_getter_def(name, getter, &impl_wrap_getter(cls, name, &spec)),
|
||||||
|
FnType::Setter(ref setter) =>
|
||||||
|
impl_py_setter_def(name, setter, &impl_wrap_setter(cls, name, &spec)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_attributes(attrs: &mut Vec<syn::Attribute>) -> (FnType, Vec<FnSpec>) {
|
|
||||||
let mut new_attrs = Vec::new();
|
|
||||||
let mut spec = Vec::new();
|
|
||||||
let mut res: Option<FnType> = None;
|
|
||||||
|
|
||||||
for attr in attrs.iter() {
|
|
||||||
match attr.value {
|
|
||||||
syn::MetaItem::Word(ref name) => {
|
|
||||||
match name.as_ref() {
|
|
||||||
"setter" | "getter" => {
|
|
||||||
if attr.style == syn::AttrStyle::Inner {
|
|
||||||
panic!("Inner style attribute is not
|
|
||||||
supported for setter and getter");
|
|
||||||
}
|
|
||||||
if res != None {
|
|
||||||
panic!("setter/getter attribute can not be used mutiple times");
|
|
||||||
}
|
|
||||||
if name.as_ref() == "setter" {
|
|
||||||
res = Some(FnType::Setter(None))
|
|
||||||
} else {
|
|
||||||
res = Some(FnType::Getter(None))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
new_attrs.push(attr.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
syn::MetaItem::List(ref name, ref meta) => {
|
|
||||||
match name.as_ref() {
|
|
||||||
"setter" | "getter" => {
|
|
||||||
if attr.style == syn::AttrStyle::Inner {
|
|
||||||
panic!("Inner style attribute is not
|
|
||||||
supported for setter and getter");
|
|
||||||
}
|
|
||||||
if res != None {
|
|
||||||
panic!("setter/getter attribute can not be used mutiple times");
|
|
||||||
}
|
|
||||||
if meta.len() != 1 {
|
|
||||||
panic!("setter/getter requires one value");
|
|
||||||
}
|
|
||||||
match *meta.first().unwrap() {
|
|
||||||
syn::NestedMetaItem::MetaItem(syn::MetaItem::Word(ref w)) => {
|
|
||||||
if name.as_ref() == "setter" {
|
|
||||||
res = Some(FnType::Setter(Some(w.to_string())))
|
|
||||||
} else {
|
|
||||||
res = Some(FnType::Getter(Some(w.to_string())))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
syn::NestedMetaItem::Literal(ref lit) => {
|
|
||||||
match *lit {
|
|
||||||
syn::Lit::Str(ref s, syn::StrStyle::Cooked) => {
|
|
||||||
if name.as_ref() == "setter" {
|
|
||||||
res = Some(FnType::Setter(Some(s.clone())))
|
|
||||||
} else {
|
|
||||||
res = Some(FnType::Getter(Some(s.clone())))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
panic!("setter/getter attribute requires str value");
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
println!("cannot parse {:?} attribute: {:?}", name, meta);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"args" => {
|
|
||||||
spec.extend(parse_args(meta))
|
|
||||||
}
|
|
||||||
"defaults" => {
|
|
||||||
// parse: #[defaults(param2=12, param3=12)]
|
|
||||||
for item in meta.iter() {
|
|
||||||
if let Some(el) = parse_args_default(item) {
|
|
||||||
spec.push(el)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
new_attrs.push(attr.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
syn::MetaItem::NameValue(_, _) => {
|
|
||||||
new_attrs.push(attr.clone())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attrs.clear();
|
|
||||||
attrs.extend(new_attrs);
|
|
||||||
|
|
||||||
match res {
|
|
||||||
Some(tp) => (tp, spec),
|
|
||||||
None => (FnType::Fn, spec),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// parse: #[args(args="args", kw="kwargs")]
|
|
||||||
fn parse_args(items: &Vec<syn::NestedMetaItem>) -> Vec<FnSpec> {
|
|
||||||
let mut spec = Vec::new();
|
|
||||||
|
|
||||||
for item in items.iter() {
|
|
||||||
match item {
|
|
||||||
&syn::NestedMetaItem::MetaItem(syn::MetaItem::NameValue(ref ident, ref name)) => {
|
|
||||||
match *name {
|
|
||||||
syn::Lit::Str(ref name, _) => match ident.as_ref() {
|
|
||||||
"args" =>
|
|
||||||
spec.push(FnSpec::Args(syn::Ident::from(name.clone()))),
|
|
||||||
"kw" =>
|
|
||||||
spec.push(FnSpec::Kwargs(syn::Ident::from(name.clone()))),
|
|
||||||
_ => (),
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spec
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_args_default(item: &syn::NestedMetaItem) -> Option<FnSpec> {
|
|
||||||
match *item {
|
|
||||||
syn::NestedMetaItem::MetaItem(syn::MetaItem::NameValue(ref name, ref lit)) => {
|
|
||||||
let mut t = Tokens::new();
|
|
||||||
match lit {
|
|
||||||
&syn::Lit::Str(ref val, _) => {
|
|
||||||
syn::Ident::from(val.as_str()).to_tokens(&mut t);
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
lit.to_tokens(&mut t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(FnSpec::Default(name.clone(), t))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
println!("expected name value {:?}", item);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_generic(name: &syn::Ident, sig: &syn::MethodSig) {
|
fn check_generic(name: &syn::Ident, sig: &syn::MethodSig) {
|
||||||
if !sig.generics.ty_params.is_empty() {
|
if !sig.generics.ty_params.is_empty() {
|
||||||
|
@ -228,60 +35,45 @@ fn check_generic(name: &syn::Ident, sig: &syn::MethodSig) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_arg_ty_and_optional<'a>(name: &'a syn::Ident, ty: &'a syn::Ty) -> Option<&'a syn::Ty> {
|
|
||||||
match ty {
|
|
||||||
&syn::Ty::Path(ref qs, ref path) => {
|
|
||||||
if let &Some(ref qs) = qs {
|
|
||||||
panic!("explicit Self type in a 'qualified path' is not supported: {:?} - {:?}",
|
|
||||||
name, qs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(segment) = path.segments.last() {
|
/// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||||
match segment.ident.as_ref() {
|
pub fn impl_wrap(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||||
"Option" => {
|
let cb = impl_call(cls, name, &spec);
|
||||||
match segment.parameters {
|
let body = impl_arg_params(&spec, cb);
|
||||||
syn::PathParameters::AngleBracketed(ref params) => {
|
|
||||||
if params.types.len() != 1 {
|
quote! {
|
||||||
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
unsafe extern "C" fn wrap(slf: *mut _pyo3::ffi::PyObject,
|
||||||
for_err_msg(name),
|
args: *mut _pyo3::ffi::PyObject,
|
||||||
for_err_msg(ty),
|
kwargs: *mut _pyo3::ffi::PyObject) -> *mut _pyo3::ffi::PyObject
|
||||||
for_err_msg(path));
|
{
|
||||||
|
const LOCATION: &'static str = concat!(
|
||||||
|
stringify!(#cls), ".", stringify!(#name), "()");
|
||||||
|
_pyo3::callback::handle_callback(
|
||||||
|
LOCATION, _pyo3::callback::PyObjectCallbackConverter, |py|
|
||||||
|
{
|
||||||
|
let args: _pyo3::PyTuple =
|
||||||
|
_pyo3::PyObject::from_borrowed_ptr(py, args).unchecked_cast_into();
|
||||||
|
let kwargs: Option<_pyo3::PyDict> = _pyo3::argparse::get_kwargs(py, kwargs);
|
||||||
|
|
||||||
|
let ret = {
|
||||||
|
#body
|
||||||
|
};
|
||||||
|
_pyo3::PyDrop::release_ref(args, py);
|
||||||
|
_pyo3::PyDrop::release_ref(kwargs, py);
|
||||||
|
ret
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Some(¶ms.types[0])
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
panic!("argument type is not supported by python method: {:?} ({:?}) {:?}",
|
|
||||||
for_err_msg(name),
|
|
||||||
for_err_msg(ty),
|
|
||||||
for_err_msg(path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
None
|
|
||||||
//panic!("argument type is not supported by python method: {:?} ({:?})",
|
|
||||||
//for_err_msg(name),
|
|
||||||
//for_err_msg(ty));
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
|
||||||
fn impl_wrap(cls: &Box<syn::Ty>,
|
/// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||||
name: &syn::Ident,
|
pub fn impl_wrap_new(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||||
args: Vec<Arg>, spec: Vec<FnSpec>) -> Tokens {
|
let cb = impl_class_new(cls, name, spec);
|
||||||
let cb = impl_call(cls, name, &args);
|
let body = impl_arg_params(spec, cb);
|
||||||
let body = impl_arg_params(args, &spec, cb);
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
unsafe extern "C" fn wrap
|
unsafe extern "C" fn wrap(cls: *mut _pyo3::ffi::PyTypeObject,
|
||||||
(slf: *mut _pyo3::ffi::PyObject,
|
|
||||||
args: *mut _pyo3::ffi::PyObject,
|
args: *mut _pyo3::ffi::PyObject,
|
||||||
kwargs: *mut _pyo3::ffi::PyObject) -> *mut _pyo3::ffi::PyObject
|
kwargs: *mut _pyo3::ffi::PyObject) -> *mut _pyo3::ffi::PyObject
|
||||||
{
|
{
|
||||||
|
@ -307,8 +99,7 @@ fn impl_wrap(cls: &Box<syn::Ty>,
|
||||||
|
|
||||||
|
|
||||||
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||||
fn impl_wrap_getter(cls: &Box<syn::Ty>,
|
fn impl_wrap_getter(cls: &Box<syn::Ty>, name: &syn::Ident, _spec: &FnSpec) -> Tokens {
|
||||||
name: &syn::Ident, _args: Vec<Arg>, _spec: Vec<FnSpec>) -> Tokens {
|
|
||||||
quote! {
|
quote! {
|
||||||
unsafe extern "C" fn wrap (slf: *mut _pyo3::ffi::PyObject,
|
unsafe extern "C" fn wrap (slf: *mut _pyo3::ffi::PyObject,
|
||||||
_: *mut _pyo3::c_void)
|
_: *mut _pyo3::c_void)
|
||||||
|
@ -330,10 +121,8 @@ fn impl_wrap_getter(cls: &Box<syn::Ty>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||||
fn impl_wrap_setter(cls: &Box<syn::Ty>,
|
fn impl_wrap_setter(cls: &Box<syn::Ty>, name: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||||
name: &syn::Ident, args: Vec<Arg>, _spec: Vec<FnSpec>) -> Tokens {
|
let val_ty = spec.args[0].ty;
|
||||||
|
|
||||||
let val_ty = args[0].ty;
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
unsafe extern "C" fn wrap(slf: *mut _pyo3::ffi::PyObject,
|
unsafe extern "C" fn wrap(slf: *mut _pyo3::ffi::PyObject,
|
||||||
|
@ -365,8 +154,8 @@ fn impl_wrap_setter(cls: &Box<syn::Ty>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn impl_call(cls: &Box<syn::Ty>, fname: &syn::Ident, args: &Vec<Arg>) -> Tokens {
|
fn impl_call(cls: &Box<syn::Ty>, fname: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||||
let names: Vec<&syn::Ident> = args.iter().map(|item| item.name).collect();
|
let names: Vec<&syn::Ident> = spec.args.iter().map(|item| item.name).collect();
|
||||||
quote! {
|
quote! {
|
||||||
{
|
{
|
||||||
let slf = _pyo3::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<#cls>();
|
let slf = _pyo3::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<#cls>();
|
||||||
|
@ -377,16 +166,28 @@ fn impl_call(cls: &Box<syn::Ty>, fname: &syn::Ident, args: &Vec<Arg>) -> Tokens
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_arg_params(mut args: Vec<Arg>, spec: &Vec<FnSpec>, body: Tokens) -> Tokens {
|
fn impl_class_new(cls: &Box<syn::Ty>, fname: &syn::Ident, spec: &FnSpec) -> Tokens {
|
||||||
|
let names: Vec<&syn::Ident> = spec.args.iter().map(|item| item.name).collect();
|
||||||
|
quote! {
|
||||||
|
{
|
||||||
|
let cls = _pyo3::PyType::from_type_ptr(py, cls);
|
||||||
|
let ret = #cls::#fname(&cls, py, #(#names),*);
|
||||||
|
_pyo3::PyDrop::release_ref(cls, py);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_arg_params(spec: &FnSpec, body: Tokens) -> Tokens {
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
|
|
||||||
for arg in args.iter() {
|
for arg in spec.args.iter() {
|
||||||
if ! (is_args(&arg.name, &spec) || is_kwargs(&arg.name, &spec)) {
|
if ! (spec.is_args(&arg.name) || spec.is_kwargs(&arg.name)) {
|
||||||
let name = arg.name.as_ref();
|
let name = arg.name.as_ref();
|
||||||
let opt = if let Some(_) = arg.optional {
|
let opt = if let Some(_) = arg.optional {
|
||||||
syn::Ident::from("true")
|
syn::Ident::from("true")
|
||||||
} else {
|
} else {
|
||||||
if let Some(_) = get_default_value(&arg.name, spec) {
|
if let Some(_) = spec.default_value(&arg.name) {
|
||||||
syn::Ident::from("true")
|
syn::Ident::from("true")
|
||||||
} else {
|
} else {
|
||||||
syn::Ident::from("false")
|
syn::Ident::from("false")
|
||||||
|
@ -403,16 +204,17 @@ fn impl_arg_params(mut args: Vec<Arg>, spec: &Vec<FnSpec>, body: Tokens) -> Toke
|
||||||
|_| syn::Ident::from("None")).collect();
|
|_| syn::Ident::from("None")).collect();
|
||||||
|
|
||||||
// generate extrat args
|
// generate extrat args
|
||||||
args.reverse();
|
let mut rargs = spec.args.clone();
|
||||||
|
rargs.reverse();
|
||||||
let mut body = body;
|
let mut body = body;
|
||||||
for arg in args.iter() {
|
for arg in spec.args.iter() {
|
||||||
body = impl_arg_param(&arg, &spec, &body);
|
body = impl_arg_param(&arg, &spec, &body);
|
||||||
}
|
}
|
||||||
|
|
||||||
let accept_args = syn::Ident::from(
|
let accept_args = syn::Ident::from(
|
||||||
if accept_args(spec) { "true" } else { "false" });
|
if spec.accept_args() { "true" } else { "false" });
|
||||||
let accept_kwargs = syn::Ident::from(
|
let accept_kwargs = syn::Ident::from(
|
||||||
if accept_kwargs(spec) { "true" } else { "false" });
|
if spec.accept_kwargs() { "true" } else { "false" });
|
||||||
|
|
||||||
// create array of arguments, and then parse
|
// create array of arguments, and then parse
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -434,7 +236,7 @@ fn impl_arg_params(mut args: Vec<Arg>, spec: &Vec<FnSpec>, body: Tokens) -> Toke
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_arg_param(arg: &Arg, spec: &Vec<FnSpec>, body: &Tokens) -> Tokens {
|
fn impl_arg_param(arg: &FnArg, spec: &FnSpec, body: &Tokens) -> Tokens {
|
||||||
let ty = arg.ty;
|
let ty = arg.ty;
|
||||||
let name = arg.name;
|
let name = arg.name;
|
||||||
|
|
||||||
|
@ -442,7 +244,7 @@ fn impl_arg_param(arg: &Arg, spec: &Vec<FnSpec>, body: &Tokens) -> Tokens {
|
||||||
// second unwrap() asserts the parameter was not missing (which fn
|
// second unwrap() asserts the parameter was not missing (which fn
|
||||||
// parse_args already checked for).
|
// parse_args already checked for).
|
||||||
|
|
||||||
if is_args(&name, &spec) {
|
if spec.is_args(&name) {
|
||||||
quote! {
|
quote! {
|
||||||
match <#ty as _pyo3::FromPyObject>::extract(py, args.as_object())
|
match <#ty as _pyo3::FromPyObject>::extract(py, args.as_object())
|
||||||
{
|
{
|
||||||
|
@ -453,7 +255,7 @@ fn impl_arg_param(arg: &Arg, spec: &Vec<FnSpec>, body: &Tokens) -> Tokens {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if is_kwargs(&name, &spec) {
|
else if spec.is_kwargs(&name) {
|
||||||
quote! {
|
quote! {
|
||||||
let #name = kwargs.as_ref();
|
let #name = kwargs.as_ref();
|
||||||
#body
|
#body
|
||||||
|
@ -463,7 +265,7 @@ fn impl_arg_param(arg: &Arg, spec: &Vec<FnSpec>, body: &Tokens) -> Tokens {
|
||||||
if let Some(ref opt_ty) = arg.optional {
|
if let Some(ref opt_ty) = arg.optional {
|
||||||
// default value
|
// default value
|
||||||
let mut default = Tokens::new();
|
let mut default = Tokens::new();
|
||||||
if let Some(d) = get_default_value(name, spec) {
|
if let Some(d) = spec.default_value(name) {
|
||||||
let dt = quote!{ Some(#d) };
|
let dt = quote!{ Some(#d) };
|
||||||
dt.to_tokens(&mut default);
|
dt.to_tokens(&mut default);
|
||||||
} else {
|
} else {
|
||||||
|
@ -488,7 +290,7 @@ fn impl_arg_param(arg: &Arg, spec: &Vec<FnSpec>, body: &Tokens) -> Tokens {
|
||||||
Err(e) => Err(e)
|
Err(e) => Err(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(default) = get_default_value(name, spec) {
|
} else if let Some(default) = spec.default_value(name) {
|
||||||
quote! {
|
quote! {
|
||||||
match match _iter.next().unwrap().as_ref() {
|
match match _iter.next().unwrap().as_ref() {
|
||||||
Some(obj) => {
|
Some(obj) => {
|
||||||
|
@ -523,63 +325,7 @@ fn impl_arg_param(arg: &Arg, spec: &Vec<FnSpec>, body: &Tokens) -> Tokens {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_args(name: &syn::Ident, spec: &Vec<FnSpec>) -> bool {
|
pub fn impl_py_method_def(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
|
||||||
for s in spec.iter() {
|
|
||||||
match *s {
|
|
||||||
FnSpec::Args(ref ident) =>
|
|
||||||
return name == ident,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn accept_args(spec: &Vec<FnSpec>) -> bool {
|
|
||||||
for s in spec.iter() {
|
|
||||||
match *s {
|
|
||||||
FnSpec::Args(_) => return true,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_kwargs(name: &syn::Ident, spec: &Vec<FnSpec>) -> bool {
|
|
||||||
for s in spec.iter() {
|
|
||||||
match *s {
|
|
||||||
FnSpec::Kwargs(ref ident) =>
|
|
||||||
return name == ident,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn accept_kwargs(spec: &Vec<FnSpec>) -> bool {
|
|
||||||
for s in spec.iter() {
|
|
||||||
match *s {
|
|
||||||
FnSpec::Kwargs(_) => return true,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_default_value<'a>(name: &syn::Ident, spec: &'a Vec<FnSpec>) -> Option<&'a Tokens> {
|
|
||||||
for s in spec.iter() {
|
|
||||||
match *s {
|
|
||||||
FnSpec::Default(ref ident, ref val) => {
|
|
||||||
if ident == name {
|
|
||||||
return Some(val)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn impl_py_method_def(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
|
|
||||||
quote! {
|
quote! {
|
||||||
_pyo3::class::PyMethodDefType::Method({
|
_pyo3::class::PyMethodDefType::Method({
|
||||||
#wrapper
|
#wrapper
|
||||||
|
@ -594,8 +340,38 @@ fn impl_py_method_def(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_py_setter_def(name: &syn::Ident, setter: Option<String>, wrapper: &Tokens) -> Tokens {
|
pub fn impl_py_method_def_new(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
|
||||||
let n = if let Some(ref name) = setter {
|
quote! {
|
||||||
|
_pyo3::class::PyMethodDefType::New({
|
||||||
|
#wrapper
|
||||||
|
|
||||||
|
_pyo3::class::PyMethodDef {
|
||||||
|
ml_name: stringify!(#name),
|
||||||
|
ml_meth: _pyo3::class::PyMethodType::PyNewFunc(wrap),
|
||||||
|
ml_flags: _pyo3::ffi::METH_VARARGS | _pyo3::ffi::METH_KEYWORDS,
|
||||||
|
ml_doc: "",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn impl_py_method_def_call(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
|
||||||
|
quote! {
|
||||||
|
_pyo3::class::PyMethodDefType::Call({
|
||||||
|
#wrapper
|
||||||
|
|
||||||
|
_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: "",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_py_setter_def(name: &syn::Ident, setter: &Option<String>, wrapper: &Tokens) -> Tokens {
|
||||||
|
let n = if let &Some(ref name) = setter {
|
||||||
name.to_string()
|
name.to_string()
|
||||||
} else {
|
} else {
|
||||||
let n = String::from(name.as_ref());
|
let n = String::from(name.as_ref());
|
||||||
|
@ -619,8 +395,8 @@ fn impl_py_setter_def(name: &syn::Ident, setter: Option<String>, wrapper: &Token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn impl_py_getter_def(name: &syn::Ident, getter: Option<String>, wrapper: &Tokens) -> Tokens {
|
fn impl_py_getter_def(name: &syn::Ident, getter: &Option<String>, wrapper: &Tokens) -> Tokens {
|
||||||
let n = if let Some(ref name) = getter {
|
let n = if let &Some(ref name) = getter {
|
||||||
name.to_string()
|
name.to_string()
|
||||||
} else {
|
} else {
|
||||||
let n = String::from(name.as_ref());
|
let n = String::from(name.as_ref());
|
||||||
|
|
|
@ -4,6 +4,7 @@ use syn;
|
||||||
use quote::{Tokens, ToTokens};
|
use quote::{Tokens, ToTokens};
|
||||||
|
|
||||||
use py_method;
|
use py_method;
|
||||||
|
use method::FnSpec;
|
||||||
use func::{MethodProto, impl_method_proto};
|
use func::{MethodProto, impl_method_proto};
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,10 +27,6 @@ static DEFAULT_METHODS: Methods = Methods {
|
||||||
methods: &[],
|
methods: &[],
|
||||||
};
|
};
|
||||||
|
|
||||||
static CONTEXT_METHODS: Methods = Methods {
|
|
||||||
methods: &["__enter__", "__exit__"],
|
|
||||||
};
|
|
||||||
|
|
||||||
static DESCR_METHODS: Methods = Methods {
|
static DESCR_METHODS: Methods = Methods {
|
||||||
methods: &["__delete__", "__set_name__"],
|
methods: &["__delete__", "__set_name__"],
|
||||||
};
|
};
|
||||||
|
@ -75,6 +72,29 @@ static ASYNC: Proto = Proto {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static CONTEXT: Proto = Proto {
|
||||||
|
name: "Context",
|
||||||
|
methods: &[
|
||||||
|
MethodProto::Unary{
|
||||||
|
name: "__enter__",
|
||||||
|
proto: "_pyo3::class::context::PyContextEnterProtocol"},
|
||||||
|
MethodProto::Quaternary {
|
||||||
|
name: "__exit__",
|
||||||
|
arg1: "ExcType", arg2: "ExcValue", arg3: "Traceback",
|
||||||
|
proto: "_pyo3::class::context::PyContextExitProtocol"},
|
||||||
|
],
|
||||||
|
py_methods: &[
|
||||||
|
PyMethod {
|
||||||
|
name: "__enter__",
|
||||||
|
proto: "_pyo3::class::context::PyContextEnterProtocolImpl",
|
||||||
|
},
|
||||||
|
PyMethod {
|
||||||
|
name: "__exit__",
|
||||||
|
proto: "_pyo3::class::context::PyContextExitProtocolImpl",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
static ITER: Proto = Proto {
|
static ITER: Proto = Proto {
|
||||||
name: "Iter",
|
name: "Iter",
|
||||||
py_methods: &[],
|
py_methods: &[],
|
||||||
|
@ -128,12 +148,11 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
|
||||||
impl_proto_impl(ty, impl_items, &MAPPING),
|
impl_proto_impl(ty, impl_items, &MAPPING),
|
||||||
"PyIterProtocol" =>
|
"PyIterProtocol" =>
|
||||||
impl_proto_impl(ty, impl_items, &ITER),
|
impl_proto_impl(ty, impl_items, &ITER),
|
||||||
|
"PyContextProtocol" =>
|
||||||
|
impl_proto_impl(ty, impl_items, &CONTEXT),
|
||||||
"PyBufferProtocol" =>
|
"PyBufferProtocol" =>
|
||||||
impl_protocol("_pyo3::class::buffer::PyBufferProtocolImpl",
|
impl_protocol("_pyo3::class::buffer::PyBufferProtocolImpl",
|
||||||
path.clone(), ty, impl_items, &DEFAULT_METHODS),
|
path.clone(), ty, impl_items, &DEFAULT_METHODS),
|
||||||
"PyContextProtocol" =>
|
|
||||||
impl_protocol("_pyo3::class::context::PyContextProtocolImpl",
|
|
||||||
path.clone(), ty, impl_items, &CONTEXT_METHODS),
|
|
||||||
"PyDescrProtocol" =>
|
"PyDescrProtocol" =>
|
||||||
impl_protocol("_pyo3::class::descr::PyDescrProtocolImpl",
|
impl_protocol("_pyo3::class::descr::PyDescrProtocolImpl",
|
||||||
path.clone(), ty, impl_items, &DESCR_METHODS),
|
path.clone(), ty, impl_items, &DESCR_METHODS),
|
||||||
|
@ -176,16 +195,23 @@ fn impl_proto_impl(ty: &Box<syn::Ty>, impls: &mut Vec<syn::ImplItem>, proto: &Pr
|
||||||
let name = syn::Ident::from(m.name);
|
let name = syn::Ident::from(m.name);
|
||||||
let proto = syn::Ident::from(m.proto);
|
let proto = syn::Ident::from(m.proto);
|
||||||
|
|
||||||
let meth = py_method::gen_py_method(
|
let fn_spec = FnSpec::parse(
|
||||||
ty, &iimpl.ident, sig, &mut iimpl.attrs);
|
&iimpl.ident, sig, &mut iimpl.attrs);
|
||||||
|
let meth = py_method::impl_wrap(ty, &iimpl.ident, &fn_spec);
|
||||||
|
|
||||||
py_methods.push(
|
py_methods.push(
|
||||||
quote! {
|
quote! {
|
||||||
impl #proto for #ty
|
impl #proto for #ty
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn #name() -> Option<_pyo3::class::methods::PyMethodDefType> {
|
fn #name() -> Option<_pyo3::class::methods::PyMethodDef> {
|
||||||
Some(#meth)
|
#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: ""})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use ffi;
|
||||||
use err::PyResult;
|
use err::PyResult;
|
||||||
use python::{Python, PythonObject};
|
use python::{Python, PythonObject};
|
||||||
use callback::PyObjectCallbackConverter;
|
use callback::PyObjectCallbackConverter;
|
||||||
use class::methods::{PyMethodDef, PyMethodDefType};
|
use class::methods::PyMethodDef;
|
||||||
|
|
||||||
|
|
||||||
/// Awaitable interface
|
/// Awaitable interface
|
||||||
|
@ -99,13 +99,11 @@ impl<T> PyAsyncProtocolImpl for T where T: PyAsyncProtocol {
|
||||||
fn methods() -> Vec<PyMethodDef> {
|
fn methods() -> Vec<PyMethodDef> {
|
||||||
let mut methods = Vec::new();
|
let mut methods = Vec::new();
|
||||||
|
|
||||||
if let Some(PyMethodDefType::Method(meth)) =
|
if let Some(def) = <Self as PyAsyncAenterProtocolImpl>::__aenter__() {
|
||||||
<Self as PyAsyncAenterProtocolImpl>::__aenter__() {
|
methods.push(def)
|
||||||
methods.push(meth)
|
|
||||||
}
|
}
|
||||||
if let Some(PyMethodDefType::Method(meth)) =
|
if let Some(def) = <Self as PyAsyncAexitProtocolImpl>::__aexit__() {
|
||||||
<Self as PyAsyncAexitProtocolImpl>::__aexit__() {
|
methods.push(def)
|
||||||
methods.push(meth)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
methods
|
methods
|
||||||
|
@ -180,27 +178,27 @@ impl<T> PyAsyncAnextProtocolImpl for T
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PyAsyncAenterProtocolImpl {
|
trait PyAsyncAenterProtocolImpl {
|
||||||
fn __aenter__() -> Option<PyMethodDefType>;
|
fn __aenter__() -> Option<PyMethodDef>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> PyAsyncAenterProtocolImpl for T
|
impl<T> PyAsyncAenterProtocolImpl for T
|
||||||
where T: PyAsyncProtocol
|
where T: PyAsyncProtocol
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
default fn __aenter__() -> Option<PyMethodDefType> {
|
default fn __aenter__() -> Option<PyMethodDef> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PyAsyncAexitProtocolImpl {
|
pub trait PyAsyncAexitProtocolImpl {
|
||||||
fn __aexit__() -> Option<PyMethodDefType>;
|
fn __aexit__() -> Option<PyMethodDef>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> PyAsyncAexitProtocolImpl for T
|
impl<T> PyAsyncAexitProtocolImpl for T
|
||||||
where T: PyAsyncProtocol
|
where T: PyAsyncProtocol
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
default fn __aexit__() -> Option<PyMethodDefType> {
|
default fn __aexit__() -> Option<PyMethodDef> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,50 +5,88 @@
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use err::PyResult;
|
use err::PyResult;
|
||||||
use python::Python;
|
use python::{Python, PythonObject};
|
||||||
use objects::PyObject;
|
use class::methods::PyMethodDef;
|
||||||
use class::{NO_METHODS, NO_PY_METHODS};
|
|
||||||
|
|
||||||
|
|
||||||
/// Awaitable interface
|
/// Context manager interface
|
||||||
pub trait PyContextProtocol {
|
#[allow(unused_variables)]
|
||||||
|
pub trait PyContextProtocol: PythonObject {
|
||||||
|
|
||||||
fn __enter__(&self, py: Python) -> PyResult<PyObject>;
|
fn __enter__(&self, py: Python)
|
||||||
|
-> Self::Result where Self: PyContextEnterProtocol { unimplemented!() }
|
||||||
|
|
||||||
fn __exit__(&self, py: Python,
|
fn __exit__(&self, py: Python,
|
||||||
exc_type: Option<PyObject>,
|
exc_type: Option<Self::ExcType>,
|
||||||
exc_value: Option<PyObject>,
|
exc_value: Option<Self::ExcValue>,
|
||||||
traceback: Option<PyObject>) -> PyResult<PyObject>;
|
traceback: Option<Self::Traceback>)
|
||||||
|
-> Self::Result where Self: PyContextExitProtocol { unimplemented!() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait PyContextEnterProtocol: PyContextProtocol {
|
||||||
impl<P> PyContextProtocol for P {
|
type Success: ::ToPyObject;
|
||||||
|
type Result: Into<PyResult<Self::Success>>;
|
||||||
default fn __enter__(&self, py: Python) -> PyResult<PyObject> {
|
|
||||||
Ok(py.None())
|
|
||||||
}
|
|
||||||
|
|
||||||
default fn __exit__(&self, py: Python,
|
|
||||||
_exc_type: Option<PyObject>,
|
|
||||||
_exc_value: Option<PyObject>,
|
|
||||||
_traceback: Option<PyObject>) -> PyResult<PyObject> {
|
|
||||||
Ok(py.None())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait PyContextExitProtocol: PyContextProtocol {
|
||||||
|
type ExcType: for<'a> ::FromPyObject<'a>;
|
||||||
|
type ExcValue: for<'a> ::FromPyObject<'a>;
|
||||||
|
type Traceback: for<'a> ::FromPyObject<'a>;
|
||||||
|
type Success: ::ToPyObject;
|
||||||
|
type Result: Into<PyResult<Self::Success>>;
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub trait PyContextProtocolImpl {
|
pub trait PyContextProtocolImpl {
|
||||||
fn methods() -> &'static [&'static str];
|
fn methods() -> Vec<PyMethodDef>;
|
||||||
|
|
||||||
fn py_methods() -> &'static [::methods::PyMethodDefType];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> PyContextProtocolImpl for T {
|
impl<T> PyContextProtocolImpl for T {
|
||||||
default fn methods() -> &'static [&'static str] {
|
#[inline]
|
||||||
NO_METHODS
|
default fn methods() -> Vec<PyMethodDef> {
|
||||||
}
|
Vec::new()
|
||||||
default fn py_methods() -> &'static [::methods::PyMethodDefType] {
|
}
|
||||||
NO_PY_METHODS
|
}
|
||||||
|
|
||||||
|
impl<T> PyContextProtocolImpl for T where T: PyContextProtocol {
|
||||||
|
#[inline]
|
||||||
|
fn methods() -> Vec<PyMethodDef> {
|
||||||
|
let mut methods = Vec::new();
|
||||||
|
|
||||||
|
if let Some(def) = <Self as PyContextEnterProtocolImpl>::__enter__() {
|
||||||
|
methods.push(def)
|
||||||
|
}
|
||||||
|
if let Some(def) = <Self as PyContextExitProtocolImpl>::__exit__() {
|
||||||
|
methods.push(def)
|
||||||
|
}
|
||||||
|
|
||||||
|
methods
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
trait PyContextEnterProtocolImpl {
|
||||||
|
fn __enter__() -> Option<PyMethodDef>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PyContextEnterProtocolImpl for T
|
||||||
|
where T: PyContextProtocol
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
default fn __enter__() -> Option<PyMethodDef> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PyContextExitProtocolImpl {
|
||||||
|
fn __exit__() -> Option<PyMethodDef>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PyContextExitProtocolImpl for T
|
||||||
|
where T: PyContextProtocol
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
default fn __exit__() -> Option<PyMethodDef> {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ use ffi;
|
||||||
use class::NO_PY_METHODS;
|
use class::NO_PY_METHODS;
|
||||||
|
|
||||||
pub enum PyMethodDefType {
|
pub enum PyMethodDefType {
|
||||||
|
New(PyMethodDef),
|
||||||
|
Call(PyMethodDef),
|
||||||
Method(PyMethodDef),
|
Method(PyMethodDef),
|
||||||
Getter(PyGetterDef),
|
Getter(PyGetterDef),
|
||||||
Setter(PySetterDef),
|
Setter(PySetterDef),
|
||||||
|
@ -16,6 +18,7 @@ pub enum PyMethodType {
|
||||||
PyCFunction(ffi::PyCFunction),
|
PyCFunction(ffi::PyCFunction),
|
||||||
PyCFunctionWithKeywords(ffi::PyCFunctionWithKeywords),
|
PyCFunctionWithKeywords(ffi::PyCFunctionWithKeywords),
|
||||||
PyNoArgsFunction(ffi::PyNoArgsFunction),
|
PyNoArgsFunction(ffi::PyNoArgsFunction),
|
||||||
|
PyNewFunc(ffi::newfunc),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
@ -63,6 +66,11 @@ impl PyMethodDef {
|
||||||
::std::mem::transmute::<
|
::std::mem::transmute::<
|
||||||
ffi::PyNoArgsFunction, ffi::PyCFunction>(meth)
|
ffi::PyNoArgsFunction, ffi::PyCFunction>(meth)
|
||||||
},
|
},
|
||||||
|
PyMethodType::PyNewFunc(meth) =>
|
||||||
|
unsafe {
|
||||||
|
::std::mem::transmute::<
|
||||||
|
ffi::newfunc, ffi::PyCFunction>(meth)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
ffi::PyMethodDef {
|
ffi::PyMethodDef {
|
||||||
|
|
|
@ -53,7 +53,7 @@ pub fn initialize_type<T>(py: Python, module_name: Option<&str>, type_name: &str
|
||||||
// type name
|
// type name
|
||||||
let name = match module_name {
|
let name = match module_name {
|
||||||
Some(module_name) => CString::new(format!("{}.{}", module_name, type_name)),
|
Some(module_name) => CString::new(format!("{}.{}", module_name, type_name)),
|
||||||
None => CString::new(stringify!(type_name))
|
None => CString::new(type_name)
|
||||||
};
|
};
|
||||||
let name = name.expect(
|
let name = name.expect(
|
||||||
"Module name/type name must not contain NUL byte").into_raw();
|
"Module name/type name must not contain NUL byte").into_raw();
|
||||||
|
@ -127,7 +127,7 @@ pub fn initialize_type<T>(py: Python, module_name: Option<&str>, type_name: &str
|
||||||
}
|
}
|
||||||
|
|
||||||
// normal methods
|
// normal methods
|
||||||
let mut methods = py_class_method_defs::<T>();
|
let (new, call, mut methods) = py_class_method_defs::<T>();
|
||||||
if !methods.is_empty() {
|
if !methods.is_empty() {
|
||||||
methods.push(ffi::PyMethodDef_INIT);
|
methods.push(ffi::PyMethodDef_INIT);
|
||||||
type_object.tp_methods = methods.as_ptr() as *mut _;
|
type_object.tp_methods = methods.as_ptr() as *mut _;
|
||||||
|
@ -137,6 +137,10 @@ pub fn initialize_type<T>(py: Python, module_name: Option<&str>, type_name: &str
|
||||||
|
|
||||||
mem::forget(methods);
|
mem::forget(methods);
|
||||||
}
|
}
|
||||||
|
// __new__ method
|
||||||
|
type_object.tp_new = new;
|
||||||
|
// __call__ method
|
||||||
|
type_object.tp_call = call;
|
||||||
|
|
||||||
// properties
|
// properties
|
||||||
let mut props = py_class_properties::<T>();
|
let mut props = py_class_properties::<T>();
|
||||||
|
@ -171,15 +175,12 @@ unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject) where T: Ba
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
fn py_class_method_defs<T>() -> Vec<ffi::PyMethodDef> {
|
fn py_class_method_defs<T>() -> (Option<ffi::newfunc>,
|
||||||
|
Option<ffi::PyCFunctionWithKeywords>,
|
||||||
|
Vec<ffi::PyMethodDef>) {
|
||||||
let mut defs = Vec::new();
|
let mut defs = Vec::new();
|
||||||
|
let mut call = None;
|
||||||
for def in <T as class::context::PyContextProtocolImpl>::py_methods() {
|
let mut new = None;
|
||||||
match def {
|
|
||||||
&PyMethodDefType::Method(ref def) => defs.push(def.as_method_def()),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for def in <T as class::number::PyNumberProtocolImpl>::py_methods() {
|
for def in <T as class::number::PyNumberProtocolImpl>::py_methods() {
|
||||||
match def {
|
match def {
|
||||||
|
@ -189,15 +190,32 @@ fn py_class_method_defs<T>() -> Vec<ffi::PyMethodDef> {
|
||||||
}
|
}
|
||||||
for def in <T as class::methods::PyMethodsProtocolImpl>::py_methods() {
|
for def in <T as class::methods::PyMethodsProtocolImpl>::py_methods() {
|
||||||
match def {
|
match def {
|
||||||
&PyMethodDefType::Method(ref def) => defs.push(def.as_method_def()),
|
&PyMethodDefType::New(ref def) => {
|
||||||
|
if let class::methods::PyMethodType::PyNewFunc(meth) = def.ml_meth {
|
||||||
|
new = Some(meth)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
&PyMethodDefType::Call(ref def) => {
|
||||||
|
if let class::methods::PyMethodType::PyCFunctionWithKeywords(meth) = def.ml_meth {
|
||||||
|
call = Some(meth)
|
||||||
|
} else {
|
||||||
|
panic!("Method type is not supoorted by tp_call slot")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&PyMethodDefType::Method(ref def) => {
|
||||||
|
defs.push(def.as_method_def())
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for def in <T as class::async::PyAsyncProtocolImpl>::methods() {
|
for def in <T as class::async::PyAsyncProtocolImpl>::methods() {
|
||||||
defs.push(def.as_method_def())
|
defs.push(def.as_method_def())
|
||||||
}
|
}
|
||||||
|
for def in <T as class::context::PyContextProtocolImpl>::methods() {
|
||||||
|
defs.push(def.as_method_def())
|
||||||
|
}
|
||||||
|
|
||||||
defs
|
(new, call, defs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue