generate method defs from protocols

This commit is contained in:
Nikolay Kim 2017-05-19 11:48:05 -07:00
parent 266e608dc5
commit eb64aa11ac
8 changed files with 149 additions and 38 deletions

View File

@ -10,7 +10,7 @@ install:
- python -c "import sysconfig; print('\n'.join(map(repr,sorted(sysconfig.get_config_vars().items()))))" - python -c "import sysconfig; print('\n'.join(map(repr,sorted(sysconfig.get_config_vars().items()))))"
- mkdir ~/rust-installer - mkdir ~/rust-installer
- curl -sL https://static.rust-lang.org/rustup.sh -o ~/rust-installer/rustup.sh - 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 PATH="$HOME/rust/bin:$PATH"
- export PYTHON_LIB=$(python -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))") - export PYTHON_LIB=$(python -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))")
- find $PYTHON_LIB - find $PYTHON_LIB

View File

@ -9,6 +9,10 @@ pub enum MethodProto {
Unary{name: &'static str, proto: &'static str}, Unary{name: &'static str, proto: &'static str},
Binary{name: &'static str, arg: &'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}, 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 { impl MethodProto {
@ -19,6 +23,7 @@ impl MethodProto {
MethodProto::Unary{name: n, proto: _} => n == name, MethodProto::Unary{name: n, proto: _} => n == name,
MethodProto::Binary{name: n, arg: _, proto: _} => n == name, MethodProto::Binary{name: n, arg: _, proto: _} => n == name,
MethodProto::Ternary{name: n, arg1: _, arg2: _, 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<syn::Ty>,
} }
} }
}, },
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"), _ => panic!("not supported"),
@ -87,10 +112,31 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
} }
// TODO: better arg ty detection
fn get_arg_ty(sig: &syn::MethodSig, idx: usize) -> syn::Ty { fn get_arg_ty(sig: &syn::MethodSig, idx: usize) -> syn::Ty {
match sig.decl.inputs[idx] { match sig.decl.inputs[idx] {
syn::FnArg::Captured(_, ref arg_ty) => { 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"), panic!("not supported"),

View File

@ -25,9 +25,9 @@ fn impl_methods(ty: &Box<syn::Ty>, impls: &mut Vec<syn::ImplItem>) -> Tokens {
let mut methods = Vec::new(); let mut methods = Vec::new();
for iimpl in impls.iter_mut() { for iimpl in impls.iter_mut() {
match iimpl.node { 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( methods.push(py_method::gen_py_method(
ty, &iimpl.ident, sig, block, &mut iimpl.attrs)); ty, &iimpl.ident, sig, &mut iimpl.attrs));
}, },
_ => (), _ => (),
} }

View File

@ -29,7 +29,7 @@ enum FnSpec {
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, _block: &mut syn::Block, 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);

View File

@ -11,10 +11,15 @@ struct Methods {
methods: &'static [&'static str], methods: &'static [&'static str],
} }
struct PyMethod {
name: &'static str,
proto: &'static str,
}
struct Proto { struct Proto {
name: &'static str, name: &'static str,
//py_methods: &'static [&'static str],
methods: &'static [MethodProto], methods: &'static [MethodProto],
py_methods: &'static [PyMethod],
} }
static DEFAULT_METHODS: Methods = Methods { static DEFAULT_METHODS: Methods = Methods {
@ -40,9 +45,8 @@ static NUM_METHODS: Methods = Methods {
static ASYNC: Proto = Proto { static ASYNC: Proto = Proto {
name: "Async", name: "Async",
//py_methods: &[],
methods: &[ methods: &[
MethodProto::Unary{ MethodProto::Unary {
name: "__await__", name: "__await__",
proto: "_pyo3::class::async::PyAsyncAwaitProtocol"}, proto: "_pyo3::class::async::PyAsyncAwaitProtocol"},
MethodProto::Unary{ MethodProto::Unary{
@ -51,12 +55,29 @@ static ASYNC: Proto = Proto {
MethodProto::Unary{ MethodProto::Unary{
name: "__anext__", name: "__anext__",
proto: "_pyo3::class::async::PyAsyncAnextProtocol"}, 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 { static ITER: Proto = Proto {
name: "Iter", name: "Iter",
//py_methods: &[], py_methods: &[],
methods: &[ methods: &[
MethodProto::Unary{ MethodProto::Unary{
name: "__iter__", name: "__iter__",
@ -70,7 +91,7 @@ static ITER: Proto = Proto {
static MAPPING: Proto = Proto { static MAPPING: Proto = Proto {
name: "Mapping", name: "Mapping",
//py_methods: &[], py_methods: &[],
methods: &[ methods: &[
MethodProto::Len{ MethodProto::Len{
name: "__len__", name: "__len__",
@ -140,6 +161,7 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
fn impl_proto_impl(ty: &Box<syn::Ty>, impls: &mut Vec<syn::ImplItem>, proto: &Proto) -> Tokens { fn impl_proto_impl(ty: &Box<syn::Ty>, impls: &mut Vec<syn::ImplItem>, proto: &Proto) -> Tokens {
let mut tokens = Tokens::new(); let mut tokens = Tokens::new();
let mut py_methods = Vec::new();
for iimpl in impls.iter_mut() { for iimpl in impls.iter_mut() {
match iimpl.node { match iimpl.node {
@ -149,6 +171,27 @@ fn impl_proto_impl(ty: &Box<syn::Ty>, impls: &mut Vec<syn::ImplItem>, proto: &Pr
impl_method_proto(ty, sig, m).to_tokens(&mut tokens); 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<syn::Ty>, impls: &mut Vec<syn::ImplItem>, proto: &Pr
extern crate pyo3 as _pyo3; extern crate pyo3 as _pyo3;
#tokens #tokens
#(#py_methods)*
}; };
} }
} }
@ -185,10 +230,10 @@ fn impl_protocol(name: &'static str,
let mut meth = Vec::new(); let mut meth = Vec::new();
for iimpl in impls.iter_mut() { for iimpl in impls.iter_mut() {
match iimpl.node { 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()) { if methods.methods.contains(&iimpl.ident.as_ref()) {
py_methods.push(py_method::gen_py_method( py_methods.push(py_method::gen_py_method(
ty, &iimpl.ident, sig, block, &mut iimpl.attrs)); ty, &iimpl.ident, sig, &mut iimpl.attrs));
} else { } else {
meth.push(String::from(iimpl.ident.as_ref())); meth.push(String::from(iimpl.ident.as_ref()));
} }

View File

@ -10,6 +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};
/// Awaitable interface /// Awaitable interface
@ -28,9 +29,11 @@ pub trait PyAsyncProtocol: PythonObject {
fn __aenter__(&self, py: Python) fn __aenter__(&self, py: Python)
-> Self::Result where Self: PyAsyncAenterProtocol { unimplemented!() } -> Self::Result where Self: PyAsyncAenterProtocol { unimplemented!() }
fn __aexit__(&self, py: Python) fn __aexit__(&self, py: Python,
exc_type: Option<Self::ExcType>,
exc_value: Option<Self::ExcValue>,
traceback: Option<Self::Traceback>)
-> Self::Result where Self: PyAsyncAexitProtocol { unimplemented!() } -> Self::Result where Self: PyAsyncAexitProtocol { unimplemented!() }
} }
@ -55,6 +58,9 @@ pub trait PyAsyncAenterProtocol: PyAsyncProtocol {
} }
pub trait PyAsyncAexitProtocol: 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 Success: ::ToPyObject;
type Result: Into<PyResult<Self::Success>>; type Result: Into<PyResult<Self::Success>>;
} }
@ -63,6 +69,8 @@ pub trait PyAsyncAexitProtocol: PyAsyncProtocol {
#[doc(hidden)] #[doc(hidden)]
pub trait PyAsyncProtocolImpl { pub trait PyAsyncProtocolImpl {
fn tp_as_async() -> Option<ffi::PyAsyncMethods>; fn tp_as_async() -> Option<ffi::PyAsyncMethods>;
fn methods() -> Vec<PyMethodDef>;
} }
impl<T> PyAsyncProtocolImpl for T { impl<T> PyAsyncProtocolImpl for T {
@ -70,6 +78,11 @@ impl<T> PyAsyncProtocolImpl for T {
default fn tp_as_async() -> Option<ffi::PyAsyncMethods> { default fn tp_as_async() -> Option<ffi::PyAsyncMethods> {
None None
} }
#[inline]
default fn methods() -> Vec<PyMethodDef> {
Vec::new()
}
} }
impl<T> PyAsyncProtocolImpl for T where T: PyAsyncProtocol { impl<T> PyAsyncProtocolImpl for T where T: PyAsyncProtocol {
@ -81,6 +94,22 @@ impl<T> PyAsyncProtocolImpl for T where T: PyAsyncProtocol {
am_anext: Self::am_anext(), am_anext: Self::am_anext(),
}) })
} }
#[inline]
fn methods() -> Vec<PyMethodDef> {
let mut methods = Vec::new();
if let Some(PyMethodDefType::Method(meth)) =
<Self as PyAsyncAenterProtocolImpl>::__aenter__() {
methods.push(meth)
}
if let Some(PyMethodDefType::Method(meth)) =
<Self as PyAsyncAexitProtocolImpl>::__aexit__() {
methods.push(meth)
}
methods
}
} }
@ -151,45 +180,27 @@ impl<T> PyAsyncAnextProtocolImpl for T
} }
trait PyAsyncAenterProtocolImpl { trait PyAsyncAenterProtocolImpl {
fn am_aenter() -> Option<ffi::unaryfunc>; fn __aenter__() -> Option<PyMethodDefType>;
} }
impl<T> PyAsyncAenterProtocolImpl for T impl<T> PyAsyncAenterProtocolImpl for T
where T: PyAsyncProtocol where T: PyAsyncProtocol
{ {
#[inline] #[inline]
default fn am_aenter() -> Option<ffi::unaryfunc> { default fn __aenter__() -> Option<PyMethodDefType> {
None None
} }
} }
impl<T> PyAsyncAenterProtocolImpl for T pub trait PyAsyncAexitProtocolImpl {
where T: PyAsyncAenterProtocol fn __aexit__() -> Option<PyMethodDefType>;
{
#[inline]
fn am_aenter() -> Option<ffi::unaryfunc> {
py_unary_func_!(PyAsyncAenterProtocol, T::__aenter__, PyObjectCallbackConverter)
}
}
trait PyAsyncAexitProtocolImpl {
fn am_aexit() -> Option<ffi::unaryfunc>;
} }
impl<T> PyAsyncAexitProtocolImpl for T impl<T> PyAsyncAexitProtocolImpl for T
where T: PyAsyncProtocol where T: PyAsyncProtocol
{ {
#[inline] #[inline]
default fn am_aexit() -> Option<ffi::unaryfunc> { default fn __aexit__() -> Option<PyMethodDefType> {
None None
} }
} }
impl<T> PyAsyncAexitProtocolImpl for T
where T: PyAsyncAexitProtocol
{
#[inline]
fn am_aexit() -> Option<ffi::unaryfunc> {
py_unary_func_!(PyAsyncAexitProtocol, T::__aexit__, PyObjectCallbackConverter)
}
}

View File

@ -15,6 +15,7 @@ pub enum PyMethodDefType {
pub enum PyMethodType { pub enum PyMethodType {
PyCFunction(ffi::PyCFunction), PyCFunction(ffi::PyCFunction),
PyCFunctionWithKeywords(ffi::PyCFunctionWithKeywords), PyCFunctionWithKeywords(ffi::PyCFunctionWithKeywords),
PyNoArgsFunction(ffi::PyNoArgsFunction),
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -56,7 +57,12 @@ impl PyMethodDef {
unsafe { unsafe {
::std::mem::transmute::< ::std::mem::transmute::<
ffi::PyCFunctionWithKeywords, ffi::PyCFunction>(meth) ffi::PyCFunctionWithKeywords, ffi::PyCFunction>(meth)
} },
PyMethodType::PyNoArgsFunction(meth) =>
unsafe {
::std::mem::transmute::<
ffi::PyNoArgsFunction, ffi::PyCFunction>(meth)
},
}; };
ffi::PyMethodDef { ffi::PyMethodDef {

View File

@ -193,6 +193,9 @@ fn py_class_method_defs<T>() -> Vec<ffi::PyMethodDef> {
_ => (), _ => (),
} }
} }
for def in <T as class::async::PyAsyncProtocolImpl>::methods() {
defs.push(def.as_method_def())
}
defs defs
} }