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)]
|
#![feature(proc_macro)]
|
||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
@ -9,12 +12,13 @@ use proc_macro::TokenStream;
|
||||||
|
|
||||||
use quote::{Tokens, ToTokens};
|
use quote::{Tokens, ToTokens};
|
||||||
|
|
||||||
mod py_impl;
|
mod py_class;
|
||||||
use py_impl::build_py_impl;
|
mod py_proto;
|
||||||
|
mod py_method;
|
||||||
|
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[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
|
// Construct a string representation of the type definition
|
||||||
let source = input.to_string();
|
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();
|
let mut ast = syn::parse_item(&source).unwrap();
|
||||||
|
|
||||||
// Build the output
|
// 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
|
// Return the generated impl as a TokenStream
|
||||||
let mut tokens = Tokens::new();
|
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
|
/// * kwargs: Keyword arguments
|
||||||
/// * output: Output array that receives the arguments.
|
/// * output: Output array that receives the arguments.
|
||||||
/// Must have same length as `params` and must be initialized to `None`.
|
/// Must have same length as `params` and must be initialized to `None`.
|
||||||
pub fn parse_args(
|
pub fn parse_args(py: Python,
|
||||||
py: Python,
|
fname: Option<&str>, params: &[ParamDescription],
|
||||||
fname: Option<&str>, params: &[ParamDescription],
|
args: &PyTuple, kwargs: Option<&PyDict>,
|
||||||
args: &PyTuple, kwargs: Option<&PyDict>,
|
output: &mut[Option<PyObject>]) -> PyResult<()>
|
||||||
output: &mut[Option<PyObject>]
|
|
||||||
) -> PyResult<()>
|
|
||||||
{
|
{
|
||||||
assert!(params.len() == output.len());
|
assert!(params.len() == output.len());
|
||||||
|
|
||||||
let nargs = args.len(py);
|
let nargs = args.len(py);
|
||||||
let nkeywords = kwargs.map_or(0, |d| d.len(py));
|
let nkeywords = kwargs.map_or(0, |d| d.len(py));
|
||||||
if nargs + nkeywords > params.len() {
|
if nargs + nkeywords > params.len() {
|
||||||
|
|
|
@ -19,26 +19,38 @@ use class::NO_METHODS;
|
||||||
/// Awaitable interface
|
/// Awaitable interface
|
||||||
pub trait PyAsyncProtocol {
|
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 {
|
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())
|
Ok(py.None())
|
||||||
}
|
}
|
||||||
|
|
||||||
default fn am_aiter(&self, py: Python) -> PyResult<PyObject> {
|
default fn __aiter__(&self, py: Python) -> PyResult<PyObject> {
|
||||||
Ok(py.None())
|
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())
|
Ok(py.None())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,19 +82,19 @@ impl ffi::PyAsyncMethods {
|
||||||
|
|
||||||
for name in methods {
|
for name in methods {
|
||||||
match name {
|
match name {
|
||||||
&"am_await" => {
|
&"__await__" => {
|
||||||
meth.am_await = py_unary_slot!(
|
meth.am_await = py_unary_slot!(
|
||||||
PyAsyncProtocol, T::am_await,
|
PyAsyncProtocol, T::__await__,
|
||||||
*mut ffi::PyObject, PyObjectCallbackConverter);
|
*mut ffi::PyObject, PyObjectCallbackConverter);
|
||||||
},
|
},
|
||||||
&"am_aiter" => {
|
&"__aiter__" => {
|
||||||
meth.am_aiter = py_unary_slot!(
|
meth.am_aiter = py_unary_slot!(
|
||||||
PyAsyncProtocol, T::am_aiter,
|
PyAsyncProtocol, T::__aiter__,
|
||||||
*mut ffi::PyObject, PyObjectCallbackConverter);
|
*mut ffi::PyObject, PyObjectCallbackConverter);
|
||||||
},
|
},
|
||||||
&"am_anext" => {
|
&"__anext__" => {
|
||||||
meth.am_anext = py_unary_slot!(
|
meth.am_anext = py_unary_slot!(
|
||||||
PyAsyncProtocol, T::am_anext,
|
PyAsyncProtocol, T::__anext__,
|
||||||
*mut ffi::PyObject, PyObjectCallbackConverter);
|
*mut ffi::PyObject, PyObjectCallbackConverter);
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => 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 async;
|
||||||
pub mod buffer;
|
pub mod buffer;
|
||||||
|
pub mod context;
|
||||||
|
pub mod methods;
|
||||||
|
pub mod gc;
|
||||||
|
|
||||||
pub use self::async::*;
|
pub use self::async::*;
|
||||||
pub use self::buffer::*;
|
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_METHODS: &'static [&'static str] = &[];
|
||||||
|
pub static NO_PY_METHODS: &'static [PyMethodDef] = &[];
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub type _PyCFunctionFast =
|
||||||
|
|
||||||
pub type PyCFunctionWithKeywords =
|
pub type PyCFunctionWithKeywords =
|
||||||
unsafe extern "C" fn
|
unsafe extern "C" fn
|
||||||
(slf: *mut PyObject, args: *mut PyObject,
|
(slf: *mut PyObject, args: *mut PyObject,
|
||||||
kwds: *mut PyObject) -> *mut PyObject;
|
kwds: *mut PyObject) -> *mut PyObject;
|
||||||
pub type PyNoArgsFunction =
|
pub type PyNoArgsFunction =
|
||||||
unsafe extern "C" fn(slf: *mut PyObject)
|
unsafe extern "C" fn(slf: *mut PyObject)
|
||||||
|
@ -48,6 +48,13 @@ pub struct PyMethodDef {
|
||||||
pub ml_doc: *const c_char,
|
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 {
|
impl Clone for PyMethodDef {
|
||||||
#[inline] fn clone(&self) -> PyMethodDef { *self }
|
#[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
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
// DEALINGS IN THE SOFTWARE.
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
#![cfg_attr(feature="nightly", feature(
|
//#![cfg_attr(feature="nightly", feature(
|
||||||
const_fn, // for GILProtected::new (#24111)
|
// const_fn, // for GILProtected::new (#24111)
|
||||||
shared, // for std::ptr::Shared (#27730)
|
// shared, // for std::ptr::Shared (#27730)
|
||||||
specialization, // for impl FromPyObject<'source> for Vec<...> (#31844)
|
// 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
|
#![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 slots;
|
||||||
#[doc(hidden)] pub mod members;
|
#[doc(hidden)] pub mod members;
|
||||||
#[doc(hidden)] pub mod properties;
|
#[doc(hidden)] pub mod properties;
|
||||||
pub mod gc;
|
|
||||||
|
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::{mem, ptr, cell};
|
use std::{mem, ptr, cell};
|
||||||
|
|
|
@ -789,25 +789,6 @@ special_names = {
|
||||||
# With statement context managers
|
# With statement context managers
|
||||||
'__enter__': normal_method(),
|
'__enter__': normal_method(),
|
||||||
'__exit__': 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():
|
def main():
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
macro_rules! py_class_impl {
|
macro_rules! py_class_impl {
|
||||||
// TT muncher macro. Results are accumulated in $info $slots $impls and $members.
|
// TT muncher macro. Results are accumulated in $info $slots $impls and $members.
|
||||||
|
|
||||||
|
|
||||||
// Base case: we're done munching and can start producing code:
|
// Base case: we're done munching and can start producing code:
|
||||||
{ {}
|
{ {}
|
||||||
$class:ident $py:ident
|
$class:ident $py:ident
|
||||||
|
@ -42,58 +41,10 @@ macro_rules! py_class_impl {
|
||||||
$slots:tt { $( $imp:item )* } $members:tt $properties:tt
|
$slots:tt { $( $imp:item )* } $members:tt $properties:tt
|
||||||
} => {
|
} => {
|
||||||
py_coerce_item! {
|
py_coerce_item! {
|
||||||
|
#[$crate::cls::class]
|
||||||
$($class_visibility)* struct $class { _unsafe_inner: $crate::PyObject }
|
$($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! {
|
py_coerce_item! {
|
||||||
impl $crate::py_class::BaseObject for $class {
|
impl $crate::py_class::BaseObject for $class {
|
||||||
type InitType = ( $( $data_ty, )* );
|
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_type_object_dynamic_init!($class, $py, TYPE_OBJECT, module_name, $slots);
|
||||||
py_class_init_members!($class, $py, TYPE_OBJECT, $members);
|
py_class_init_members!($class, $py, TYPE_OBJECT, $members);
|
||||||
py_class_init_properties!($class, $py, TYPE_OBJECT, $properties);
|
py_class_init_properties!($class, $py, TYPE_OBJECT, $properties);
|
||||||
unsafe {
|
unsafe { <$class as $crate::class::methods::PyClassInit>
|
||||||
if $crate::_detail::ffi::PyType_Ready(&mut TYPE_OBJECT) == 0 {
|
::build_type($py, module_name, &mut TYPE_OBJECT) }
|
||||||
Ok($crate::PyType::from_type_ptr($py, &mut TYPE_OBJECT))
|
|
||||||
} else {
|
|
||||||
Err($crate::PyErr::fetch($py))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,77 +194,6 @@ macro_rules! py_class_impl {
|
||||||
}
|
}
|
||||||
$members $properties
|
$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)* }
|
{ { def __abs__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
|
||||||
$class:ident $py:ident $info:tt
|
$class:ident $py:ident $info:tt
|
||||||
/* slots: */ {
|
/* slots: */ {
|
||||||
|
@ -474,39 +349,6 @@ macro_rules! py_class_impl {
|
||||||
$members $properties
|
$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)* }
|
{ { def __bool__(&$slf:ident) -> $res_type:ty { $($body:tt)* } $($tail:tt)* }
|
||||||
$class:ident $py:ident $info:tt
|
$class:ident $py:ident $info:tt
|
||||||
/* slots: */ {
|
/* slots: */ {
|
||||||
|
@ -537,66 +379,6 @@ macro_rules! py_class_impl {
|
||||||
{ { def __bool__ $($tail:tt)* } $( $stuff:tt )* } => {
|
{ { def __bool__ $($tail:tt)* } $( $stuff:tt )* } => {
|
||||||
py_error! { "Invalid signature for operator __bool__" }
|
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)* }
|
{ { def __call__ (&$slf:ident) -> $res_type:ty { $( $body:tt )* } $($tail:tt)* }
|
||||||
$class:ident $py:ident $info:tt
|
$class:ident $py:ident $info:tt
|
||||||
/* slots: */ {
|
/* slots: */ {
|
||||||
|
|
|
@ -47,37 +47,13 @@ macro_rules! py_class_type_object_static_init {
|
||||||
}) => (
|
}) => (
|
||||||
$crate::_detail::ffi::PyTypeObject {
|
$crate::_detail::ffi::PyTypeObject {
|
||||||
$( $slot_name : $slot_value, )*
|
$( $slot_name : $slot_value, )*
|
||||||
tp_dealloc: Some($crate::py_class::slots::tp_dealloc_callback::<$class_name>),
|
//tp_traverse: py_class_tp_traverse!($class_name, $gc),
|
||||||
tp_flags: py_class_type_object_flags!($gc),
|
|
||||||
tp_traverse: py_class_tp_traverse!($class_name, $gc),
|
|
||||||
..
|
..
|
||||||
$crate::_detail::ffi::PyTypeObject_INIT
|
$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]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! py_class_type_object_dynamic_init {
|
macro_rules! py_class_type_object_dynamic_init {
|
||||||
|
@ -93,57 +69,14 @@ macro_rules! py_class_type_object_dynamic_init {
|
||||||
$setdelitem:tt
|
$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
|
// 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_sequence }) = py_class_as_sequence!($as_sequence);
|
||||||
*(unsafe { &mut $type_object.tp_as_number }) = py_class_as_number!($as_number);
|
*(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);
|
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]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! py_class_wrap_newfunc {
|
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]
|
#[macro_export]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
macro_rules! py_class_as_mapping {
|
macro_rules! py_class_as_mapping {
|
||||||
|
|
Loading…
Reference in New Issue