pyo3/pyo3cls/src/py_class.rs

360 lines
13 KiB
Rust
Raw Normal View History

2017-05-16 05:24:06 +00:00
// Copyright (c) 2017-present PyO3 Project and Contributors
2017-06-09 19:30:13 +00:00
use std;
use std::collections::HashMap;
2017-05-16 05:24:06 +00:00
use syn;
use quote::Tokens;
2017-05-16 05:24:06 +00:00
2017-06-13 00:15:26 +00:00
use utils;
2017-05-16 05:24:06 +00:00
2017-06-09 19:30:13 +00:00
pub fn build_py_class(ast: &mut syn::DeriveInput, attr: String) -> Tokens {
let params = parse_attribute(attr);
2017-06-18 15:00:27 +00:00
let doc = utils::get_doc(&ast.attrs, true);
2017-06-09 19:30:13 +00:00
2017-05-25 03:31:51 +00:00
let base = syn::Ident::from("_pyo3::PyObject");
2017-05-28 05:45:48 +00:00
let mut token: Option<syn::Ident> = None;
2017-05-17 06:43:39 +00:00
match ast.body {
2017-05-28 05:45:48 +00:00
syn::Body::Struct(syn::VariantData::Struct(ref mut fields)) => {
2017-06-01 16:45:00 +00:00
for field in fields.iter() {
if is_python_token(field) {
token = field.ident.clone();
break
2017-05-28 05:45:48 +00:00
}
}
},
2017-06-18 15:41:20 +00:00
_ => panic!("#[class] can only be used with normal structs"),
2017-05-17 06:43:39 +00:00
}
2017-05-16 05:24:06 +00:00
let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_CLS_{}", ast.ident));
2017-06-13 00:15:26 +00:00
let tokens = impl_class(&ast.ident, &base, token, doc, params);
2017-05-16 05:24:06 +00:00
quote! {
#[feature(specialization)]
#[allow(non_upper_case_globals, unused_attributes,
unused_qualifications, unused_variables, non_camel_case_types)]
2017-05-16 05:24:06 +00:00
const #dummy_const: () = {
2017-06-01 22:06:48 +00:00
use std;
2017-05-18 18:15:06 +00:00
extern crate pyo3 as _pyo3;
2017-05-16 05:24:06 +00:00
#tokens
};
}
}
2017-06-09 19:30:13 +00:00
fn impl_class(cls: &syn::Ident, base: &syn::Ident,
2017-06-13 00:15:26 +00:00
token: Option<syn::Ident>, doc: syn::Lit,
params: HashMap<&'static str, syn::Ident>) -> Tokens {
2017-06-09 19:30:13 +00:00
let cls_name = match params.get("name") {
Some(name) => quote! { #name }.as_str().to_string(),
None => quote! { #cls }.as_str().to_string()
};
2017-05-18 23:57:39 +00:00
2017-05-28 05:45:48 +00:00
let extra = if let Some(token) = token {
Some(quote! {
2017-06-01 22:06:48 +00:00
impl _pyo3::PyObjectWithToken for #cls {
2017-06-20 18:59:07 +00:00
#[inline]
2017-06-15 18:13:58 +00:00
fn token<'p>(&'p self) -> _pyo3::Python<'p> {
2017-06-22 20:35:21 +00:00
self.#token.py()
2017-05-28 05:45:48 +00:00
}
}
impl _pyo3::ToPyObject for #cls
{
#[inline]
2017-06-22 08:04:37 +00:00
fn to_object<'p>(&self, py: _pyo3::Python<'p>) -> _pyo3::PyObjectPtr {
unsafe { _pyo3::PyObjectPtr::from_borrowed_ptr(py, self.as_ptr()) }
}
#[inline]
fn with_borrowed_ptr<F, R>(&self, _py: _pyo3::Python, f: F) -> R
where F: FnOnce(*mut ffi::PyObject) -> R
{
f(self.as_ptr())
}
}
2017-06-20 06:57:34 +00:00
impl<'a> _pyo3::IntoPyObject for &'a #cls
{
#[inline]
2017-06-22 08:04:37 +00:00
fn into_object<'p>(self, py: _pyo3::Python) -> _pyo3::PyObjectPtr {
unsafe { _pyo3::PyObjectPtr::from_borrowed_ptr(py, self.as_ptr()) }
2017-06-20 06:57:34 +00:00
}
}
impl std::convert::AsRef<PyObject> for #cls {
fn as_ref(&self) -> &_pyo3::PyObject {
unsafe{std::mem::transmute(self.as_ptr())}
}
}
impl _pyo3::ToPyPointer for #cls {
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
unsafe {
{self as *const _ as *mut u8}.offset(-offset) as *mut ffi::PyObject
}
}
}
2017-06-22 08:04:37 +00:00
impl std::fmt::Debug for #cls {
2017-05-28 05:45:48 +00:00
fn fmt(&self, f : &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
2017-06-22 08:04:37 +00:00
use pyo3::ObjectProtocol;
let s = try!(self.repr().map_err(|_| std::fmt::Error));
2017-06-21 22:11:32 +00:00
f.write_str(&s.to_string_lossy())
2017-05-28 05:45:48 +00:00
}
}
2017-06-22 08:04:37 +00:00
impl std::fmt::Display for #cls {
2017-05-28 05:45:48 +00:00
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
2017-06-22 08:04:37 +00:00
use pyo3::ObjectProtocol;
let s = try!(self.str().map_err(|_| std::fmt::Error));
2017-06-21 22:11:32 +00:00
f.write_str(&s.to_string_lossy())
2017-05-28 05:45:48 +00:00
}
2017-05-28 15:57:34 +00:00
}
2017-05-28 05:45:48 +00:00
})
} else {
None
};
2017-06-09 21:27:37 +00:00
let extra = {
if let Some(freelist) = params.get("freelist") {
Some(quote! {
impl _pyo3::freelist::PyObjectWithFreeList for #cls {
#[inline]
fn get_free_list() -> &'static mut _pyo3::freelist::FreeList<*mut ffi::PyObject> {
static mut FREELIST: *mut _pyo3::freelist::FreeList<*mut ffi::PyObject> = 0 as *mut _;
unsafe {
if FREELIST.is_null() {
FREELIST = Box::into_raw(Box::new(
_pyo3::freelist::FreeList::with_capacity(#freelist)));
<#cls as _pyo3::typeob::PyTypeObject>::init_type(
_pyo3::Python::assume_gil_acquired());
}
std::mem::transmute(FREELIST)
}
}
}
#extra
})
} else {
extra
}
};
2017-05-17 06:43:39 +00:00
quote! {
2017-05-25 05:43:07 +00:00
impl _pyo3::typeob::PyTypeInfo for #cls {
type Type = #cls;
2017-05-22 05:22:45 +00:00
#[inline]
fn size() -> usize {
Self::offset() as usize + std::mem::size_of::<#cls>()
2017-05-22 05:22:45 +00:00
}
2017-05-17 06:43:39 +00:00
2017-05-22 05:22:45 +00:00
#[inline]
fn offset() -> isize {
2017-05-22 05:22:45 +00:00
let align = std::mem::align_of::<#cls>();
2017-05-25 05:43:07 +00:00
let bs = <#base as _pyo3::typeob::PyTypeInfo>::size();
2017-05-17 06:43:39 +00:00
2017-05-22 05:22:45 +00:00
// round base_size up to next multiple of align
((bs + align - 1) / align * align) as isize
2017-05-17 06:43:39 +00:00
}
2017-05-18 23:57:39 +00:00
fn type_name() -> &'static str { #cls_name }
2017-06-13 00:15:26 +00:00
fn type_description() -> &'static str {
#doc
}
2017-05-17 06:43:39 +00:00
#[inline]
2017-05-19 04:35:08 +00:00
fn type_object() -> &'static mut _pyo3::ffi::PyTypeObject {
2017-05-18 18:15:06 +00:00
static mut TYPE_OBJECT: _pyo3::ffi::PyTypeObject = _pyo3::ffi::PyTypeObject_INIT;
2017-05-19 04:35:08 +00:00
unsafe { &mut TYPE_OBJECT }
2017-05-17 06:43:39 +00:00
}
2017-06-20 18:59:07 +00:00
#[inline]
fn is_instance(ptr: *mut ffi::PyObject) -> bool {
unsafe {ffi::PyObject_TypeCheck(
ptr, <#cls as _pyo3::typeob::PyTypeInfo>::type_object()) != 0}
}
2017-05-17 06:43:39 +00:00
}
2017-05-25 05:43:07 +00:00
2017-06-08 21:21:48 +00:00
impl _pyo3::typeob::PyTypeObject for #cls {
#[inline(always)]
fn init_type(py: Python) {
static START: std::sync::Once = std::sync::ONCE_INIT;
START.call_once(|| {
let mut ty = <#cls as _pyo3::typeob::PyTypeInfo>::type_object();
if (ty.tp_flags & _pyo3::ffi::Py_TPFLAGS_READY) == 0 {
// automatically initialize the class on-demand
_pyo3::typeob::initialize_type::<#cls>(
2017-06-13 00:15:26 +00:00
py, None, <#cls as _pyo3::typeob::PyTypeInfo>::type_name(),
<#cls as _pyo3::typeob::PyTypeInfo>::type_description(), ty).expect(
format!("An error occurred while initializing class {}",
<#cls as _pyo3::typeob::PyTypeInfo>::type_name())
.as_ref());
2017-06-08 21:21:48 +00:00
}
});
}
}
2017-06-15 18:13:58 +00:00
impl _pyo3::PyDowncastFrom for #cls
{
2017-06-22 08:04:37 +00:00
fn downcast_from(ob: &_pyo3::PyObject) -> Result<&#cls, _pyo3::PyDowncastError>
{
unsafe {
let checked = ffi::PyObject_TypeCheck(
2017-06-03 01:58:16 +00:00
ob.as_ptr(), <#cls as _pyo3::typeob::PyTypeInfo>::type_object()) != 0;
if checked {
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
2017-06-03 01:58:16 +00:00
let ptr = (ob.as_ptr() as *mut u8).offset(offset) as *mut #cls;
Ok(ptr.as_ref().unwrap())
} else {
2017-06-22 08:04:37 +00:00
Err(_pyo3::PyDowncastError(ob.token(), None))
}
}
}
2017-06-21 06:26:28 +00:00
2017-06-22 08:04:37 +00:00
unsafe fn unchecked_downcast_from(ob: &_pyo3::PyObject) -> &Self
{
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
let ptr = (ob.as_ptr() as *mut u8).offset(offset) as *mut #cls;
2017-06-22 08:04:37 +00:00
std::mem::transmute(ptr)
}
unsafe fn unchecked_mut_downcast_from(ob: &_pyo3::PyObject) -> &mut Self
2017-06-21 06:26:28 +00:00
{
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
let ptr = (ob.as_ptr() as *mut u8).offset(offset) as *mut #cls;
2017-06-21 06:26:28 +00:00
std::mem::transmute(ptr)
}
}
2017-06-15 18:13:58 +00:00
impl _pyo3::PyMutDowncastFrom for #cls
2017-06-06 03:25:00 +00:00
{
2017-06-22 08:04:37 +00:00
fn downcast_mut_from(ob: &mut _pyo3::PyObject)
-> Result<&mut #cls, _pyo3::PyDowncastError>
2017-06-06 03:25:00 +00:00
{
unsafe {
let checked = ffi::PyObject_TypeCheck(
ob.as_ptr(), <#cls as _pyo3::typeob::PyTypeInfo>::type_object()) != 0;
if checked {
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
let ptr = (ob.as_ptr() as *mut u8).offset(offset) as *mut #cls;
Ok(ptr.as_mut().unwrap())
} else {
2017-06-22 08:04:37 +00:00
Err(_pyo3::PyDowncastError(ob.token(), None))
2017-06-06 03:25:00 +00:00
}
}
}
}
2017-05-30 05:59:03 +00:00
2017-05-28 05:45:48 +00:00
#extra
2017-05-16 05:24:06 +00:00
}
}
2017-06-01 16:45:00 +00:00
fn is_python_token(field: &syn::Field) -> bool {
match field.ty {
syn::Ty::Path(_, ref path) => {
if let Some(segment) = path.segments.last() {
2017-06-01 22:06:48 +00:00
return segment.ident.as_ref() == "PyToken"
2017-06-01 16:45:00 +00:00
}
}
_ => (),
}
return false
}
2017-06-09 19:30:13 +00:00
fn parse_attribute(attr: String) -> HashMap<&'static str, syn::Ident> {
let mut params = HashMap::new();
if let Ok(tts) = syn::parse_token_trees(&attr) {
2017-06-09 19:35:29 +00:00
let mut elem = Vec::new();
2017-06-09 19:30:13 +00:00
let mut elems = Vec::new();
for tt in tts.iter() {
match tt {
&syn::TokenTree::Token(_) => {
println!("Wrong format: {:?}", attr.to_string());
}
&syn::TokenTree::Delimited(ref delimited) => {
for tt in delimited.tts.iter() {
match tt {
&syn::TokenTree::Token(syn::Token::Comma) => {
let el = std::mem::replace(&mut elem, Vec::new());
elems.push(el);
},
_ => elem.push(tt.clone())
}
}
}
}
}
2017-06-09 19:35:29 +00:00
if !elem.is_empty() {
elems.push(elem);
}
2017-06-09 19:30:13 +00:00
for elem in elems {
if elem.len() < 3 {
println!("Wrong format: {:?}", elem);
continue
}
let key = match elem[0] {
syn::TokenTree::Token(syn::Token::Ident(ref ident)) => {
ident.as_ref().to_owned().to_lowercase()
},
_ => {
println!("Wrong format: {:?}", attr.to_string());
continue
}
};
match elem[1] {
syn::TokenTree::Token(syn::Token::Eq) => (),
_ => {
println!("Wrong format: {:?}", attr.to_string());
continue
}
}
match key.as_ref() {
"freelist" => {
if elem.len() != 3 {
println!("Wrong 'freelist' format: {:?}", elem);
} else {
match elem[2] {
syn::TokenTree::Token(
syn::Token::Literal(
syn::Lit::Int(val, _))) => {
params.insert("freelist", syn::Ident::from(val.to_string()));
}
_ => println!("Wrong 'freelist' format: {:?}", elem)
}
}
},
"name" => {
if elem.len() != 3 {
println!("Wrong 'name' format: {:?}", elem);
} else {
match elem[2] {
syn::TokenTree::Token(syn::Token::Ident(ref ident)) => {
params.insert("name", ident.clone());
},
_ => println!("Wrong 'name' format: {:?}", elem)
}
}
},
"base" => {
}
_ => {
println!("Unsupported parameter: {:?}", key);
}
}
}
}
params
}