stopping point
This commit is contained in:
parent
f4feade487
commit
5427a33c75
|
@ -1,3 +1,6 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
#![recursion_limit="1024"]
|
||||
#![feature(proc_macro)]
|
||||
|
||||
extern crate proc_macro;
|
||||
|
@ -9,12 +12,13 @@ use proc_macro::TokenStream;
|
|||
|
||||
use quote::{Tokens, ToTokens};
|
||||
|
||||
mod py_impl;
|
||||
use py_impl::build_py_impl;
|
||||
mod py_class;
|
||||
mod py_proto;
|
||||
mod py_method;
|
||||
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn py_impl(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
pub fn proto(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
// Construct a string representation of the type definition
|
||||
let source = input.to_string();
|
||||
|
||||
|
@ -23,7 +27,28 @@ pub fn py_impl(_: TokenStream, input: TokenStream) -> TokenStream {
|
|||
let mut ast = syn::parse_item(&source).unwrap();
|
||||
|
||||
// Build the output
|
||||
let expanded = build_py_impl(&mut ast);
|
||||
let expanded = py_proto::build_py_proto(&mut ast);
|
||||
|
||||
// Return the generated impl as a TokenStream
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn class(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
// Construct a string representation of the type definition
|
||||
let source = input.to_string();
|
||||
|
||||
// Parse the string representation into a syntax tree
|
||||
//let ast: syn::Crate = source.parse().unwrap();
|
||||
let mut ast = syn::parse_derive_input(&source).unwrap();
|
||||
|
||||
// Build the output
|
||||
let expanded = py_class::build_py_class(&mut ast);
|
||||
|
||||
// Return the generated impl as a TokenStream
|
||||
let mut tokens = Tokens::new();
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use syn;
|
||||
use quote::{Tokens, ToTokens};
|
||||
|
||||
|
||||
pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens {
|
||||
if let syn::Body::Enum(_) = ast.body {
|
||||
panic!("#[py_class] can only be used with structs")
|
||||
}
|
||||
|
||||
let mut tokens = Tokens::new();
|
||||
impl_to_py_object(&ast.ident).to_tokens(&mut tokens);
|
||||
impl_from_py_object(&ast.ident).to_tokens(&mut tokens);
|
||||
impl_python_object(&ast.ident).to_tokens(&mut tokens);
|
||||
impl_checked_downcast(&ast.ident).to_tokens(&mut tokens);
|
||||
impl_class_init(&ast.ident).to_tokens(&mut tokens);
|
||||
|
||||
let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_CLS_{}", ast.ident));
|
||||
quote! {
|
||||
#[feature(specialization)]
|
||||
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
|
||||
const #dummy_const: () = {
|
||||
extern crate pyo3;
|
||||
use std;
|
||||
use pyo3::ffi;
|
||||
|
||||
#tokens
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_to_py_object(cls: &syn::Ident) -> Tokens {
|
||||
quote! {
|
||||
/// Identity conversion: allows using existing `PyObject` instances where
|
||||
/// `T: ToPyObject` is expected.
|
||||
impl pyo3::ToPyObject for #cls where #cls: pyo3::PythonObject {
|
||||
#[inline]
|
||||
fn to_py_object(&self, py: pyo3::Python) -> pyo3::PyObject {
|
||||
pyo3::PyClone::clone_ref(self, py).into_object()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_py_object(self, _py: pyo3::Python) -> pyo3::PyObject {
|
||||
self.into_object()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_borrowed_ptr<F, R>(&self, _py: pyo3::Python, f: F) -> R
|
||||
where F: FnOnce(*mut pyo3::ffi::PyObject) -> R
|
||||
{
|
||||
f(pyo3::PythonObject::as_object(self).as_ptr())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_from_py_object(cls: &syn::Ident) -> Tokens {
|
||||
quote! {
|
||||
impl <'source> pyo3::FromPyObject<'source> for #cls {
|
||||
#[inline]
|
||||
fn extract(py: pyo3::Python, obj: &'source pyo3::PyObject)
|
||||
-> pyo3::PyResult<#cls> {
|
||||
Ok(obj.clone_ref(py).cast_into::<#cls>(py)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl <'source> pyo3::FromPyObject<'source> for &'source #cls {
|
||||
#[inline]
|
||||
fn extract(py: pyo3::Python, obj: &'source pyo3::PyObject)
|
||||
-> pyo3::PyResult<&'source #cls> {
|
||||
Ok(obj.cast_as::<#cls>(py)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_python_object(cls: &syn::Ident) -> Tokens {
|
||||
quote! {
|
||||
impl pyo3::PythonObject for #cls {
|
||||
#[inline]
|
||||
fn as_object(&self) -> &pyo3::PyObject {
|
||||
&self._unsafe_inner
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_object(self) -> pyo3::PyObject {
|
||||
self._unsafe_inner
|
||||
}
|
||||
|
||||
/// Unchecked downcast from PyObject to Self.
|
||||
/// Undefined behavior if the input object does not have the expected type.
|
||||
#[inline]
|
||||
unsafe fn unchecked_downcast_from(obj: pyo3::PyObject) -> Self {
|
||||
#cls { _unsafe_inner: obj }
|
||||
}
|
||||
|
||||
/// Unchecked downcast from PyObject to Self.
|
||||
/// Undefined behavior if the input object does not have the expected type.
|
||||
#[inline]
|
||||
unsafe fn unchecked_downcast_borrow_from<'a>(obj: &'a pyo3::PyObject) -> &'a Self {
|
||||
std::mem::transmute(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_checked_downcast(cls: &syn::Ident) -> Tokens {
|
||||
quote! {
|
||||
impl pyo3::PythonObjectWithCheckedDowncast for #cls {
|
||||
#[inline]
|
||||
fn downcast_from<'p>(py: pyo3::Python<'p>, obj: pyo3::PyObject)
|
||||
-> Result<#cls, pyo3::PythonObjectDowncastError<'p>> {
|
||||
if py.get_type::<#cls>().is_instance(py, &obj) {
|
||||
Ok(#cls { _unsafe_inner: obj })
|
||||
} else {
|
||||
Err(pyo3::PythonObjectDowncastError(py))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn downcast_borrow_from<'a, 'p>(py: pyo3::Python<'p>, obj: &'a pyo3::PyObject)
|
||||
-> Result<&'a #cls, pyo3::PythonObjectDowncastError<'p>> {
|
||||
if py.get_type::<#cls>().is_instance(py, obj) {
|
||||
unsafe { Ok(std::mem::transmute(obj)) }
|
||||
} else {
|
||||
Err(pyo3::PythonObjectDowncastError(py))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_class_init(cls: &syn::Ident) -> Tokens {
|
||||
quote! {
|
||||
impl pyo3::class::methods::PyClassInit for #cls {
|
||||
fn init() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
use syn;
|
||||
use quote;
|
||||
|
||||
|
||||
enum ImplType {
|
||||
Async,
|
||||
Buffer,
|
||||
}
|
||||
|
||||
pub fn build_py_impl(ast: &mut syn::Item) -> quote::Tokens {
|
||||
match ast.node {
|
||||
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, false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//ImplType::Impl
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
_ => panic!("#[py_impl] can only be used with Impl blocks"),
|
||||
}
|
||||
}
|
||||
|
||||
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"),
|
||||
}
|
||||
} else {
|
||||
panic!("#[py_impl] can not be used with this block");
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_protocol(name: &'static str,
|
||||
path: syn::Path, ty: &Box<syn::Ty>,
|
||||
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_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
|
||||
let mut path = path;
|
||||
{
|
||||
let mut last = path.segments.last_mut().unwrap();
|
||||
last.ident = syn::Ident::from(name);
|
||||
}
|
||||
|
||||
quote! {
|
||||
impl #path for #ty {
|
||||
fn methods() -> &'static [&'static str] {
|
||||
static METHODS: &'static [&'static str] = &[#(#meth,),*];
|
||||
METHODS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,234 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use syn;
|
||||
use quote::Tokens;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Arg<'a> {
|
||||
pub name: &'a syn::Ident,
|
||||
pub mode: &'a syn::BindingMode,
|
||||
pub ty: &'a syn::Ty,
|
||||
pub optional: Option<&'a syn::Ty>,
|
||||
}
|
||||
|
||||
|
||||
pub fn gen_py_method<'a>(cls: &Box<syn::Ty>, name: &syn::Ident,
|
||||
sig: &mut syn::MethodSig, _block: &mut syn::Block) -> Tokens {
|
||||
check_generic(name, sig);
|
||||
|
||||
//let mut has_self = false;
|
||||
let mut py = false;
|
||||
let mut arguments: Vec<Arg> = Vec::new();
|
||||
|
||||
for input in sig.decl.inputs.iter() {
|
||||
match input {
|
||||
&syn::FnArg::SelfRef(_, _) => {
|
||||
//has_self = true;
|
||||
},
|
||||
&syn::FnArg::SelfValue(_) => {
|
||||
//has_self = true;
|
||||
}
|
||||
&syn::FnArg::Captured(ref pat, ref ty) => {
|
||||
let (mode, ident) = match pat {
|
||||
&syn::Pat::Ident(ref mode, ref ident, _) =>
|
||||
(mode, ident),
|
||||
_ =>
|
||||
panic!("unsupported argument: {:?}", pat),
|
||||
};
|
||||
// TODO add check for first py: Python arg
|
||||
if py {
|
||||
let opt = check_arg_ty_and_optional(name, ty);
|
||||
arguments.push(Arg{name: ident, mode: mode, ty: ty, optional: opt});
|
||||
} else {
|
||||
py = true;
|
||||
}
|
||||
}
|
||||
&syn::FnArg::Ignored(_) =>
|
||||
panic!("ignored argument: {:?}", name),
|
||||
}
|
||||
}
|
||||
|
||||
impl_py_method_def(name, &impl_wrap(cls, name, arguments))
|
||||
}
|
||||
|
||||
fn check_generic(name: &syn::Ident, sig: &syn::MethodSig) {
|
||||
if !sig.generics.ty_params.is_empty() {
|
||||
panic!("python method can not be generic: {:?}", name);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_arg_ty_and_optional<'a>(name: &'a syn::Ident, ty: &'a syn::Ty) -> Option<&'a syn::Ty> {
|
||||
match ty {
|
||||
&syn::Ty::Path(ref qs, ref path) => {
|
||||
if let &Some(ref qs) = qs {
|
||||
panic!("explicit Self type in a 'qualified path' is not supported: {:?} - {:?}",
|
||||
name, qs);
|
||||
}
|
||||
|
||||
if let Some(segment) = path.segments.last() {
|
||||
match segment.ident.as_ref() {
|
||||
"Option" => {
|
||||
match segment.parameters {
|
||||
syn::PathParameters::AngleBracketed(ref params) => {
|
||||
if params.types.len() != 1 {
|
||||
panic!("argument type is not supported by python method: {:?} ({:?})",
|
||||
name, ty);
|
||||
}
|
||||
Some(¶ms.types[0])
|
||||
},
|
||||
_ => {
|
||||
panic!("argument type is not supported by python method: {:?} ({:?})",
|
||||
name, ty);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
panic!("argument type is not supported by python method: {:?} ({:?})",
|
||||
name, ty);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate functiona wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||
fn impl_wrap(cls: &Box<syn::Ty>, name: &syn::Ident, args: Vec<Arg>) -> Tokens {
|
||||
let cb = impl_call(cls, name, &args);
|
||||
let body = impl_arg_params(args, cb);
|
||||
|
||||
quote! {
|
||||
unsafe extern "C" fn wrap
|
||||
(slf: *mut pyo3::ffi::PyObject,
|
||||
args: *mut pyo3::ffi::PyObject,
|
||||
kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(
|
||||
stringify!(#cls), ".", stringify!(#name), "()");
|
||||
pyo3::_detail::handle_callback(
|
||||
LOCATION, pyo3::_detail::PyObjectCallbackConverter, |py|
|
||||
{
|
||||
let args: pyo3::PyTuple =
|
||||
pyo3::PyObject::from_borrowed_ptr(py, args).unchecked_cast_into();
|
||||
let kwargs: Option<pyo3::PyDict> = pyo3::argparse::get_kwargs(py, kwargs);
|
||||
|
||||
let ret = {
|
||||
#body
|
||||
};
|
||||
pyo3::PyDrop::release_ref(args, py);
|
||||
pyo3::PyDrop::release_ref(kwargs, py);
|
||||
ret
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_call(cls: &Box<syn::Ty>, fname: &syn::Ident, args: &Vec<Arg>) -> Tokens {
|
||||
let names: Vec<&syn::Ident> = args.iter().map(|item| item.name).collect();
|
||||
quote! {
|
||||
{
|
||||
let slf = pyo3::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<#cls>();
|
||||
let ret = slf.#fname(py, #(#names),*);
|
||||
pyo3::PyDrop::release_ref(slf, py);
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_arg_params(mut args: Vec<Arg>, body: Tokens) -> Tokens {
|
||||
let mut params = Vec::new();
|
||||
|
||||
for arg in args.iter() {
|
||||
let name = arg.name.as_ref();
|
||||
let opt = if let Some(_) = arg.optional {
|
||||
syn::Ident::from("true")
|
||||
} else {
|
||||
syn::Ident::from("false")
|
||||
};
|
||||
params.push(
|
||||
quote! {
|
||||
pyo3::argparse::ParamDescription{name: #name, is_optional: #opt,}
|
||||
}
|
||||
);
|
||||
}
|
||||
let placeholders: Vec<syn::Ident> = params.iter().map(
|
||||
|_| syn::Ident::from("None")).collect();
|
||||
|
||||
// generate extrat args
|
||||
args.reverse();
|
||||
let mut body = body;
|
||||
for arg in args.iter() {
|
||||
body = impl_arg_param(&arg, &body);
|
||||
}
|
||||
|
||||
// create array of arguments, and then parse
|
||||
quote! {
|
||||
const PARAMS: &'static [pyo3::argparse::ParamDescription<'static>] = &[
|
||||
#(#params),*
|
||||
];
|
||||
|
||||
let mut output = [#(#placeholders),*];
|
||||
match pyo3::argparse::parse_args(
|
||||
py, Some(LOCATION), PARAMS, &args, kwargs.as_ref(), &mut output) {
|
||||
Ok(_) => {
|
||||
let mut _iter = output.iter();
|
||||
|
||||
#body
|
||||
},
|
||||
Err(err) => Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_arg_param(arg: &Arg, body: &Tokens) -> Tokens {
|
||||
let ty = arg.ty;
|
||||
let name = arg.name;
|
||||
|
||||
// First unwrap() asserts the iterated sequence is long enough (which should be guaranteed);
|
||||
// second unwrap() asserts the parameter was not missing (which fn
|
||||
// parse_args already checked for).
|
||||
|
||||
if let Some(ref opt_ty) = arg.optional {
|
||||
quote! {
|
||||
match match _iter.next().unwrap().as_ref() {
|
||||
Some(obj) => {
|
||||
match <#opt_ty as pyo3::FromPyObject>::extract(py, obj) {
|
||||
Ok(obj) => Ok(Some(obj)),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
},
|
||||
None => Ok(None)
|
||||
} {
|
||||
Ok(#name) => #body,
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
match <#ty as pyo3::FromPyObject>::extract(
|
||||
py, _iter.next().unwrap().as_ref().unwrap())
|
||||
{
|
||||
Ok(#name) => {
|
||||
#body
|
||||
}
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_py_method_def(name: &syn::Ident, wrapper: &Tokens) -> Tokens {
|
||||
quote! {{
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#name),
|
||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(wrap),
|
||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS,
|
||||
ml_doc: "",
|
||||
}
|
||||
}}
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use syn;
|
||||
use quote::{Tokens, ToTokens};
|
||||
|
||||
use py_method;
|
||||
|
||||
|
||||
struct Methods {
|
||||
methods: &'static [&'static str],
|
||||
non_pyobj_result: &'static [&'static str],
|
||||
}
|
||||
|
||||
static DEFAULT_METHODS: Methods = Methods {
|
||||
methods: &[],
|
||||
non_pyobj_result: &[],
|
||||
};
|
||||
|
||||
static BUFFER_METHODS: Methods = Methods {
|
||||
methods: &[],
|
||||
non_pyobj_result: &["bf_getbuffer", "bf_releasebuffer"],
|
||||
};
|
||||
|
||||
static GC_METHODS: Methods = Methods {
|
||||
methods: &[],
|
||||
non_pyobj_result: &["__traverse__", "__clear__"],
|
||||
};
|
||||
|
||||
static CONTEXT_METHODS: Methods = Methods {
|
||||
methods: &["__enter__", "__exit__"],
|
||||
non_pyobj_result: &[],
|
||||
};
|
||||
|
||||
|
||||
enum ImplType {
|
||||
Async,
|
||||
Buffer,
|
||||
Context,
|
||||
GC,
|
||||
}
|
||||
|
||||
pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
|
||||
match ast.node {
|
||||
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("pyo3::class::async::PyAsyncProtocolImpl",
|
||||
path.clone(), ty, impl_items, &DEFAULT_METHODS)
|
||||
}
|
||||
ImplType::Buffer => {
|
||||
impl_protocol("pyo3::class::buffer::PyBufferProtocolImpl",
|
||||
path.clone(), ty, impl_items, &BUFFER_METHODS)
|
||||
}
|
||||
ImplType::Context => {
|
||||
impl_protocol("pyo3::class::context::PyContextProtocolImpl",
|
||||
path.clone(), ty, impl_items, &CONTEXT_METHODS)
|
||||
}
|
||||
ImplType::GC => {
|
||||
impl_protocol("pyo3::class::gc::PyGCProtocolImpl",
|
||||
path.clone(), ty, impl_items, &GC_METHODS)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("#[py_proto] can only be used with protocol trait implementations")
|
||||
}
|
||||
},
|
||||
_ => panic!("#[py_proto] can only be used with Impl blocks"),
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
"PyContextProtocol" => ImplType::Context,
|
||||
"PyGCProtocol" => ImplType::GC,
|
||||
_ => panic!("#[py_proto] can not be used with this block"),
|
||||
}
|
||||
} else {
|
||||
panic!("#[py_proto] can not be used with this block");
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_protocol(name: &'static str,
|
||||
path: syn::Path, ty: &Box<syn::Ty>,
|
||||
impls: &mut Vec<syn::ImplItem>, methods: &Methods) -> Tokens {
|
||||
let mut py_methods = Vec::new();
|
||||
|
||||
// get method names in impl block
|
||||
let mut meth = Vec::new();
|
||||
for iimpl in impls.iter_mut() {
|
||||
match iimpl.node {
|
||||
syn::ImplItemKind::Method(ref mut sig, ref mut block) => {
|
||||
if methods.methods.contains(&iimpl.ident.as_ref()) {
|
||||
py_methods.push(py_method::gen_py_method(ty, &iimpl.ident, sig, block));
|
||||
} else {
|
||||
meth.push(String::from(iimpl.ident.as_ref()));
|
||||
|
||||
// adjust return type
|
||||
if !methods.non_pyobj_result.contains(&iimpl.ident.as_ref()) {
|
||||
impl_adjust_result(sig, block);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// set trait name
|
||||
let mut path = path;
|
||||
{
|
||||
let mut last = path.segments.last_mut().unwrap();
|
||||
last.ident = syn::Ident::from(name);
|
||||
}
|
||||
|
||||
let i = syn::Ident::from(name);
|
||||
let tokens = if py_methods.is_empty() {
|
||||
quote! {
|
||||
impl #i for #ty {
|
||||
fn methods() -> &'static [&'static str] {
|
||||
static METHODS: &'static [&'static str] = &[#(#meth),*];
|
||||
METHODS
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
impl #i for #ty {
|
||||
fn methods() -> &'static [&'static str] {
|
||||
static METHODS: &'static [&'static str] = &[#(#meth,),*];
|
||||
METHODS
|
||||
}
|
||||
|
||||
fn py_methods() -> &'static [pyo3::class::PyMethodDef] {
|
||||
static METHODS: &'static [pyo3::class::PyMethodDef] = &[
|
||||
#(#py_methods),*
|
||||
];
|
||||
METHODS
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let name = name.split("::").last().unwrap();
|
||||
let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_{}", name));
|
||||
quote! {
|
||||
#[feature(specialization)]
|
||||
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
|
||||
const #dummy_const: () = {
|
||||
extern crate pyo3;
|
||||
use pyo3::ffi;
|
||||
|
||||
#tokens
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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 &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
|
||||
}
|
||||
}
|
|
@ -42,14 +42,13 @@ pub struct ParamDescription<'a> {
|
|||
/// * kwargs: Keyword arguments
|
||||
/// * output: Output array that receives the arguments.
|
||||
/// Must have same length as `params` and must be initialized to `None`.
|
||||
pub fn parse_args(
|
||||
py: Python,
|
||||
pub fn parse_args(py: Python,
|
||||
fname: Option<&str>, params: &[ParamDescription],
|
||||
args: &PyTuple, kwargs: Option<&PyDict>,
|
||||
output: &mut[Option<PyObject>]
|
||||
) -> PyResult<()>
|
||||
output: &mut[Option<PyObject>]) -> PyResult<()>
|
||||
{
|
||||
assert!(params.len() == output.len());
|
||||
|
||||
let nargs = args.len(py);
|
||||
let nkeywords = kwargs.map_or(0, |d| d.len(py));
|
||||
if nargs + nkeywords > params.len() {
|
||||
|
|
|
@ -19,26 +19,38 @@ use class::NO_METHODS;
|
|||
/// Awaitable interface
|
||||
pub trait PyAsyncProtocol {
|
||||
|
||||
fn am_await(&self, py: Python) -> PyResult<PyObject>;
|
||||
fn __await__(&self, py: Python) -> PyResult<PyObject>;
|
||||
|
||||
fn am_aiter(&self, py: Python) -> PyResult<PyObject>;
|
||||
fn __aiter__(&self, py: Python) -> PyResult<PyObject>;
|
||||
|
||||
fn am_anext(&self, py: Python) -> PyResult<PyObject>;
|
||||
fn __anext__(&self, py: Python) -> PyResult<PyObject>;
|
||||
|
||||
fn __aenter__(&self, py: Python) -> PyResult<PyObject>;
|
||||
|
||||
fn __aexit__(&self, py: Python) -> PyResult<PyObject>;
|
||||
|
||||
}
|
||||
|
||||
|
||||
impl<P> PyAsyncProtocol for P {
|
||||
|
||||
default fn am_await(&self, py: Python) -> PyResult<PyObject> {
|
||||
default fn __await__(&self, py: Python) -> PyResult<PyObject> {
|
||||
Ok(py.None())
|
||||
}
|
||||
|
||||
default fn am_aiter(&self, py: Python) -> PyResult<PyObject> {
|
||||
default fn __aiter__(&self, py: Python) -> PyResult<PyObject> {
|
||||
Ok(py.None())
|
||||
}
|
||||
|
||||
default fn am_anext(&self, py: Python) -> PyResult<PyObject> {
|
||||
default fn __anext__(&self, py: Python) -> PyResult<PyObject> {
|
||||
Ok(py.None())
|
||||
}
|
||||
|
||||
default fn __aenter__(&self, py: Python) -> PyResult<PyObject> {
|
||||
Ok(py.None())
|
||||
}
|
||||
|
||||
default fn __aexit__(&self, py: Python) -> PyResult<PyObject> {
|
||||
Ok(py.None())
|
||||
}
|
||||
}
|
||||
|
@ -70,19 +82,19 @@ impl ffi::PyAsyncMethods {
|
|||
|
||||
for name in methods {
|
||||
match name {
|
||||
&"am_await" => {
|
||||
&"__await__" => {
|
||||
meth.am_await = py_unary_slot!(
|
||||
PyAsyncProtocol, T::am_await,
|
||||
PyAsyncProtocol, T::__await__,
|
||||
*mut ffi::PyObject, PyObjectCallbackConverter);
|
||||
},
|
||||
&"am_aiter" => {
|
||||
&"__aiter__" => {
|
||||
meth.am_aiter = py_unary_slot!(
|
||||
PyAsyncProtocol, T::am_aiter,
|
||||
PyAsyncProtocol, T::__aiter__,
|
||||
*mut ffi::PyObject, PyObjectCallbackConverter);
|
||||
},
|
||||
&"am_anext" => {
|
||||
&"__anext__" => {
|
||||
meth.am_anext = py_unary_slot!(
|
||||
PyAsyncProtocol, T::am_anext,
|
||||
PyAsyncProtocol, T::__anext__,
|
||||
*mut ffi::PyObject, PyObjectCallbackConverter);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
//! Basic Python Object customization
|
||||
//!
|
||||
//! more information on python async support
|
||||
//! https://docs.python.org/3/reference/datamodel.html#basic-customization
|
||||
|
||||
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;
|
||||
|
||||
|
||||
/// Basic customization
|
||||
pub trait PyObjectProtocol {
|
||||
|
||||
// fn __new__(&self, py: Python) -> PyResult<PyObject>;
|
||||
|
||||
fn __str__(&self, py: Python) -> PyResult<PyString>;
|
||||
|
||||
fn __repr__(&self, py: Python) -> PyResult<PyString>;
|
||||
|
||||
fn __hash__(&self, py: Python) -> PyResult<PyObject>;
|
||||
|
||||
fn __bool__(&self, py: Python) -> PyResult<bool>;
|
||||
|
||||
fn __richcmp__(&self, other: PyObject, op: pyo3::CompareOp) -> PyResult<bool>;
|
||||
|
||||
fn __call__(&self) -> PyResult<PyObject>;
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
//! Context manager api
|
||||
//! Trait and support implementation for context manager api
|
||||
//!
|
||||
|
||||
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, NO_PY_METHODS};
|
||||
|
||||
|
||||
/// Awaitable interface
|
||||
pub trait PyContextProtocol {
|
||||
|
||||
fn __enter__(&self, py: Python) -> PyResult<PyObject>;
|
||||
|
||||
fn __exit__(&self, py: Python,
|
||||
exc_type: Option<PyObject>,
|
||||
exc_value: Option<PyObject>,
|
||||
traceback: Option<PyObject>) -> PyResult<PyObject>;
|
||||
}
|
||||
|
||||
|
||||
impl<P> PyContextProtocol for P {
|
||||
|
||||
default fn __enter__(&self, py: Python) -> PyResult<PyObject> {
|
||||
Ok(py.None())
|
||||
}
|
||||
|
||||
default fn __exit__(&self, py: Python,
|
||||
_exc_type: Option<PyObject>,
|
||||
_exc_value: Option<PyObject>,
|
||||
_traceback: Option<PyObject>) -> PyResult<PyObject> {
|
||||
Ok(py.None())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait PyContextProtocolImpl {
|
||||
fn methods() -> &'static [&'static str];
|
||||
|
||||
fn py_methods() -> &'static [::class::PyMethodDef];
|
||||
}
|
||||
|
||||
impl<T> PyContextProtocolImpl for T {
|
||||
default fn methods() -> &'static [&'static str] {
|
||||
NO_METHODS
|
||||
}
|
||||
|
||||
default fn py_methods() -> &'static [::class::PyMethodDef] {
|
||||
NO_PY_METHODS
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// 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)
|
||||
}
|
||||
*/
|
|
@ -0,0 +1,118 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
//! Python GC support
|
||||
//!
|
||||
|
||||
use std::mem;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
|
||||
use ffi;
|
||||
use err::{PyErr, PyResult};
|
||||
use python::{self, Python, PythonObject, PyDrop, ToPythonPointer};
|
||||
use conversion::ToPyObject;
|
||||
use objects::{PyObject, PyType, PyModule};
|
||||
use py_class::slots::UnitCallbackConverter;
|
||||
use function::{handle_callback, PyObjectCallbackConverter, AbortOnDrop};
|
||||
use class::NO_METHODS;
|
||||
|
||||
pub struct PyTraverseError(c_int);
|
||||
|
||||
/// GC support
|
||||
pub trait PyGCProtocol {
|
||||
|
||||
fn __traverse__(&self, py: Python, visit: PyVisit) -> Result<(), PyTraverseError>;
|
||||
|
||||
fn __clear__(&self, py: Python);
|
||||
|
||||
}
|
||||
|
||||
impl<T> PyGCProtocol for T {
|
||||
default fn __traverse__(&self, _: Python, _: PyVisit) -> Result<(), PyTraverseError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
default fn __clear__(&self, _: Python) {}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait PyGCProtocolImpl {
|
||||
fn methods() -> &'static [&'static str];
|
||||
|
||||
fn update_type_object(type_object: &mut ffi::PyTypeObject);
|
||||
}
|
||||
|
||||
impl<T> PyGCProtocolImpl for T where T: PyGCProtocol + PythonObject {
|
||||
default fn methods() -> &'static [&'static str] {
|
||||
NO_METHODS
|
||||
}
|
||||
|
||||
fn update_type_object(type_object: &mut ffi::PyTypeObject) {
|
||||
if <T as PyGCProtocolImpl>::methods().is_empty() {
|
||||
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT
|
||||
} else {
|
||||
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HAVE_GC;
|
||||
type_object.tp_traverse = Some(tp_traverse::<T>);
|
||||
type_object.tp_clear = Some(tp_clear::<T>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PyVisit<'a> {
|
||||
visit: ffi::visitproc,
|
||||
arg: *mut c_void,
|
||||
/// VisitProc contains a Python instance to ensure that
|
||||
/// 1) it is cannot be moved out of the traverse() call
|
||||
/// 2) it cannot be sent to other threads
|
||||
_py: Python<'a>
|
||||
}
|
||||
|
||||
impl <'a> PyVisit<'a> {
|
||||
pub fn call<T>(&self, obj: &T) -> Result<(), PyTraverseError>
|
||||
where T: PythonObject
|
||||
{
|
||||
let r = unsafe { (self.visit)(obj.as_ptr(), self.arg) };
|
||||
if r == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(PyTraverseError(r))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe extern "C" fn tp_traverse<T>(slf: *mut ffi::PyObject,
|
||||
visit: ffi::visitproc,
|
||||
arg: *mut c_void) -> c_int
|
||||
where T: PyGCProtocol + PythonObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!(T), ".__traverse__()");
|
||||
|
||||
let guard = AbortOnDrop(LOCATION);
|
||||
let py = Python::assume_gil_acquired();
|
||||
let visit = PyVisit { visit: visit, arg: arg, _py: py };
|
||||
let slf = PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
|
||||
|
||||
let ret = match T::__traverse__(&slf, py, visit) {
|
||||
Ok(()) => 0,
|
||||
Err(PyTraverseError(code)) => code
|
||||
};
|
||||
slf.release_ref(py);
|
||||
mem::forget(guard);
|
||||
ret
|
||||
}
|
||||
|
||||
unsafe extern "C" fn tp_clear<T>(slf: *mut ffi::PyObject) -> c_int
|
||||
where T: PyGCProtocol + PythonObject
|
||||
{
|
||||
const LOCATION: &'static str = concat!(stringify!(T), ".__clear__()");
|
||||
|
||||
let guard = AbortOnDrop(LOCATION);
|
||||
let py = Python::assume_gil_acquired();
|
||||
let slf = PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<T>();
|
||||
T::__clear__(&slf, py);
|
||||
slf.release_ref(py);
|
||||
mem::forget(guard);
|
||||
0
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::ffi::CString;
|
||||
|
||||
use ::{ffi, exc, class, py_class, PyErr, Python, PyResult, PythonObject};
|
||||
use objects::PyType;
|
||||
use function::AbortOnDrop;
|
||||
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum PyMethodType {
|
||||
PyCFunction(ffi::PyCFunction),
|
||||
PyCFunctionWithKeywords(ffi::PyCFunctionWithKeywords),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PyMethodDef {
|
||||
pub ml_name: &'static str,
|
||||
pub ml_meth: PyMethodType,
|
||||
pub ml_flags: ::c_int,
|
||||
pub ml_doc: &'static str,
|
||||
}
|
||||
|
||||
unsafe impl Sync for PyMethodDef {}
|
||||
unsafe impl Sync for ffi::PyMethodDef {}
|
||||
|
||||
|
||||
pub trait PyClassInit {
|
||||
|
||||
fn init() -> bool;
|
||||
|
||||
fn type_object() -> &'static mut ffi::PyTypeObject;
|
||||
|
||||
fn build_type(py: Python,
|
||||
module_name: Option<&str>,
|
||||
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType>;
|
||||
|
||||
}
|
||||
|
||||
impl<T> PyClassInit for T where T: PythonObject + py_class::BaseObject {
|
||||
|
||||
default fn init() -> bool { false }
|
||||
|
||||
default fn type_object() -> &'static mut ffi::PyTypeObject {
|
||||
static mut TYPE_OBJECT: ffi::PyTypeObject = ffi::PyTypeObject_INIT;
|
||||
unsafe {
|
||||
&mut TYPE_OBJECT
|
||||
}
|
||||
}
|
||||
|
||||
default fn build_type(py: Python, module_name: Option<&str>,
|
||||
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType> {
|
||||
// type name
|
||||
let name = match module_name {
|
||||
Some(module_name) => CString::new(
|
||||
format!("{}.{}", module_name, stringify!(type_name))),
|
||||
None => CString::new(stringify!(type_name))
|
||||
};
|
||||
let name = name.expect(
|
||||
"Module name/type name must not contain NUL byte").into_raw();
|
||||
|
||||
type_object.tp_name = name;
|
||||
|
||||
// dealloc
|
||||
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);
|
||||
|
||||
// GC support
|
||||
//<T as class::gc::PyGCProtocolImpl>::update_type_object(type_object);
|
||||
|
||||
// type size
|
||||
type_object.tp_basicsize = <T as py_class::BaseObject>::size() as ffi::Py_ssize_t;
|
||||
|
||||
// buffer protocol
|
||||
if let Some(buf) = ffi::PyBufferProcs::new::<T>() {
|
||||
static mut BUFFER_PROCS: ffi::PyBufferProcs = ffi::PyBufferProcs_INIT;
|
||||
*(unsafe { &mut BUFFER_PROCS }) = buf;
|
||||
type_object.tp_as_buffer = unsafe { &mut BUFFER_PROCS };
|
||||
} else {
|
||||
type_object.tp_as_buffer = 0 as *mut ffi::PyBufferProcs;
|
||||
}
|
||||
|
||||
// async methods
|
||||
if let Some(buf) = ffi::PyAsyncMethods::new::<T>() {
|
||||
static mut ASYNC_METHODS: ffi::PyAsyncMethods = ffi::PyAsyncMethods_INIT;
|
||||
*(unsafe { &mut ASYNC_METHODS }) = buf;
|
||||
type_object.tp_as_async = unsafe { &mut ASYNC_METHODS };
|
||||
} else {
|
||||
type_object.tp_as_async = 0 as *mut ffi::PyAsyncMethods;
|
||||
}
|
||||
|
||||
// normal methods
|
||||
let mut methods = class::methods::py_class_method_defs::<T>();
|
||||
if !methods.is_empty() {
|
||||
methods.push(ffi::PyMethodDef_INIT);
|
||||
type_object.tp_methods = methods.as_ptr() as *mut _;
|
||||
|
||||
static mut METHODS: *const ffi::PyMethodDef = 0 as *const _;
|
||||
*(unsafe { &mut METHODS }) = methods.as_ptr();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
if ffi::PyType_Ready(type_object) == 0 {
|
||||
Ok(PyType::from_type_ptr(py, type_object))
|
||||
} else {
|
||||
Err(PyErr::fetch(py))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject)
|
||||
where T: py_class::BaseObject
|
||||
{
|
||||
let guard = AbortOnDrop("Cannot unwind out of tp_dealloc");
|
||||
let py = Python::assume_gil_acquired();
|
||||
let r = T::dealloc(py, obj);
|
||||
mem::forget(guard);
|
||||
r
|
||||
}
|
||||
|
||||
pub fn py_class_method_defs<T>() -> Vec<ffi::PyMethodDef> {
|
||||
let mut defs = Vec::new();
|
||||
|
||||
for def in <T as class::context::PyContextProtocolImpl>::py_methods() {
|
||||
let meth = match def.ml_meth {
|
||||
PyMethodType::PyCFunction(meth) => meth,
|
||||
PyMethodType::PyCFunctionWithKeywords(meth) =>
|
||||
unsafe {
|
||||
::std::mem::transmute::<
|
||||
ffi::PyCFunctionWithKeywords, ffi::PyCFunction>(meth)
|
||||
}
|
||||
};
|
||||
|
||||
let fdef = ffi::PyMethodDef {
|
||||
ml_name: CString::new(def.ml_name).expect(
|
||||
"Method name must not contain NULL byte").into_raw(),
|
||||
ml_meth: Some(meth),
|
||||
ml_flags: def.ml_flags,
|
||||
ml_doc: 0 as *const ::c_char,
|
||||
};
|
||||
defs.push(fdef)
|
||||
}
|
||||
|
||||
defs
|
||||
}
|
|
@ -4,8 +4,17 @@
|
|||
|
||||
pub mod async;
|
||||
pub mod buffer;
|
||||
pub mod context;
|
||||
pub mod methods;
|
||||
pub mod gc;
|
||||
|
||||
pub use self::async::*;
|
||||
pub use self::buffer::*;
|
||||
pub use self::context::*;
|
||||
pub use self::gc::{PyVisit, PyGCProtocol, PyTraverseError};
|
||||
pub use self::methods::{PyMethodDef, PyMethodType};
|
||||
|
||||
use self::gc::PyGCProtocolImpl;
|
||||
|
||||
pub static NO_METHODS: &'static [&'static str] = &[];
|
||||
pub static NO_PY_METHODS: &'static [PyMethodDef] = &[];
|
||||
|
|
|
@ -48,6 +48,13 @@ pub struct PyMethodDef {
|
|||
pub ml_doc: *const c_char,
|
||||
}
|
||||
|
||||
pub const PyMethodDef_INIT : PyMethodDef = PyMethodDef {
|
||||
ml_name: 0 as *const _,
|
||||
ml_meth: None,
|
||||
ml_flags: 0,
|
||||
ml_doc: 0 as *const _,
|
||||
};
|
||||
|
||||
impl Clone for PyMethodDef {
|
||||
#[inline] fn clone(&self) -> PyMethodDef { *self }
|
||||
}
|
||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -16,11 +16,13 @@
|
|||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#![cfg_attr(feature="nightly", feature(
|
||||
const_fn, // for GILProtected::new (#24111)
|
||||
shared, // for std::ptr::Shared (#27730)
|
||||
specialization, // for impl FromPyObject<'source> for Vec<...> (#31844)
|
||||
))]
|
||||
//#![cfg_attr(feature="nightly", feature(
|
||||
// const_fn, // for GILProtected::new (#24111)
|
||||
// shared, // for std::ptr::Shared (#27730)
|
||||
// specialization, // for impl FromPyObject<'source> for Vec<...> (#31844)
|
||||
//))]
|
||||
|
||||
#![feature(specialization, shared, const_fn)]
|
||||
|
||||
#![allow(unused_imports)] // because some imports are only necessary with python 2.x or 3.x
|
||||
|
||||
|
|
|
@ -1,173 +0,0 @@
|
|||
// Copyright (c) 2016 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use std::os::raw::{c_void, c_int};
|
||||
use ffi;
|
||||
use std::mem;
|
||||
use python::{Python, PythonObject, PyDrop, ToPythonPointer};
|
||||
use objects::PyObject;
|
||||
use function::AbortOnDrop;
|
||||
|
||||
// TODO: what's the semantics of the traverse return code?
|
||||
// If it's just a normal python exception, we might want to use PyErr instead.
|
||||
pub struct TraverseError(c_int);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct VisitProc<'a> {
|
||||
visit: ffi::visitproc,
|
||||
arg: *mut c_void,
|
||||
/// VisitProc contains a Python instance to ensure that
|
||||
/// 1) it is cannot be moved out of the traverse() call
|
||||
/// 2) it cannot be sent to other threads
|
||||
_py: Python<'a>
|
||||
}
|
||||
|
||||
impl <'a> VisitProc<'a> {
|
||||
pub fn call<T>(&self, obj: &T) -> Result<(), TraverseError>
|
||||
where T: PythonObject
|
||||
{
|
||||
let r = unsafe { (self.visit)(obj.as_ptr(), self.arg) };
|
||||
if r == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(TraverseError(r))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_tp_traverse {
|
||||
($class_name:ident,
|
||||
/* gc: */ {
|
||||
/* traverse_proc: */ None,
|
||||
/* traverse_data: */ [ ]
|
||||
}) => {
|
||||
// If there's nothing to traverse, we don't need to generate
|
||||
// tp_traverse.
|
||||
// Note that in this case, py_class_type_object_flags! must not
|
||||
// use Py_TPFLAGS_HAVE_GC.
|
||||
None
|
||||
};
|
||||
($class_name:ident,
|
||||
/* gc: */ {
|
||||
$traverse_proc: expr,
|
||||
/* traverse_data: */ []
|
||||
}) => {{
|
||||
unsafe extern "C" fn tp_traverse(
|
||||
slf: *mut $crate::_detail::ffi::PyObject,
|
||||
visit: $crate::_detail::ffi::visitproc,
|
||||
arg: *mut $crate::_detail::libc::c_void
|
||||
) -> $crate::_detail::libc::c_int
|
||||
{
|
||||
$crate::py_class::gc::tp_traverse::<$class_name, _>(
|
||||
concat!(stringify!($class_name), ".__traverse__"),
|
||||
slf, visit, arg, $traverse_proc)
|
||||
}
|
||||
Some(tp_traverse)
|
||||
}};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn tp_traverse<C, F>(
|
||||
location: &str,
|
||||
slf: *mut ffi::PyObject,
|
||||
visit: ffi::visitproc,
|
||||
arg: *mut c_void,
|
||||
callback: F) -> c_int
|
||||
where C: PythonObject,
|
||||
F: FnOnce(&C, Python, VisitProc) -> Result<(), TraverseError>
|
||||
{
|
||||
let guard = AbortOnDrop(location);
|
||||
let py = Python::assume_gil_acquired();
|
||||
let visit = VisitProc { visit: visit, arg: arg, _py: py };
|
||||
let slf = PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<C>();
|
||||
let ret = match callback(&slf, py, visit) {
|
||||
Ok(()) => 0,
|
||||
Err(TraverseError(code)) => code
|
||||
};
|
||||
slf.release_ref(py);
|
||||
mem::forget(guard);
|
||||
ret
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_tp_clear {
|
||||
($class_name:ident) => {{
|
||||
unsafe extern "C" fn tp_clear(
|
||||
slf: *mut $crate::_detail::ffi::PyObject
|
||||
) -> $crate::_detail::libc::c_int
|
||||
{
|
||||
$crate::py_class::gc::tp_clear::<$class_name, _>(
|
||||
concat!(stringify!($class_name), ".__clear__"),
|
||||
slf, $class_name::__clear__)
|
||||
}
|
||||
Some(tp_clear)
|
||||
}}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn tp_clear<C, F>(
|
||||
location: &str,
|
||||
slf: *mut ffi::PyObject,
|
||||
callback: F) -> c_int
|
||||
where C: PythonObject,
|
||||
F: FnOnce(&C, Python)
|
||||
{
|
||||
let guard = AbortOnDrop(location);
|
||||
let py = Python::assume_gil_acquired();
|
||||
let slf = PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<C>();
|
||||
callback(&slf, py);
|
||||
slf.release_ref(py);
|
||||
mem::forget(guard);
|
||||
0
|
||||
}
|
||||
|
||||
/*
|
||||
/// Trait that has to be implemented by `#[gc_traverse]` members.
|
||||
pub trait Traversable {
|
||||
/// Call VisitProc for all python objects owned by this value.
|
||||
fn traverse(&self, py: Python, visit: VisitProc) -> Result<(), TraverseError>;
|
||||
}
|
||||
|
||||
impl <T> Traversable for T where T: PythonObject {
|
||||
fn traverse(&self, _py: Python, visit: VisitProc) -> Result<(), TraverseError> {
|
||||
visit.call(self.as_object())
|
||||
}
|
||||
}
|
||||
|
||||
impl <T> Traversable for Option<T> where T: Traversable {
|
||||
fn traverse(&self, py: Python, visit: VisitProc) -> Result<(), TraverseError> {
|
||||
match *self {
|
||||
Some(ref val) => val.traverse(py, visit),
|
||||
None => Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <T> Traversable for Vec<T> where T: Traversable {
|
||||
fn traverse(&self, py: Python, visit: VisitProc) -> Result<(), TraverseError> {
|
||||
for val in self {
|
||||
try!(val.traverse(py, visit));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
@ -21,7 +21,6 @@ mod py_class_impl;
|
|||
#[doc(hidden)] pub mod slots;
|
||||
#[doc(hidden)] pub mod members;
|
||||
#[doc(hidden)] pub mod properties;
|
||||
pub mod gc;
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use std::{mem, ptr, cell};
|
||||
|
|
|
@ -789,25 +789,6 @@ special_names = {
|
|||
# With statement context managers
|
||||
'__enter__': normal_method(),
|
||||
'__exit__': normal_method(),
|
||||
|
||||
# Coroutines
|
||||
'__await__': operator('am_await'),
|
||||
'__aiter__': operator('am_aiter'),
|
||||
'__anext__': operator('am_anext'),
|
||||
'__aenter__': unimplemented(),
|
||||
'__aexit__': unimplemented(),
|
||||
|
||||
# Buffer
|
||||
'__buffer_get__': operator(
|
||||
"bf_getbuffer",
|
||||
args=[Argument('view', '*mut $crate::_detail::ffi::Py_buffer'), Argument('flags', 'int')],
|
||||
wrapper="py_class_ternary_internal",
|
||||
res_type='bool', res_conv='$crate::py_class::slots::SuccessConverter',
|
||||
res_ffi_type = '$crate::_detail::libc::c_int'),
|
||||
'__buffer_release__': operator(
|
||||
"bf_releasebuffer",
|
||||
args=[Argument('view', '*mut $crate::_detail::ffi::Py_buffer')],
|
||||
wrapper="py_class_binary_internal", res_type='void'),
|
||||
}
|
||||
|
||||
def main():
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
macro_rules! py_class_impl {
|
||||
// TT muncher macro. Results are accumulated in $info $slots $impls and $members.
|
||||
|
||||
|
||||
// Base case: we're done munching and can start producing code:
|
||||
{ {}
|
||||
$class:ident $py:ident
|
||||
|
@ -42,58 +41,10 @@ macro_rules! py_class_impl {
|
|||
$slots:tt { $( $imp:item )* } $members:tt $properties:tt
|
||||
} => {
|
||||
py_coerce_item! {
|
||||
#[$crate::cls::class]
|
||||
$($class_visibility)* struct $class { _unsafe_inner: $crate::PyObject }
|
||||
}
|
||||
|
||||
py_impl_to_py_object_for_python_object!($class);
|
||||
py_impl_from_py_object_for_python_object!($class);
|
||||
|
||||
impl $crate::PythonObject for $class {
|
||||
#[inline]
|
||||
fn as_object(&self) -> &$crate::PyObject {
|
||||
&self._unsafe_inner
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_object(self) -> $crate::PyObject {
|
||||
self._unsafe_inner
|
||||
}
|
||||
|
||||
/// Unchecked downcast from PyObject to Self.
|
||||
/// Undefined behavior if the input object does not have the expected type.
|
||||
#[inline]
|
||||
unsafe fn unchecked_downcast_from(obj: $crate::PyObject) -> Self {
|
||||
$class { _unsafe_inner: obj }
|
||||
}
|
||||
|
||||
/// Unchecked downcast from PyObject to Self.
|
||||
/// Undefined behavior if the input object does not have the expected type.
|
||||
#[inline]
|
||||
unsafe fn unchecked_downcast_borrow_from<'a>(obj: &'a $crate::PyObject) -> &'a Self {
|
||||
::std::mem::transmute(obj)
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::PythonObjectWithCheckedDowncast for $class {
|
||||
#[inline]
|
||||
fn downcast_from<'p>(py: $crate::Python<'p>, obj: $crate::PyObject) -> Result<$class, $crate::PythonObjectDowncastError<'p>> {
|
||||
if py.get_type::<$class>().is_instance(py, &obj) {
|
||||
Ok($class { _unsafe_inner: obj })
|
||||
} else {
|
||||
Err($crate::PythonObjectDowncastError(py))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn downcast_borrow_from<'a, 'p>(py: $crate::Python<'p>, obj: &'a $crate::PyObject) -> Result<&'a $class, $crate::PythonObjectDowncastError<'p>> {
|
||||
if py.get_type::<$class>().is_instance(py, obj) {
|
||||
unsafe { Ok(::std::mem::transmute(obj)) }
|
||||
} else {
|
||||
Err($crate::PythonObjectDowncastError(py))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
py_coerce_item! {
|
||||
impl $crate::py_class::BaseObject for $class {
|
||||
type InitType = ( $( $data_ty, )* );
|
||||
|
@ -177,13 +128,8 @@ macro_rules! py_class_impl {
|
|||
py_class_type_object_dynamic_init!($class, $py, TYPE_OBJECT, module_name, $slots);
|
||||
py_class_init_members!($class, $py, TYPE_OBJECT, $members);
|
||||
py_class_init_properties!($class, $py, TYPE_OBJECT, $properties);
|
||||
unsafe {
|
||||
if $crate::_detail::ffi::PyType_Ready(&mut TYPE_OBJECT) == 0 {
|
||||
Ok($crate::PyType::from_type_ptr($py, &mut TYPE_OBJECT))
|
||||
} else {
|
||||
Err($crate::PyErr::fetch($py))
|
||||
}
|
||||
}
|
||||
unsafe { <$class as $crate::class::methods::PyClassInit>
|
||||
::build_type($py, module_name, &mut TYPE_OBJECT) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -248,77 +194,6 @@ macro_rules! py_class_impl {
|
|||
}
|
||||
$members $properties
|
||||
}};
|
||||
{ { def __traverse__(&$slf:tt, $visit:ident) $body:block $($tail:tt)* }
|
||||
$class:ident $py:ident
|
||||
/* info: */ {
|
||||
$base_type: ty,
|
||||
$size: expr,
|
||||
$class_visibility: tt,
|
||||
/* gc: */ {
|
||||
/* traverse_proc: */ None,
|
||||
$traverse_data: tt
|
||||
},
|
||||
$datas: tt
|
||||
}
|
||||
$slots:tt
|
||||
{ $( $imp:item )* }
|
||||
$members:tt $properties:tt
|
||||
} => { py_class_impl! {
|
||||
{ $($tail)* }
|
||||
$class $py
|
||||
/* info: */ {
|
||||
$base_type,
|
||||
$size,
|
||||
$class_visibility,
|
||||
/* gc: */ {
|
||||
/* traverse_proc: */ $class::__traverse__,
|
||||
$traverse_data
|
||||
},
|
||||
$datas
|
||||
}
|
||||
$slots
|
||||
/* impl: */ {
|
||||
$($imp)*
|
||||
py_coerce_item!{
|
||||
impl $class {
|
||||
fn __traverse__(&$slf,
|
||||
$py: $crate::Python,
|
||||
$visit: $crate::py_class::gc::VisitProc)
|
||||
-> Result<(), $crate::py_class::gc::TraverseError>
|
||||
$body
|
||||
}
|
||||
}
|
||||
}
|
||||
$members $properties
|
||||
}};
|
||||
{ { def __clear__ (&$slf:ident) $body:block $($tail:tt)* }
|
||||
$class:ident $py:ident $info:tt
|
||||
/* slots: */ {
|
||||
/* type_slots */ [ $( $tp_slot_name:ident : $tp_slot_value:expr, )* ]
|
||||
$as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt
|
||||
}
|
||||
{ $( $imp:item )* }
|
||||
$members:tt $properties:tt
|
||||
} => { py_class_impl! {
|
||||
{ $($tail)* }
|
||||
$class $py $info
|
||||
/* slots: */ {
|
||||
/* type_slots */ [
|
||||
$( $tp_slot_name : $tp_slot_value, )*
|
||||
tp_clear: py_class_tp_clear!($class),
|
||||
]
|
||||
$as_async $as_number $as_sequence $as_mapping $as_buffer $setdelitem
|
||||
}
|
||||
/* impl: */ {
|
||||
$($imp)*
|
||||
py_coerce_item!{
|
||||
impl $class {
|
||||
fn __clear__(&$slf, $py: $crate::Python) $body
|
||||
}
|
||||
}
|
||||
}
|
||||
$members $properties
|
||||
}};
|
||||
{ { def __abs__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
|
||||
$class:ident $py:ident $info:tt
|
||||
/* slots: */ {
|
||||
|
@ -474,39 +349,6 @@ macro_rules! py_class_impl {
|
|||
$members $properties
|
||||
}};
|
||||
|
||||
{ { def __anext__ $($tail:tt)* } $( $stuff:tt )* } => {
|
||||
py_error! { "Invalid signature for operator __anext__" }
|
||||
};
|
||||
{ { def __await__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
|
||||
$class:ident $py:ident $info:tt
|
||||
/* slots: */ {
|
||||
$type_slots:tt
|
||||
/* as_async */ [ $( $am_slot_name:ident : $am_slot_value:expr, )* ]
|
||||
$as_number:tt $as_sequence:tt $as_mapping:tt $as_buffer:tt $setdelitem:tt
|
||||
}
|
||||
{ $( $imp:item )* }
|
||||
$members:tt $properties:tt
|
||||
} => { py_class_impl! {
|
||||
{ $($tail)* }
|
||||
$class $py $info
|
||||
/* slots: */ {
|
||||
$type_slots
|
||||
/* as_async */ [
|
||||
$( $am_slot_name : $am_slot_value, )*
|
||||
am_await: py_class_unary_slot!($class::__await__, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PyObjectCallbackConverter),
|
||||
]
|
||||
$as_number $as_sequence $as_mapping $as_buffer $setdelitem
|
||||
}
|
||||
/* impl: */ {
|
||||
$($imp)*
|
||||
py_class_impl_item! { $class, $py, __await__(&$slf,) $res_type; { $($body)* } [] }
|
||||
}
|
||||
$members $properties
|
||||
}};
|
||||
|
||||
{ { def __await__ $($tail:tt)* } $( $stuff:tt )* } => {
|
||||
py_error! { "Invalid signature for operator __await__" }
|
||||
};
|
||||
{ { def __bool__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
|
||||
$class:ident $py:ident $info:tt
|
||||
/* slots: */ {
|
||||
|
@ -537,66 +379,6 @@ macro_rules! py_class_impl {
|
|||
{ { def __bool__ $($tail:tt)* } $( $stuff:tt )* } => {
|
||||
py_error! { "Invalid signature for operator __bool__" }
|
||||
};
|
||||
{ { def __buffer_get__(&$slf:ident, $view:ident , $flags:ident ) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
|
||||
$class:ident $py:ident $info:tt
|
||||
/* slots: */ {
|
||||
$type_slots:tt $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt
|
||||
/* as_buffer */ [ $( $bf_slot_name:ident : $bf_slot_value:expr, )* ]
|
||||
$setdelitem:tt
|
||||
}
|
||||
{ $( $imp:item )* }
|
||||
$members:tt $properties:tt
|
||||
} => { py_class_impl! {
|
||||
{ $($tail)* }
|
||||
$class $py $info
|
||||
/* slots: */ {
|
||||
$type_slots $as_async $as_number $as_sequence $as_mapping
|
||||
/* as_buffer */ [
|
||||
$( $bf_slot_name : $bf_slot_value, )*
|
||||
bf_getbuffer: py_class_ternary_internal!($class::__buffer_get__, *mut $crate::_detail::ffi::Py_buffer, $crate::_detail::libc::c_int, $crate::_detail::libc::c_int, $crate::py_class::slots::SuccessConverter),
|
||||
]
|
||||
$setdelitem
|
||||
}
|
||||
/* impl: */ {
|
||||
$($imp)*
|
||||
py_class_impl_item! { $class, $py, __buffer_get__(&$slf,) $res_type; { $($body)* } [{ $view : *mut $crate::_detail::ffi::Py_buffer = {} } { $flags : $crate::_detail::libc::c_int = {} }] }
|
||||
}
|
||||
$members $properties
|
||||
}};
|
||||
|
||||
{ { def __buffer_get__ $($tail:tt)* } $( $stuff:tt )* } => {
|
||||
py_error! { "Invalid signature for operator __buffer_get__" }
|
||||
};
|
||||
{ { def __buffer_release__(&$slf:ident, $view:ident ) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
|
||||
$class:ident $py:ident $info:tt
|
||||
/* slots: */ {
|
||||
$type_slots:tt $as_async:tt $as_number:tt $as_sequence:tt $as_mapping:tt
|
||||
/* as_buffer */ [ $( $bf_slot_name:ident : $bf_slot_value:expr, )* ]
|
||||
$setdelitem:tt
|
||||
}
|
||||
{ $( $imp:item )* }
|
||||
$members:tt $properties:tt
|
||||
} => { py_class_impl! {
|
||||
{ $($tail)* }
|
||||
$class $py $info
|
||||
/* slots: */ {
|
||||
$type_slots $as_async $as_number $as_sequence $as_mapping
|
||||
/* as_buffer */ [
|
||||
$( $bf_slot_name : $bf_slot_value, )*
|
||||
bf_releasebuffer: py_class_binary_slot!($class::__buffer_release__, *mut $crate::_detail::ffi::Py_buffer, *mut $crate::_detail::ffi::PyObject, $crate::_detail::PythonObjectCallbackConverter::<$crate::void>(::std::marker::PhantomData)),
|
||||
]
|
||||
$setdelitem
|
||||
}
|
||||
/* impl: */ {
|
||||
$($imp)*
|
||||
py_class_impl_item! { $class, $py, __buffer_release__(&$slf,) $res_type; { $($body)* } [{ $view : *mut $crate::_detail::ffi::Py_buffer = {} }] }
|
||||
}
|
||||
$members $properties
|
||||
}};
|
||||
|
||||
{ { def __buffer_release__ $($tail:tt)* } $( $stuff:tt )* } => {
|
||||
py_error! { "Invalid signature for operator __buffer_release__" }
|
||||
};
|
||||
{ { def __call__ (&$slf:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)* }
|
||||
$class:ident $py:ident $info:tt
|
||||
/* slots: */ {
|
||||
|
|
|
@ -47,37 +47,13 @@ macro_rules! py_class_type_object_static_init {
|
|||
}) => (
|
||||
$crate::_detail::ffi::PyTypeObject {
|
||||
$( $slot_name : $slot_value, )*
|
||||
tp_dealloc: Some($crate::py_class::slots::tp_dealloc_callback::<$class_name>),
|
||||
tp_flags: py_class_type_object_flags!($gc),
|
||||
tp_traverse: py_class_tp_traverse!($class_name, $gc),
|
||||
//tp_traverse: py_class_tp_traverse!($class_name, $gc),
|
||||
..
|
||||
$crate::_detail::ffi::PyTypeObject_INIT
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_type_object_flags {
|
||||
(/* gc: */ {
|
||||
/* traverse_proc: */ None,
|
||||
/* traverse_data: */ [ /*name*/ ]
|
||||
}) => {
|
||||
$crate::py_class::slots::TPFLAGS_DEFAULT
|
||||
};
|
||||
(/* gc: */ {
|
||||
$traverse_proc: expr,
|
||||
$traverse_data: tt
|
||||
}) => {
|
||||
$crate::py_class::slots::TPFLAGS_DEFAULT
|
||||
| $crate::_detail::ffi::Py_TPFLAGS_HAVE_GC
|
||||
};
|
||||
}
|
||||
|
||||
pub const TPFLAGS_DEFAULT : ::libc::c_ulong = ffi::Py_TPFLAGS_DEFAULT;
|
||||
|
||||
use class::buffer::*;
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_type_object_dynamic_init {
|
||||
|
@ -93,57 +69,14 @@ macro_rules! py_class_type_object_dynamic_init {
|
|||
$setdelitem:tt
|
||||
}
|
||||
) => {
|
||||
unsafe {
|
||||
$type_object.tp_name = $crate::py_class::slots::build_tp_name($module_name, stringify!($class));
|
||||
$type_object.tp_basicsize = <$class as $crate::py_class::BaseObject>::size()
|
||||
as $crate::_detail::ffi::Py_ssize_t;
|
||||
}
|
||||
|
||||
// call slot macros outside of unsafe block
|
||||
*(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;
|
||||
*(unsafe { &mut $type_object.tp_as_buffer }) = unsafe { &mut BUFFER_PROCS };
|
||||
} else {
|
||||
*(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn build_tp_name(module_name: Option<&str>, type_name: &str) -> *mut c_char {
|
||||
let name = match module_name {
|
||||
Some(module_name) => CString::new(format!("{}.{}", module_name, type_name)),
|
||||
None => CString::new(type_name)
|
||||
};
|
||||
name.expect("Module name/type name must not contain NUL byte").into_raw()
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject)
|
||||
where T: super::BaseObject
|
||||
{
|
||||
let guard = ::function::AbortOnDrop("Cannot unwind out of tp_dealloc");
|
||||
let py = Python::assume_gil_acquired();
|
||||
let r = T::dealloc(py, obj);
|
||||
mem::forget(guard);
|
||||
r
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_wrap_newfunc {
|
||||
|
@ -203,22 +136,6 @@ macro_rules! py_class_as_number {
|
|||
}}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_as_async {
|
||||
([]) => (0 as *mut $crate::_detail::ffi::PyAsyncMethods);
|
||||
([$( $slot_name:ident : $slot_value:expr ,)+]) => {{
|
||||
static mut ASYNC_METHODS : $crate::_detail::ffi::PyAsyncMethods
|
||||
= $crate::_detail::ffi::PyAsyncMethods {
|
||||
$( $slot_name : $slot_value, )*
|
||||
..
|
||||
$crate::_detail::ffi::PyAsyncMethods_INIT
|
||||
};
|
||||
unsafe { &mut ASYNC_METHODS }
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_class_as_mapping {
|
||||
|
|
Loading…
Reference in New Issue