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));
if MIN_MINOR <= interpreter_version.minor.unwrap_or(0) &&
interpreter_version.major == 3 {

View file

@ -6,7 +6,7 @@ use quote::Tokens;
pub enum MethodProto {
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},
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 {
match *self {
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::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} => {
let p = syn::Ident::from(proto);
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 {
//py_methods: &[],
methods: &[
@ -81,8 +96,9 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
impl_protocol("pyo3::class::async::PyObjectProtocolImpl",
path.clone(), ty, impl_items, &DEFAULT_METHODS),
ImplType::Async =>
impl_protocol("pyo3::class::async::PyAsyncProtocolImpl",
path.clone(), ty, impl_items, &DEFAULT_METHODS),
impl_proto_impl(ty, impl_items, &ASYNC),
ImplType::Mapping =>
impl_proto_impl(ty, impl_items, &MAPPING),
ImplType::Buffer =>
impl_protocol("pyo3::class::buffer::PyBufferProtocolImpl",
path.clone(), ty, impl_items, &DEFAULT_METHODS),
@ -101,8 +117,6 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
ImplType::Number =>
impl_protocol("pyo3::class::number::PyNumberProtocolImpl",
path.clone(), ty, impl_items, &NUM_METHODS),
ImplType::Mapping =>
impl_proto_impl(ty, impl_items, &MAPPING),
}
} else {
panic!("#[proto] can only be used with protocol trait implementations")

View file

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

View file

@ -16,21 +16,18 @@ use conversion::{ToPyObject, FromPyObject};
/// Mapping interface
#[allow(unused_variables)]
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
where Self: PyMappingGetItemProtocol
{ unimplemented!() }
fn __len__(&self, py: Python)
-> Self::Result where Self: PyMappingLenProtocol { unimplemented!() }
fn __setitem__(&self, py: Python, key: Self::Key, value: Self::Value) -> Self::Result
where Self: PyMappingSetItemProtocol
{ unimplemented!() }
fn __getitem__(&self, py: Python, key: Self::Key)
-> Self::Result where Self: PyMappingGetItemProtocol { unimplemented!() }
fn __delitem__(&self, py: Python, key: Self::Key) -> Self::Result
where Self: PyMappingDelItemProtocol
{ unimplemented!() }
fn __setitem__(&self, py: Python, key: Self::Key, value: Self::Value)
-> Self::Result where Self: PyMappingSetItemProtocol { 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
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;
*(unsafe { &mut ASYNC_METHODS }) = meth;
type_object.tp_as_async = unsafe { &mut ASYNC_METHODS };