refactor PyAsyncProtocol

This commit is contained in:
Nikolay Kim 2017-05-17 18:25:26 -07:00
parent 7a75d3e652
commit 9759cf7177
7 changed files with 216 additions and 80 deletions

View File

@ -268,7 +268,7 @@ fn find_interpreter_and_get_config() -> Result<(PythonVersion, String, Vec<Strin
} }
{ {
let interpreter_path = "python"; let interpreter_path = "python3";
let (interpreter_version, lines) = try!(get_config_from_interpreter(interpreter_path)); let (interpreter_version, lines) = try!(get_config_from_interpreter(interpreter_path));
if MIN_MINOR <= interpreter_version.minor.unwrap_or(0) && if MIN_MINOR <= interpreter_version.minor.unwrap_or(0) &&
interpreter_version.major == 3 { interpreter_version.major == 3 {

View File

@ -6,7 +6,7 @@ use quote::Tokens;
pub enum MethodProto { pub enum MethodProto {
Len{name: &'static str, proto: &'static str}, Len{name: &'static str, proto: &'static str},
//Unary(&'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},
} }
@ -16,7 +16,7 @@ impl MethodProto {
pub fn eq(&self, name: &str) -> bool { pub fn eq(&self, name: &str) -> bool {
match *self { match *self {
MethodProto::Len{name: n, proto: _} => n == name, MethodProto::Len{name: n, proto: _} => n == name,
//MethodProto::Unary(n) => 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,
} }
@ -38,6 +38,17 @@ pub fn impl_method_proto(cls: &Box<syn::Ty>,
} }
} }
}, },
MethodProto::Unary{name: _, proto} => {
let p = syn::Ident::from(proto);
let succ = get_res_success(ty);
quote! {
impl #p for #cls {
type Success = #succ;
type Result = #ty;
}
}
},
MethodProto::Binary{name: _, arg, proto} => { MethodProto::Binary{name: _, arg, proto} => {
let p = syn::Ident::from(proto); let p = syn::Ident::from(proto);
let arg_name = syn::Ident::from(arg); let arg_name = syn::Ident::from(arg);

View File

@ -37,6 +37,21 @@ static NUM_METHODS: Methods = Methods {
], ],
}; };
static ASYNC: Proto = Proto {
//py_methods: &[],
methods: &[
MethodProto::Unary{
name: "__await__",
proto: "class::mapping::PyAsyncAwaitProtocol"},
MethodProto::Unary{
name: "__aiter__",
proto: "class::mapping::PyAsyncAiterProtocol"},
MethodProto::Unary{
name: "__anext__",
proto: "class::mapping::PyAsyncAnextProtocol"},
],
};
static MAPPING: Proto = Proto { static MAPPING: Proto = Proto {
//py_methods: &[], //py_methods: &[],
methods: &[ methods: &[
@ -81,8 +96,9 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
impl_protocol("pyo3::class::async::PyObjectProtocolImpl", impl_protocol("pyo3::class::async::PyObjectProtocolImpl",
path.clone(), ty, impl_items, &DEFAULT_METHODS), path.clone(), ty, impl_items, &DEFAULT_METHODS),
ImplType::Async => ImplType::Async =>
impl_protocol("pyo3::class::async::PyAsyncProtocolImpl", impl_proto_impl(ty, impl_items, &ASYNC),
path.clone(), ty, impl_items, &DEFAULT_METHODS), ImplType::Mapping =>
impl_proto_impl(ty, impl_items, &MAPPING),
ImplType::Buffer => ImplType::Buffer =>
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),
@ -101,8 +117,6 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
ImplType::Number => ImplType::Number =>
impl_protocol("pyo3::class::number::PyNumberProtocolImpl", impl_protocol("pyo3::class::number::PyNumberProtocolImpl",
path.clone(), ty, impl_items, &NUM_METHODS), path.clone(), ty, impl_items, &NUM_METHODS),
ImplType::Mapping =>
impl_proto_impl(ty, impl_items, &MAPPING),
} }
} else { } else {
panic!("#[proto] can only be used with protocol trait implementations") panic!("#[proto] can only be used with protocol trait implementations")

View File

@ -9,93 +9,187 @@
use ffi; use ffi;
use err::PyResult; use err::PyResult;
use python::{Python, PythonObject}; use python::{Python, PythonObject};
use objects::PyObject;
use callback::PyObjectCallbackConverter; use callback::PyObjectCallbackConverter;
use class::NO_METHODS;
/// Awaitable interface /// Awaitable interface
pub trait PyAsyncProtocol { #[allow(unused_variables)]
pub trait PyAsyncProtocol: PythonObject {
fn __await__(&self, py: Python) -> PyResult<PyObject>; fn __await__(&self, py: Python)
-> Self::Result where Self: PyAsyncAwaitProtocol { unimplemented!() }
fn __aiter__(&self, py: Python) -> PyResult<PyObject>; fn __aiter__(&self, py: Python)
-> Self::Result where Self: PyAsyncAiterProtocol { unimplemented!() }
fn __anext__(&self, py: Python) -> PyResult<PyObject>; fn __anext__(&self, py: Python)
-> Self::Result where Self: PyAsyncAnextProtocol { unimplemented!() }
fn __aenter__(&self, py: Python) -> PyResult<PyObject>; fn __aenter__(&self, py: Python)
-> Self::Result where Self: PyAsyncAenterProtocol { unimplemented!() }
fn __aexit__(&self, py: Python) -> PyResult<PyObject>; fn __aexit__(&self, py: Python)
-> Self::Result where Self: PyAsyncAexitProtocol { unimplemented!() }
} }
impl<P> PyAsyncProtocol for P { pub trait PyAsyncAwaitProtocol: PyAsyncProtocol {
type Success: ::ToPyObject;
type Result: Into<PyResult<Self::Success>>;
}
default fn __await__(&self, py: Python) -> PyResult<PyObject> { pub trait PyAsyncAiterProtocol: PyAsyncProtocol {
Ok(py.None()) type Success: ::ToPyObject;
} type Result: Into<PyResult<Self::Success>>;
}
default fn __aiter__(&self, py: Python) -> PyResult<PyObject> { pub trait PyAsyncAnextProtocol: PyAsyncProtocol {
Ok(py.None()) type Success: ::ToPyObject;
} type Result: Into<PyResult<Self::Success>>;
}
default fn __anext__(&self, py: Python) -> PyResult<PyObject> { pub trait PyAsyncAenterProtocol: PyAsyncProtocol {
Ok(py.None()) type Success: ::ToPyObject;
} type Result: Into<PyResult<Self::Success>>;
}
default fn __aenter__(&self, py: Python) -> PyResult<PyObject> { pub trait PyAsyncAexitProtocol: PyAsyncProtocol {
Ok(py.None()) type Success: ::ToPyObject;
} type Result: Into<PyResult<Self::Success>>;
default fn __aexit__(&self, py: Python) -> PyResult<PyObject> {
Ok(py.None())
}
} }
#[doc(hidden)] #[doc(hidden)]
pub trait PyAsyncProtocolImpl { pub trait PyAsyncProtocolImpl {
fn methods() -> &'static [&'static str]; fn tp_as_async() -> Option<ffi::PyAsyncMethods>;
} }
impl<T> PyAsyncProtocolImpl for T { impl<T> PyAsyncProtocolImpl for T {
default fn methods() -> &'static [&'static str] { #[inline]
NO_METHODS default fn tp_as_async() -> Option<ffi::PyAsyncMethods> {
None
} }
} }
impl ffi::PyAsyncMethods { impl<T> PyAsyncProtocolImpl for T where T: PyAsyncProtocol {
#[inline]
/// Construct PyAsyncMethods struct for PyTypeObject.tp_as_async fn tp_as_async() -> Option<ffi::PyAsyncMethods> {
pub fn new<T>() -> Option<ffi::PyAsyncMethods> Some(ffi::PyAsyncMethods {
where T: PyAsyncProtocol + PyAsyncProtocolImpl + PythonObject am_await: Self::am_await(),
{ am_aiter: Self::am_aiter(),
let methods = T::methods(); am_anext: Self::am_anext(),
if methods.is_empty() { })
return None }
} }
let mut meth: ffi::PyAsyncMethods = ffi::PyAsyncMethods_INIT;
trait PyAsyncAwaitProtocolImpl {
for name in methods { fn am_await() -> Option<ffi::unaryfunc>;
match name { }
&"__await__" => {
meth.am_await = py_unary_func!( impl<T> PyAsyncAwaitProtocolImpl for T
PyAsyncProtocol, T::__await__, PyObjectCallbackConverter); where T: PyAsyncProtocol
}, {
&"__aiter__" => { #[inline]
meth.am_aiter = py_unary_func!( default fn am_await() -> Option<ffi::unaryfunc> {
PyAsyncProtocol, T::__aiter__, PyObjectCallbackConverter); None
}, }
&"__anext__" => { }
meth.am_anext = py_unary_func!(
PyAsyncProtocol, T::__anext__, PyObjectCallbackConverter); impl<T> PyAsyncAwaitProtocolImpl for T
}, where T: PyAsyncAwaitProtocol
_ => unreachable!(), {
} #[inline]
} fn am_await() -> Option<ffi::unaryfunc> {
py_unary_func_!(PyAsyncAwaitProtocol, T::__await__, PyObjectCallbackConverter)
Some(meth) }
}
trait PyAsyncAiterProtocolImpl {
fn am_aiter() -> Option<ffi::unaryfunc>;
}
impl<T> PyAsyncAiterProtocolImpl for T
where T: PyAsyncProtocol
{
#[inline]
default fn am_aiter() -> Option<ffi::unaryfunc> {
None
}
}
impl<T> PyAsyncAiterProtocolImpl for T
where T: PyAsyncAiterProtocol
{
#[inline]
fn am_aiter() -> Option<ffi::unaryfunc> {
py_unary_func_!(PyAsyncAiterProtocol, T::__aiter__, PyObjectCallbackConverter)
}
}
trait PyAsyncAnextProtocolImpl {
fn am_anext() -> Option<ffi::unaryfunc>;
}
impl<T> PyAsyncAnextProtocolImpl for T
where T: PyAsyncProtocol
{
#[inline]
default fn am_anext() -> Option<ffi::unaryfunc> {
None
}
}
impl<T> PyAsyncAnextProtocolImpl for T
where T: PyAsyncAnextProtocol
{
#[inline]
fn am_anext() -> Option<ffi::unaryfunc> {
py_unary_func_!(PyAsyncAnextProtocol, T::__anext__, PyObjectCallbackConverter)
}
}
trait PyAsyncAenterProtocolImpl {
fn am_aenter() -> Option<ffi::unaryfunc>;
}
impl<T> PyAsyncAenterProtocolImpl for T
where T: PyAsyncProtocol
{
#[inline]
default fn am_aenter() -> Option<ffi::unaryfunc> {
None
}
}
impl<T> PyAsyncAenterProtocolImpl for T
where T: PyAsyncAenterProtocol
{
#[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
where T: PyAsyncProtocol
{
#[inline]
default fn am_aexit() -> Option<ffi::unaryfunc> {
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

@ -22,6 +22,26 @@ macro_rules! py_unary_func {
}} }}
} }
#[macro_export]
#[doc(hidden)]
macro_rules! py_unary_func_ {
($trait:ident, $class:ident :: $f:ident, $conv:expr) => {{
unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject)
-> *mut $crate::ffi::PyObject
where T: $trait
{
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
$crate::callback::handle_callback(LOCATION, $conv, |py| {
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
let ret = slf.$f(py).into();
$crate::PyDrop::release_ref(slf, py);
ret
})
}
Some(wrap::<$class>)
}}
}
#[macro_export] #[macro_export]
#[doc(hidden)] #[doc(hidden)]
macro_rules! py_len_func { macro_rules! py_len_func {

View File

@ -16,21 +16,18 @@ use conversion::{ToPyObject, FromPyObject};
/// Mapping interface /// Mapping interface
#[allow(unused_variables)] #[allow(unused_variables)]
pub trait PyMappingProtocol: PythonObject { pub trait PyMappingProtocol: PythonObject {
fn __len__(&self, py: Python) -> Self::Result
where Self: PyMappingLenProtocol
{ unimplemented!() }
fn __getitem__(&self, py: Python, key: Self::Key) -> Self::Result fn __len__(&self, py: Python)
where Self: PyMappingGetItemProtocol -> Self::Result where Self: PyMappingLenProtocol { unimplemented!() }
{ unimplemented!() }
fn __setitem__(&self, py: Python, key: Self::Key, value: Self::Value) -> Self::Result fn __getitem__(&self, py: Python, key: Self::Key)
where Self: PyMappingSetItemProtocol -> Self::Result where Self: PyMappingGetItemProtocol { unimplemented!() }
{ unimplemented!() }
fn __delitem__(&self, py: Python, key: Self::Key) -> Self::Result fn __setitem__(&self, py: Python, key: Self::Key, value: Self::Value)
where Self: PyMappingDelItemProtocol -> Self::Result where Self: PyMappingSetItemProtocol { unimplemented!() }
{ unimplemented!() }
fn __delitem__(&self, py: Python, key: Self::Key)
-> Self::Result where Self: PyMappingDelItemProtocol { unimplemented!() }
} }

View File

@ -76,7 +76,7 @@ pub fn initialize_type<T>(py: Python, module_name: Option<&str>,
} }
// async methods // async methods
if let Some(meth) = ffi::PyAsyncMethods::new::<T>() { if let Some(meth) = <T as class::async::PyAsyncProtocolImpl>::tp_as_async() {
static mut ASYNC_METHODS: ffi::PyAsyncMethods = ffi::PyAsyncMethods_INIT; static mut ASYNC_METHODS: ffi::PyAsyncMethods = ffi::PyAsyncMethods_INIT;
*(unsafe { &mut ASYNC_METHODS }) = meth; *(unsafe { &mut ASYNC_METHODS }) = meth;
type_object.tp_as_async = unsafe { &mut ASYNC_METHODS }; type_object.tp_as_async = unsafe { &mut ASYNC_METHODS };