stopping point

This commit is contained in:
Nikolay Kim 2017-05-15 22:24:06 -07:00
parent f4feade487
commit 5427a33c75
19 changed files with 1081 additions and 671 deletions

View File

@ -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();

142
pyo3cls/src/py_class.rs Normal file
View File

@ -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
}
}
}
}

View File

@ -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 &quote::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
}
}

234
pyo3cls/src/py_method.rs Normal file
View File

@ -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(&params.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: "",
}
}}
}

223
pyo3cls/src/py_proto.rs Normal file
View File

@ -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
}
}

View File

@ -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,
fname: Option<&str>, params: &[ParamDescription],
args: &PyTuple, kwargs: Option<&PyDict>,
output: &mut[Option<PyObject>]
) -> PyResult<()>
pub fn parse_args(py: Python,
fname: Option<&str>, params: &[ParamDescription],
args: &PyTuple, kwargs: Option<&PyDict>,
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() {

View File

@ -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!(),

35
src/class/basic.rs Normal file
View File

@ -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>;
}

96
src/class/context.rs Normal file
View File

@ -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)
}
*/

118
src/class/gc.rs Normal file
View File

@ -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
}

147
src/class/methods.rs Normal file
View File

@ -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
}

View File

@ -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] = &[];

View File

@ -25,7 +25,7 @@ pub type _PyCFunctionFast =
pub type PyCFunctionWithKeywords =
unsafe extern "C" fn
(slf: *mut PyObject, args: *mut PyObject,
(slf: *mut PyObject, args: *mut PyObject,
kwds: *mut PyObject) -> *mut PyObject;
pub type PyNoArgsFunction =
unsafe extern "C" fn(slf: *mut PyObject)
@ -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 }
}

View File

@ -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

View File

@ -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(())
}
}
*/

View File

@ -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};

View File

@ -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():

View File

@ -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: */ {

View File

@ -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 {