add PyAsyncProtocol
This commit is contained in:
parent
088d44f8d1
commit
f4feade487
2
Makefile
2
Makefile
|
@ -30,7 +30,7 @@ build: src/py_class/py_class_impl.rs
|
|||
cargo build $(CARGO_FLAGS)
|
||||
|
||||
test: build
|
||||
cargo test $(CARGO_FLAGS)
|
||||
cargo test $(CARGO_FLAGS) --lib
|
||||
|
||||
#ifeq ($(NIGHTLY),1)
|
||||
# ast-json output is only supported on nightly
|
||||
|
|
|
@ -7,6 +7,8 @@ extern crate syn;
|
|||
use std::str::FromStr;
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
use quote::{Tokens, ToTokens};
|
||||
|
||||
mod py_impl;
|
||||
use py_impl::build_py_impl;
|
||||
|
||||
|
@ -18,13 +20,15 @@ pub fn py_impl(_: TokenStream, input: TokenStream) -> TokenStream {
|
|||
|
||||
// Parse the string representation into a syntax tree
|
||||
//let ast: syn::Crate = source.parse().unwrap();
|
||||
let ast = syn::parse_item(&source).unwrap();
|
||||
let mut ast = syn::parse_item(&source).unwrap();
|
||||
|
||||
// Build the output
|
||||
let expanded = build_py_impl(&ast);
|
||||
let expanded = build_py_impl(&mut ast);
|
||||
|
||||
// Return the generated impl as a TokenStream
|
||||
let s = source + expanded.as_str();
|
||||
let mut tokens = Tokens::new();
|
||||
ast.to_tokens(&mut tokens);
|
||||
let s = String::from(tokens.as_str()) + expanded.as_str();
|
||||
|
||||
TokenStream::from_str(s.as_str()).unwrap()
|
||||
}
|
||||
|
|
|
@ -3,16 +3,22 @@ use quote;
|
|||
|
||||
|
||||
enum ImplType {
|
||||
Async,
|
||||
Buffer,
|
||||
}
|
||||
|
||||
pub fn build_py_impl(ast: &syn::Item) -> quote::Tokens {
|
||||
pub fn build_py_impl(ast: &mut syn::Item) -> quote::Tokens {
|
||||
match ast.node {
|
||||
syn::ItemKind::Impl(_, _, _, ref path, ref ty, ref impl_items) => {
|
||||
syn::ItemKind::Impl(_, _, _, ref path, ref ty, ref mut impl_items) => {
|
||||
if let &Some(ref path) = path {
|
||||
match process_path(path) {
|
||||
ImplType::Async => {
|
||||
impl_protocol("PyAsyncProtocolImpl",
|
||||
path.clone(), ty, impl_items, true)
|
||||
}
|
||||
ImplType::Buffer => {
|
||||
impl_protocol("PyBufferProtocolImpl", path.clone(), ty, impl_items)
|
||||
impl_protocol("PyBufferProtocolImpl",
|
||||
path.clone(), ty, impl_items, false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -27,6 +33,7 @@ pub fn build_py_impl(ast: &syn::Item) -> quote::Tokens {
|
|||
fn process_path(path: &syn::Path) -> ImplType {
|
||||
if let Some(segment) = path.segments.last() {
|
||||
match segment.ident.as_ref() {
|
||||
"PyAsyncProtocol" => ImplType::Async,
|
||||
"PyBufferProtocol" => ImplType::Buffer,
|
||||
_ => panic!("#[py_impl] can not be used with this block"),
|
||||
}
|
||||
|
@ -37,11 +44,21 @@ fn process_path(path: &syn::Path) -> ImplType {
|
|||
|
||||
fn impl_protocol(name: &'static str,
|
||||
path: syn::Path, ty: &Box<syn::Ty>,
|
||||
impls: &Vec<syn::ImplItem>) -> quote::Tokens {
|
||||
impls: &mut Vec<syn::ImplItem>, adjust_result: bool) -> quote::Tokens {
|
||||
// get method names in impl block
|
||||
let mut meth = Vec::new();
|
||||
for iimpl in impls.iter() {
|
||||
meth.push(String::from(iimpl.ident.as_ref()))
|
||||
for iimpl in impls.iter_mut() {
|
||||
match iimpl.node {
|
||||
syn::ImplItemKind::Method(ref mut sig, ref mut block) => {
|
||||
meth.push(String::from(iimpl.ident.as_ref()));
|
||||
|
||||
// adjust return type
|
||||
if adjust_result {
|
||||
impl_adjust_result(sig, block);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// set trait name
|
||||
|
@ -60,3 +77,69 @@ fn impl_protocol(name: &'static str,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_adjust_result(sig: &mut syn::MethodSig, block: &mut syn::Block) {
|
||||
match sig.decl.output {
|
||||
syn::FunctionRetTy::Ty(ref mut ty) => match *ty {
|
||||
syn::Ty::Path(_, ref mut path) => {
|
||||
// check if function returns PyResult
|
||||
if let Some(segment) = path.segments.last_mut() {
|
||||
match segment.ident.as_ref() {
|
||||
// check result type
|
||||
"PyResult" => match segment.parameters {
|
||||
syn::PathParameters::AngleBracketed(ref mut data) => {
|
||||
if rewrite_pyobject(&mut data.types) {
|
||||
let expr = {
|
||||
let s = block as "e::ToTokens;
|
||||
quote! {
|
||||
match #s {
|
||||
Ok(res) => Ok(res.to_py_object(py)),
|
||||
Err(err) => Err(err)
|
||||
}
|
||||
}
|
||||
};
|
||||
let expr = syn::parse_expr(&expr.as_str()).unwrap();
|
||||
let expr = syn::Stmt::Expr(Box::new(expr));
|
||||
block.stmts = vec![expr];
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
syn::FunctionRetTy::Default => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn rewrite_pyobject(path: &mut Vec<syn::Ty>) -> bool {
|
||||
if path.len() != 1 {
|
||||
false
|
||||
} else {
|
||||
if let &mut syn::Ty::Path(_, ref mut path) = path.first_mut().unwrap() {
|
||||
if let Some(segment) = path.segments.last_mut() {
|
||||
if segment.ident.as_ref() == "PyObject" {
|
||||
return false
|
||||
} else {
|
||||
segment.ident = syn::Ident::from("PyObject");
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
let ty = syn::Ty::Path(
|
||||
None, syn::Path{
|
||||
global: false,
|
||||
segments: vec![
|
||||
syn::PathSegment {
|
||||
ident: syn::Ident::from("PyObject"),
|
||||
parameters: syn::PathParameters::AngleBracketed(
|
||||
syn::AngleBracketedParameterData {
|
||||
lifetimes: vec![], types: vec![], bindings: vec![] }) }]});
|
||||
let _ = path.pop();
|
||||
let _ = path.push(ty);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
//! Represent Python Async Object Structures
|
||||
//! Trait and support implementation for implementing awaitable objects
|
||||
//!
|
||||
//! more information on python async support
|
||||
//! https://docs.python.org/3/c-api/typeobj.html#async-object-structures
|
||||
|
||||
use ffi;
|
||||
use err::{PyErr, PyResult};
|
||||
use python::{self, Python, PythonObject};
|
||||
use conversion::ToPyObject;
|
||||
use objects::{PyObject, PyType, PyModule};
|
||||
use py_class::slots::UnitCallbackConverter;
|
||||
use function::{handle_callback, PyObjectCallbackConverter};
|
||||
use class::NO_METHODS;
|
||||
|
||||
|
||||
/// Awaitable interface
|
||||
pub trait PyAsyncProtocol {
|
||||
|
||||
fn am_await(&self, py: Python) -> PyResult<PyObject>;
|
||||
|
||||
fn am_aiter(&self, py: Python) -> PyResult<PyObject>;
|
||||
|
||||
fn am_anext(&self, py: Python) -> PyResult<PyObject>;
|
||||
|
||||
}
|
||||
|
||||
|
||||
impl<P> PyAsyncProtocol for P {
|
||||
|
||||
default fn am_await(&self, py: Python) -> PyResult<PyObject> {
|
||||
Ok(py.None())
|
||||
}
|
||||
|
||||
default fn am_aiter(&self, py: Python) -> PyResult<PyObject> {
|
||||
Ok(py.None())
|
||||
}
|
||||
|
||||
default fn am_anext(&self, py: Python) -> PyResult<PyObject> {
|
||||
Ok(py.None())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait PyAsyncProtocolImpl {
|
||||
fn methods() -> &'static [&'static str];
|
||||
}
|
||||
|
||||
impl<T> PyAsyncProtocolImpl for T {
|
||||
default fn methods() -> &'static [&'static str] {
|
||||
NO_METHODS
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
&"am_await" => {
|
||||
meth.am_await = py_unary_slot!(
|
||||
PyAsyncProtocol, T::am_await,
|
||||
*mut ffi::PyObject, PyObjectCallbackConverter);
|
||||
},
|
||||
&"am_aiter" => {
|
||||
meth.am_aiter = py_unary_slot!(
|
||||
PyAsyncProtocol, T::am_aiter,
|
||||
*mut ffi::PyObject, PyObjectCallbackConverter);
|
||||
},
|
||||
&"am_anext" => {
|
||||
meth.am_anext = py_unary_slot!(
|
||||
PyAsyncProtocol, T::am_anext,
|
||||
*mut ffi::PyObject, PyObjectCallbackConverter);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
Some(meth)
|
||||
}
|
||||
}
|
|
@ -14,16 +14,17 @@ use conversion::ToPyObject;
|
|||
use objects::{PyObject, PyType, PyModule};
|
||||
use py_class::slots::UnitCallbackConverter;
|
||||
use function::handle_callback;
|
||||
use self::NO_METHODS;
|
||||
use class::NO_METHODS;
|
||||
|
||||
|
||||
/// Buffer protocol interface
|
||||
pub trait PyBufferProtocol {
|
||||
|
||||
fn bf_getbuffer(&self, py: Python, view: *mut ffi::Py_buffer, flags: c_int) -> PyResult<()>;
|
||||
|
||||
fn bf_releasebuffer(&self, py: Python, view: *mut ffi::Py_buffer) -> PyResult<()>;
|
||||
fn bf_getbuffer(&self, py: Python, view: *mut ffi::Py_buffer, flags: c_int)
|
||||
-> PyResult<()>;
|
||||
|
||||
fn bf_releasebuffer(&self, py: Python, view: *mut ffi::Py_buffer)
|
||||
-> PyResult<()>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_unary_slot {
|
||||
($trait:ident, $class:ident :: $f:ident, $res_type:ty, $conv:expr) => {{
|
||||
unsafe extern "C" fn wrap<T>(slf: *mut $crate::ffi::PyObject) -> $res_type
|
||||
where T: $trait + PythonObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
|
||||
$crate::_detail::handle_callback(
|
||||
LOCATION, $conv,
|
||||
|py| {
|
||||
let slf = $crate::PyObject::from_borrowed_ptr(
|
||||
py, slf).unchecked_cast_into::<T>();
|
||||
let ret = slf.$f(py);
|
||||
$crate::PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
})
|
||||
}
|
||||
Some(wrap::<T>)
|
||||
}}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
#[macro_use] mod macros;
|
||||
|
||||
pub mod async;
|
||||
pub mod buffer;
|
||||
|
||||
|
|
|
@ -60,6 +60,8 @@ fn main() {
|
|||
#[macro_export]
|
||||
macro_rules! py_exception {
|
||||
($module: ident, $name: ident, $base: ty) => {
|
||||
use $crate::PythonObject;
|
||||
|
||||
pub struct $name($crate::PyObject);
|
||||
|
||||
pyobject_newtype!($name);
|
||||
|
|
|
@ -84,8 +84,6 @@ extern crate libc;
|
|||
pub use pyo3cls as cls;
|
||||
|
||||
pub mod ffi;
|
||||
pub mod class;
|
||||
|
||||
pub use ffi::Py_ssize_t;
|
||||
pub use err::{PyErr, PyResult};
|
||||
pub use objects::*;
|
||||
|
@ -94,7 +92,6 @@ pub use pythonrun::{GILGuard, GILProtected, prepare_freethreaded_python};
|
|||
pub use conversion::{FromPyObject, RefFromPyObject, ToPyObject, ToPyTuple};
|
||||
pub use py_class::{CompareOp};
|
||||
pub use objectprotocol::{ObjectProtocol};
|
||||
pub use class::*;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type Py_hash_t = ffi::Py_hash_t;
|
||||
|
@ -168,6 +165,7 @@ macro_rules! py_impl_from_py_object_for_python_object {
|
|||
}
|
||||
}
|
||||
|
||||
pub mod py_class;
|
||||
mod python;
|
||||
mod err;
|
||||
mod conversion;
|
||||
|
@ -177,7 +175,8 @@ mod pythonrun;
|
|||
pub mod argparse;
|
||||
mod function;
|
||||
pub mod buffer;
|
||||
pub mod py_class;
|
||||
pub mod class;
|
||||
pub use class::*;
|
||||
|
||||
// re-export for simplicity
|
||||
pub use std::os::raw::*;
|
||||
|
@ -193,7 +192,7 @@ pub mod _detail {
|
|||
}
|
||||
pub use err::{from_owned_ptr_or_panic, result_from_owned_ptr};
|
||||
pub use function::{handle_callback, py_fn_impl, AbortOnDrop,
|
||||
PyObjectCallbackConverter, PythonObjectCallbackConverter};
|
||||
PyObjectCallbackConverter, PythonObjectCallbackConverter};
|
||||
}
|
||||
|
||||
/// Expands to an `extern "C"` function that allows Python to load
|
||||
|
|
|
@ -25,6 +25,7 @@ use conversion::ToPyObject;
|
|||
use objects::PyObject;
|
||||
use function::CallbackConverter;
|
||||
use err::{PyErr, PyResult};
|
||||
use class::*;
|
||||
use py_class::{CompareOp};
|
||||
use class::PyBufferProtocol;
|
||||
use exc;
|
||||
|
@ -99,10 +100,10 @@ macro_rules! py_class_type_object_dynamic_init {
|
|||
}
|
||||
|
||||
// call slot macros outside of unsafe block
|
||||
*(unsafe { &mut $type_object.tp_as_async }) = py_class_as_async!($as_async);
|
||||
*(unsafe { &mut $type_object.tp_as_sequence }) = py_class_as_sequence!($as_sequence);
|
||||
*(unsafe { &mut $type_object.tp_as_number }) = py_class_as_number!($as_number);
|
||||
|
||||
// buffer protocol
|
||||
if let Some(buf) = $crate::ffi::PyBufferProcs::new::<$class>() {
|
||||
static mut BUFFER_PROCS: $crate::ffi::PyBufferProcs = $crate::ffi::PyBufferProcs_INIT;
|
||||
*(unsafe { &mut BUFFER_PROCS }) = buf;
|
||||
|
@ -111,6 +112,15 @@ macro_rules! py_class_type_object_dynamic_init {
|
|||
*(unsafe { &mut $type_object.tp_as_buffer }) = 0 as *mut $crate::ffi::PyBufferProcs;
|
||||
}
|
||||
|
||||
// async methods
|
||||
if let Some(buf) = $crate::ffi::PyAsyncMethods::new::<$class>() {
|
||||
static mut ASYNC_METHODS: $crate::ffi::PyAsyncMethods = $crate::ffi::PyAsyncMethods_INIT;
|
||||
*(unsafe { &mut ASYNC_METHODS }) = buf;
|
||||
*(unsafe { &mut $type_object.tp_as_async }) = unsafe { &mut ASYNC_METHODS };
|
||||
} else {
|
||||
*(unsafe { &mut $type_object.tp_as_async }) = 0 as *mut $crate::ffi::PyAsyncMethods;
|
||||
}
|
||||
|
||||
py_class_as_mapping!($type_object, $as_mapping, $setdelitem);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue