From 9759cf7177354b84fdd9e566834786185f77ac83 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 17 May 2017 18:25:26 -0700 Subject: [PATCH] refactor PyAsyncProtocol --- build.rs | 2 +- pyo3cls/src/func.rs | 15 ++- pyo3cls/src/py_proto.rs | 22 ++++- src/class/async.rs | 214 +++++++++++++++++++++++++++++----------- src/class/macros.rs | 20 ++++ src/class/mapping.rs | 21 ++-- src/class/typeob.rs | 2 +- 7 files changed, 216 insertions(+), 80 deletions(-) diff --git a/build.rs b/build.rs index 5b9b8dcb..801ee67d 100644 --- a/build.rs +++ b/build.rs @@ -268,7 +268,7 @@ fn find_interpreter_and_get_config() -> Result<(PythonVersion, String, Vec 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, } } }, + 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); diff --git a/pyo3cls/src/py_proto.rs b/pyo3cls/src/py_proto.rs index e9d42155..314f2707 100644 --- a/pyo3cls/src/py_proto.rs +++ b/pyo3cls/src/py_proto.rs @@ -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") diff --git a/src/class/async.rs b/src/class/async.rs index 32b5afdb..e68ad5c8 100644 --- a/src/class/async.rs +++ b/src/class/async.rs @@ -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; + fn __await__(&self, py: Python) + -> Self::Result where Self: PyAsyncAwaitProtocol { unimplemented!() } - fn __aiter__(&self, py: Python) -> PyResult; + fn __aiter__(&self, py: Python) + -> Self::Result where Self: PyAsyncAiterProtocol { unimplemented!() } - fn __anext__(&self, py: Python) -> PyResult; + fn __anext__(&self, py: Python) + -> Self::Result where Self: PyAsyncAnextProtocol { unimplemented!() } - fn __aenter__(&self, py: Python) -> PyResult; + fn __aenter__(&self, py: Python) + -> Self::Result where Self: PyAsyncAenterProtocol { unimplemented!() } - fn __aexit__(&self, py: Python) -> PyResult; + fn __aexit__(&self, py: Python) + -> Self::Result where Self: PyAsyncAexitProtocol { unimplemented!() } } -impl

PyAsyncProtocol for P { +pub trait PyAsyncAwaitProtocol: PyAsyncProtocol { + type Success: ::ToPyObject; + type Result: Into>; +} - default fn __await__(&self, py: Python) -> PyResult { - Ok(py.None()) - } +pub trait PyAsyncAiterProtocol: PyAsyncProtocol { + type Success: ::ToPyObject; + type Result: Into>; +} - default fn __aiter__(&self, py: Python) -> PyResult { - Ok(py.None()) - } +pub trait PyAsyncAnextProtocol: PyAsyncProtocol { + type Success: ::ToPyObject; + type Result: Into>; +} - default fn __anext__(&self, py: Python) -> PyResult { - Ok(py.None()) - } +pub trait PyAsyncAenterProtocol: PyAsyncProtocol { + type Success: ::ToPyObject; + type Result: Into>; +} - default fn __aenter__(&self, py: Python) -> PyResult { - Ok(py.None()) - } - - default fn __aexit__(&self, py: Python) -> PyResult { - Ok(py.None()) - } +pub trait PyAsyncAexitProtocol: PyAsyncProtocol { + type Success: ::ToPyObject; + type Result: Into>; } #[doc(hidden)] pub trait PyAsyncProtocolImpl { - fn methods() -> &'static [&'static str]; + fn tp_as_async() -> Option; } impl PyAsyncProtocolImpl for T { - default fn methods() -> &'static [&'static str] { - NO_METHODS + #[inline] + default fn tp_as_async() -> Option { + None } } -impl ffi::PyAsyncMethods { - - /// Construct PyAsyncMethods struct for PyTypeObject.tp_as_async - pub fn new() -> Option - 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 PyAsyncProtocolImpl for T where T: PyAsyncProtocol { + #[inline] + fn tp_as_async() -> Option { + Some(ffi::PyAsyncMethods { + am_await: Self::am_await(), + am_aiter: Self::am_aiter(), + am_anext: Self::am_anext(), + }) + } +} + + +trait PyAsyncAwaitProtocolImpl { + fn am_await() -> Option; +} + +impl PyAsyncAwaitProtocolImpl for T + where T: PyAsyncProtocol +{ + #[inline] + default fn am_await() -> Option { + None + } +} + +impl PyAsyncAwaitProtocolImpl for T + where T: PyAsyncAwaitProtocol +{ + #[inline] + fn am_await() -> Option { + py_unary_func_!(PyAsyncAwaitProtocol, T::__await__, PyObjectCallbackConverter) + } +} + +trait PyAsyncAiterProtocolImpl { + fn am_aiter() -> Option; +} + +impl PyAsyncAiterProtocolImpl for T + where T: PyAsyncProtocol +{ + #[inline] + default fn am_aiter() -> Option { + None + } +} + +impl PyAsyncAiterProtocolImpl for T + where T: PyAsyncAiterProtocol +{ + #[inline] + fn am_aiter() -> Option { + py_unary_func_!(PyAsyncAiterProtocol, T::__aiter__, PyObjectCallbackConverter) + } +} + +trait PyAsyncAnextProtocolImpl { + fn am_anext() -> Option; +} + +impl PyAsyncAnextProtocolImpl for T + where T: PyAsyncProtocol +{ + #[inline] + default fn am_anext() -> Option { + None + } +} + +impl PyAsyncAnextProtocolImpl for T + where T: PyAsyncAnextProtocol +{ + #[inline] + fn am_anext() -> Option { + py_unary_func_!(PyAsyncAnextProtocol, T::__anext__, PyObjectCallbackConverter) + } +} + +trait PyAsyncAenterProtocolImpl { + fn am_aenter() -> Option; +} + +impl PyAsyncAenterProtocolImpl for T + where T: PyAsyncProtocol +{ + #[inline] + default fn am_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; +} + +impl PyAsyncAexitProtocolImpl for T + where T: PyAsyncProtocol +{ + #[inline] + default fn am_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/macros.rs b/src/class/macros.rs index c194d596..57a1ede5 100644 --- a/src/class/macros.rs +++ b/src/class/macros.rs @@ -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(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::(); + 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 { diff --git a/src/class/mapping.rs b/src/class/mapping.rs index 7c1717a7..237be0bd 100644 --- a/src/class/mapping.rs +++ b/src/class/mapping.rs @@ -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!() } } diff --git a/src/class/typeob.rs b/src/class/typeob.rs index 1973a30d..19135615 100644 --- a/src/class/typeob.rs +++ b/src/class/typeob.rs @@ -76,7 +76,7 @@ pub fn initialize_type(py: Python, module_name: Option<&str>, } // async methods - if let Some(meth) = ffi::PyAsyncMethods::new::() { + if let Some(meth) = ::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 };