diff --git a/.travis.yml b/.travis.yml index dbd3a3f5..08074800 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ install: - python -c "import sysconfig; print('\n'.join(map(repr,sorted(sysconfig.get_config_vars().items()))))" - mkdir ~/rust-installer - curl -sL https://static.rust-lang.org/rustup.sh -o ~/rust-installer/rustup.sh - - sh ~/rust-installer/rustup.sh --prefix=~/rust --spec=$RUST_VERSION -y --disable-sudo + - sh ~/rust-installer/rustup.sh --prefix=~/rust --spec=$RUST_VERSION -y - export PATH="$HOME/rust/bin:$PATH" - export PYTHON_LIB=$(python -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))") - find $PYTHON_LIB diff --git a/pyo3cls/src/func.rs b/pyo3cls/src/func.rs index e3626eca..94476cb8 100644 --- a/pyo3cls/src/func.rs +++ b/pyo3cls/src/func.rs @@ -9,6 +9,10 @@ pub enum MethodProto { Unary{name: &'static str, proto: &'static str}, Binary{name: &'static str, arg: &'static str, proto: &'static str}, Ternary{name: &'static str, arg1: &'static str, arg2: &'static str, proto: &'static str}, + Quaternary{name: &'static str, + arg1: &'static str, + arg2: &'static str, + arg3: &'static str, proto: &'static str}, } impl MethodProto { @@ -19,6 +23,7 @@ impl MethodProto { MethodProto::Unary{name: n, proto: _} => n == name, MethodProto::Binary{name: n, arg: _, proto: _} => n == name, MethodProto::Ternary{name: n, arg1: _, arg2: _, proto: _} => n == name, + MethodProto::Quaternary{name: n, arg1: _, arg2: _, arg3: _, proto: _} => n == name, } } } @@ -80,6 +85,26 @@ pub fn impl_method_proto(cls: &Box, } } }, + MethodProto::Quaternary{name: _, arg1, arg2, arg3, proto} => { + let p = syn::Ident::from(proto); + let arg1_name = syn::Ident::from(arg1); + let arg1_ty = get_arg_ty(sig, 2); + let arg2_name = syn::Ident::from(arg2); + let arg2_ty = get_arg_ty(sig, 3); + let arg3_name = syn::Ident::from(arg3); + let arg3_ty = get_arg_ty(sig, 4); + let succ = get_res_success(ty); + + quote! { + impl #p for #cls { + type #arg1_name = #arg1_ty; + type #arg2_name = #arg2_ty; + type #arg3_name = #arg3_ty; + type Success = #succ; + type Result = #ty; + } + } + }, } }, _ => panic!("not supported"), @@ -87,10 +112,31 @@ pub fn impl_method_proto(cls: &Box, } +// TODO: better arg ty detection fn get_arg_ty(sig: &syn::MethodSig, idx: usize) -> syn::Ty { match sig.decl.inputs[idx] { syn::FnArg::Captured(_, ref arg_ty) => { - arg_ty.clone() + match arg_ty { + &syn::Ty::Path(_, ref path) => { + // use only last path segment for Option<> + let seg = path.segments.last().unwrap().clone(); + if seg.ident.as_ref() == "Option" { + match seg.parameters { + syn::PathParameters::AngleBracketed(ref data) => { + if let Some(ty) = data.types.last() { + return ty.clone() + } + } + _ => (), + } + } + + arg_ty.clone() + }, + _ => { + arg_ty.clone() + } + } }, _ => panic!("not supported"), diff --git a/pyo3cls/src/py_impl.rs b/pyo3cls/src/py_impl.rs index ecaa5a94..d593549f 100644 --- a/pyo3cls/src/py_impl.rs +++ b/pyo3cls/src/py_impl.rs @@ -25,9 +25,9 @@ fn impl_methods(ty: &Box, impls: &mut Vec) -> Tokens { let mut methods = Vec::new(); for iimpl in impls.iter_mut() { match iimpl.node { - syn::ImplItemKind::Method(ref mut sig, ref mut block) => { + syn::ImplItemKind::Method(ref mut sig, _) => { methods.push(py_method::gen_py_method( - ty, &iimpl.ident, sig, block, &mut iimpl.attrs)); + ty, &iimpl.ident, sig, &mut iimpl.attrs)); }, _ => (), } diff --git a/pyo3cls/src/py_method.rs b/pyo3cls/src/py_method.rs index f01e5870..be2425cb 100644 --- a/pyo3cls/src/py_method.rs +++ b/pyo3cls/src/py_method.rs @@ -29,7 +29,7 @@ enum FnSpec { pub fn gen_py_method<'a>(cls: &Box, name: &syn::Ident, - sig: &mut syn::MethodSig, _block: &mut syn::Block, + sig: &mut syn::MethodSig, meth_attrs: &mut Vec) -> Tokens { check_generic(name, sig); diff --git a/pyo3cls/src/py_proto.rs b/pyo3cls/src/py_proto.rs index 18030e7b..2e87a557 100644 --- a/pyo3cls/src/py_proto.rs +++ b/pyo3cls/src/py_proto.rs @@ -11,10 +11,15 @@ struct Methods { methods: &'static [&'static str], } +struct PyMethod { + name: &'static str, + proto: &'static str, +} + struct Proto { name: &'static str, - //py_methods: &'static [&'static str], methods: &'static [MethodProto], + py_methods: &'static [PyMethod], } static DEFAULT_METHODS: Methods = Methods { @@ -40,9 +45,8 @@ static NUM_METHODS: Methods = Methods { static ASYNC: Proto = Proto { name: "Async", - //py_methods: &[], methods: &[ - MethodProto::Unary{ + MethodProto::Unary { name: "__await__", proto: "_pyo3::class::async::PyAsyncAwaitProtocol"}, MethodProto::Unary{ @@ -51,12 +55,29 @@ static ASYNC: Proto = Proto { MethodProto::Unary{ name: "__anext__", proto: "_pyo3::class::async::PyAsyncAnextProtocol"}, + MethodProto::Unary{ + name: "__aenter__", + proto: "_pyo3::class::async::PyAsyncAenterProtocol"}, + MethodProto::Quaternary { + name: "__aexit__", + arg1: "ExcType", arg2: "ExcValue", arg3: "Traceback", + proto: "_pyo3::class::async::PyAsyncAexitProtocol"}, + ], + py_methods: &[ + PyMethod { + name: "__aenter__", + proto: "_pyo3::class::async::PyAsyncAenterProtocolImpl", + }, + PyMethod { + name: "__aexit__", + proto: "_pyo3::class::async::PyAsyncAexitProtocolImpl", + }, ], }; static ITER: Proto = Proto { name: "Iter", - //py_methods: &[], + py_methods: &[], methods: &[ MethodProto::Unary{ name: "__iter__", @@ -70,7 +91,7 @@ static ITER: Proto = Proto { static MAPPING: Proto = Proto { name: "Mapping", - //py_methods: &[], + py_methods: &[], methods: &[ MethodProto::Len{ name: "__len__", @@ -140,6 +161,7 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens { fn impl_proto_impl(ty: &Box, impls: &mut Vec, proto: &Proto) -> Tokens { let mut tokens = Tokens::new(); + let mut py_methods = Vec::new(); for iimpl in impls.iter_mut() { match iimpl.node { @@ -149,6 +171,27 @@ fn impl_proto_impl(ty: &Box, impls: &mut Vec, proto: &Pr impl_method_proto(ty, sig, m).to_tokens(&mut tokens); } } + for m in proto.py_methods { + if m.name == iimpl.ident.as_ref() { + let name = syn::Ident::from(m.name); + let proto = syn::Ident::from(m.proto); + + let meth = py_method::gen_py_method( + ty, &iimpl.ident, sig, &mut iimpl.attrs); + + py_methods.push( + quote! { + impl #proto for #ty + { + #[inline] + fn #name() -> Option<_pyo3::class::methods::PyMethodDefType> { + Some(#meth) + } + } + } + ); + } + } }, _ => (), } @@ -172,6 +215,8 @@ fn impl_proto_impl(ty: &Box, impls: &mut Vec, proto: &Pr extern crate pyo3 as _pyo3; #tokens + + #(#py_methods)* }; } } @@ -185,10 +230,10 @@ fn impl_protocol(name: &'static str, let mut meth = Vec::new(); for iimpl in impls.iter_mut() { match iimpl.node { - syn::ImplItemKind::Method(ref mut sig, ref mut block) => { + syn::ImplItemKind::Method(ref mut sig, _) => { if methods.methods.contains(&iimpl.ident.as_ref()) { py_methods.push(py_method::gen_py_method( - ty, &iimpl.ident, sig, block, &mut iimpl.attrs)); + ty, &iimpl.ident, sig, &mut iimpl.attrs)); } else { meth.push(String::from(iimpl.ident.as_ref())); } diff --git a/src/class/async.rs b/src/class/async.rs index e68ad5c8..7e1ab8f7 100644 --- a/src/class/async.rs +++ b/src/class/async.rs @@ -10,6 +10,7 @@ use ffi; use err::PyResult; use python::{Python, PythonObject}; use callback::PyObjectCallbackConverter; +use class::methods::{PyMethodDef, PyMethodDefType}; /// Awaitable interface @@ -28,9 +29,11 @@ pub trait PyAsyncProtocol: PythonObject { fn __aenter__(&self, py: Python) -> Self::Result where Self: PyAsyncAenterProtocol { unimplemented!() } - fn __aexit__(&self, py: Python) + fn __aexit__(&self, py: Python, + exc_type: Option, + exc_value: Option, + traceback: Option) -> Self::Result where Self: PyAsyncAexitProtocol { unimplemented!() } - } @@ -55,6 +58,9 @@ pub trait PyAsyncAenterProtocol: PyAsyncProtocol { } pub trait PyAsyncAexitProtocol: PyAsyncProtocol { + type ExcType: for<'a> ::FromPyObject<'a>; + type ExcValue: for<'a> ::FromPyObject<'a>; + type Traceback: for<'a> ::FromPyObject<'a>; type Success: ::ToPyObject; type Result: Into>; } @@ -63,6 +69,8 @@ pub trait PyAsyncAexitProtocol: PyAsyncProtocol { #[doc(hidden)] pub trait PyAsyncProtocolImpl { fn tp_as_async() -> Option; + + fn methods() -> Vec; } impl PyAsyncProtocolImpl for T { @@ -70,6 +78,11 @@ impl PyAsyncProtocolImpl for T { default fn tp_as_async() -> Option { None } + + #[inline] + default fn methods() -> Vec { + Vec::new() + } } impl PyAsyncProtocolImpl for T where T: PyAsyncProtocol { @@ -81,6 +94,22 @@ impl PyAsyncProtocolImpl for T where T: PyAsyncProtocol { am_anext: Self::am_anext(), }) } + + #[inline] + fn methods() -> Vec { + let mut methods = Vec::new(); + + if let Some(PyMethodDefType::Method(meth)) = + ::__aenter__() { + methods.push(meth) + } + if let Some(PyMethodDefType::Method(meth)) = + ::__aexit__() { + methods.push(meth) + } + + methods + } } @@ -151,45 +180,27 @@ impl PyAsyncAnextProtocolImpl for T } trait PyAsyncAenterProtocolImpl { - fn am_aenter() -> Option; + fn __aenter__() -> Option; } impl PyAsyncAenterProtocolImpl for T where T: PyAsyncProtocol { #[inline] - default fn am_aenter() -> Option { + default fn __aenter__() -> Option { None } } -impl PyAsyncAenterProtocolImpl for T - where T: PyAsyncAenterProtocol -{ - #[inline] - fn am_aenter() -> Option { - py_unary_func_!(PyAsyncAenterProtocol, T::__aenter__, PyObjectCallbackConverter) - } -} - -trait PyAsyncAexitProtocolImpl { - fn am_aexit() -> Option; +pub trait PyAsyncAexitProtocolImpl { + fn __aexit__() -> Option; } impl PyAsyncAexitProtocolImpl for T where T: PyAsyncProtocol { #[inline] - default fn am_aexit() -> Option { + default fn __aexit__() -> Option { None } } - -impl PyAsyncAexitProtocolImpl for T - where T: PyAsyncAexitProtocol -{ - #[inline] - fn am_aexit() -> Option { - py_unary_func_!(PyAsyncAexitProtocol, T::__aexit__, PyObjectCallbackConverter) - } -} diff --git a/src/class/methods.rs b/src/class/methods.rs index bc9ea355..cc508421 100644 --- a/src/class/methods.rs +++ b/src/class/methods.rs @@ -15,6 +15,7 @@ pub enum PyMethodDefType { pub enum PyMethodType { PyCFunction(ffi::PyCFunction), PyCFunctionWithKeywords(ffi::PyCFunctionWithKeywords), + PyNoArgsFunction(ffi::PyNoArgsFunction), } #[derive(Copy, Clone)] @@ -56,7 +57,12 @@ impl PyMethodDef { unsafe { ::std::mem::transmute::< ffi::PyCFunctionWithKeywords, ffi::PyCFunction>(meth) - } + }, + PyMethodType::PyNoArgsFunction(meth) => + unsafe { + ::std::mem::transmute::< + ffi::PyNoArgsFunction, ffi::PyCFunction>(meth) + }, }; ffi::PyMethodDef { diff --git a/src/class/typeob.rs b/src/class/typeob.rs index 46446e5e..e0e9a25c 100644 --- a/src/class/typeob.rs +++ b/src/class/typeob.rs @@ -193,6 +193,9 @@ fn py_class_method_defs() -> Vec { _ => (), } } + for def in ::methods() { + defs.push(def.as_method_def()) + } defs }