pyo3/pyo3cls/src/py_class.rs

281 lines
9.7 KiB
Rust
Raw Normal View History

2017-05-16 05:24:06 +00:00
// Copyright (c) 2017-present PyO3 Project and Contributors
use syn;
use quote::{Tokens, ToTokens};
pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens {
2017-05-18 18:15:06 +00:00
let base = syn::Ident::from("_pyo3::PyObject");
2017-05-16 05:24:06 +00:00
let mut tokens = Tokens::new();
2017-05-17 06:43:39 +00:00
match ast.body {
syn::Body::Struct(syn::VariantData::Struct(ref mut data)) => {
impl_storage(&ast.ident, &base, data).to_tokens(&mut tokens);
let tt = quote! {
struct Test {
_unsafe_inner: PyObject
}
};
let t = syn::parse_item(tt.as_str()).unwrap();
match t.node {
syn::ItemKind::Struct(syn::VariantData::Struct(fields), _) => {
data.clear();
data.extend(fields);
}
_ => panic!("something is worng"),
}
},
_ =>
panic!("#[class] can only be used with notmal structs"),
}
2017-05-16 05:24:06 +00:00
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);
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: () = {
2017-05-18 18:15:06 +00:00
extern crate pyo3 as _pyo3;
2017-05-16 05:24:06 +00:00
use std;
#tokens
};
}
}
2017-05-17 06:43:39 +00:00
fn impl_storage(cls: &syn::Ident, base: &syn::Ident, fields: &Vec<syn::Field>) -> Tokens {
let names: Vec<syn::Ident> = fields.iter()
.map(|f| f.ident.as_ref().unwrap().clone()).collect();
let values: Vec<syn::Ident> = fields.iter()
.map(|f| f.ident.as_ref().unwrap().clone()).collect();
//let types: Vec<syn::Ty> = fields.iter().map(|f| f.ty.clone()).collect();
2017-05-18 18:15:06 +00:00
let storage = syn::Ident::from(format!("{}_Storage", cls).as_str());
2017-05-17 06:43:39 +00:00
let mut accessors = Tokens::new();
for field in fields.iter() {
let name = &field.ident.as_ref().unwrap();
let name_mut = syn::Ident::from(format!("{}_mut", name.as_ref()));
let ty = &field.ty;
let accessor = quote!{
impl #cls {
2017-05-18 18:15:06 +00:00
fn #name<'a>(&'a self, py: _pyo3::Python<'a>) -> &'a #ty {
2017-05-17 06:43:39 +00:00
unsafe {
let ptr = (self._unsafe_inner.as_ptr() as *const u8)
2017-05-18 18:15:06 +00:00
.offset(base_offset() as isize) as *const #storage;
2017-05-17 06:43:39 +00:00
&(*ptr).#name
}
}
2017-05-18 18:15:06 +00:00
fn #name_mut<'a>(&'a self, py: _pyo3::Python<'a>) -> &'a mut #ty {
2017-05-17 06:43:39 +00:00
unsafe {
let ptr = (self._unsafe_inner.as_ptr() as *const u8)
2017-05-18 18:15:06 +00:00
.offset(base_offset() as isize) as *mut #storage;
2017-05-17 06:43:39 +00:00
&mut (*ptr).#name
}
}
}
};
accessor.to_tokens(&mut accessors);
}
quote! {
2017-05-18 18:15:06 +00:00
struct #storage {
2017-05-17 06:43:39 +00:00
#(#fields),*
}
impl #cls {
2017-05-18 18:15:06 +00:00
fn create_instance(py: _pyo3::Python, #(#fields),*) -> _pyo3::PyResult<#cls> {
2017-05-17 06:43:39 +00:00
let obj = try!(unsafe {
2017-05-18 18:15:06 +00:00
<#cls as _pyo3::class::BaseObject>::alloc(
2017-05-17 06:43:39 +00:00
py, &py.get_type::<#cls>(),
2017-05-18 18:15:06 +00:00
#storage { #(#names: #values),*})});
2017-05-17 06:43:39 +00:00
return Ok(#cls { _unsafe_inner: obj });
}
}
#accessors
2017-05-18 18:15:06 +00:00
impl _pyo3::PythonObjectWithTypeObject for #cls {
2017-05-17 06:43:39 +00:00
#[inline]
2017-05-18 18:15:06 +00:00
fn type_object(py: _pyo3::Python) -> _pyo3::PyType {
unsafe { <#cls as _pyo3::class::PyTypeObject>::initialized(py, None) }
2017-05-17 06:43:39 +00:00
}
}
2017-05-18 18:15:06 +00:00
impl _pyo3::class::PyTypeObject for #cls {
2017-05-17 06:43:39 +00:00
2017-05-18 18:15:06 +00:00
fn add_to_module(py: _pyo3::Python, module: &_pyo3::PyModule) -> _pyo3::PyResult<()> {
2017-05-17 06:43:39 +00:00
let ty = unsafe { #cls::initialized(py, module.name(py).ok()) };
module.add(py, stringify!(#cls), ty)
}
#[inline]
2017-05-18 18:15:06 +00:00
unsafe fn type_obj() -> &'static mut _pyo3::ffi::PyTypeObject {
static mut TYPE_OBJECT: _pyo3::ffi::PyTypeObject = _pyo3::ffi::PyTypeObject_INIT;
2017-05-17 06:43:39 +00:00
&mut TYPE_OBJECT
}
2017-05-18 18:15:06 +00:00
unsafe fn initialized(py: _pyo3::Python, module_name: Option<&str>) -> _pyo3::PyType {
2017-05-17 06:43:39 +00:00
let mut ty = #cls::type_obj();
2017-05-18 18:15:06 +00:00
if (ty.tp_flags & _pyo3::ffi::Py_TPFLAGS_READY) != 0 {
_pyo3::PyType::from_type_ptr(py, ty)
2017-05-17 06:43:39 +00:00
} else {
// automatically initialize the class on-demand
2017-05-18 18:15:06 +00:00
_pyo3::class::typeob::initialize_type::<#cls>(
2017-05-17 06:43:39 +00:00
py, module_name, ty).expect(
concat!("An error occurred while initializing class ",
stringify!(#cls)));
2017-05-18 18:15:06 +00:00
_pyo3::PyType::from_type_ptr(py, ty)
2017-05-17 06:43:39 +00:00
}
}
}
#[inline]
fn base_offset() -> usize {
2017-05-18 18:15:06 +00:00
let align = std::mem::align_of::<#storage>();
let bs = <#base as _pyo3::class::BaseObject>::size();
2017-05-17 06:43:39 +00:00
// round base_size up to next multiple of align
(bs + align - 1) / align * align
}
2017-05-18 18:15:06 +00:00
impl _pyo3::class::BaseObject for #cls {
type Type = #storage;
2017-05-17 06:43:39 +00:00
#[inline]
fn size() -> usize {
base_offset() + std::mem::size_of::<Self::Type>()
}
2017-05-18 18:15:06 +00:00
unsafe fn alloc(py: _pyo3::Python, ty: &_pyo3::PyType,
value: Self::Type) -> _pyo3::PyResult<_pyo3::PyObject>
2017-05-17 06:43:39 +00:00
{
2017-05-18 18:15:06 +00:00
let obj = try!(<#base as _pyo3::class::BaseObject>::alloc(py, ty, ()));
2017-05-17 06:43:39 +00:00
let ptr = (obj.as_ptr() as *mut u8)
.offset(base_offset() as isize) as *mut Self::Type;
std::ptr::write(ptr, value);
Ok(obj)
}
2017-05-18 18:15:06 +00:00
unsafe fn dealloc(py: _pyo3::Python, obj: *mut _pyo3::ffi::PyObject) {
2017-05-17 06:43:39 +00:00
let ptr = (obj as *mut u8).offset(base_offset() as isize) as *mut Self::Type;
std::ptr::drop_in_place(ptr);
2017-05-18 18:15:06 +00:00
<#base as _pyo3::class::BaseObject>::dealloc(py, obj)
2017-05-17 06:43:39 +00:00
}
}
}
}
2017-05-16 05:24:06 +00:00
fn impl_to_py_object(cls: &syn::Ident) -> Tokens {
quote! {
/// Identity conversion: allows using existing `PyObject` instances where
/// `T: ToPyObject` is expected.
2017-05-18 18:15:06 +00:00
impl _pyo3::ToPyObject for #cls where #cls: _pyo3::PythonObject {
2017-05-16 05:24:06 +00:00
#[inline]
2017-05-18 18:15:06 +00:00
fn to_py_object(&self, py: _pyo3::Python) -> _pyo3::PyObject {
_pyo3::PyClone::clone_ref(self, py).into_object()
2017-05-16 05:24:06 +00:00
}
#[inline]
2017-05-18 18:15:06 +00:00
fn into_py_object(self, _py: _pyo3::Python) -> _pyo3::PyObject {
2017-05-16 05:24:06 +00:00
self.into_object()
}
#[inline]
2017-05-18 18:15:06 +00:00
fn with_borrowed_ptr<F, R>(&self, _py: _pyo3::Python, f: F) -> R
where F: FnOnce(*mut _pyo3::ffi::PyObject) -> R
2017-05-16 05:24:06 +00:00
{
2017-05-18 18:15:06 +00:00
f(_pyo3::PythonObject::as_object(self).as_ptr())
2017-05-16 05:24:06 +00:00
}
}
}
}
fn impl_from_py_object(cls: &syn::Ident) -> Tokens {
quote! {
2017-05-18 18:15:06 +00:00
impl <'source> _pyo3::FromPyObject<'source> for #cls {
2017-05-16 05:24:06 +00:00
#[inline]
2017-05-18 18:15:06 +00:00
fn extract(py: _pyo3::Python, obj: &'source _pyo3::PyObject)
-> _pyo3::PyResult<#cls> {
2017-05-16 05:24:06 +00:00
Ok(obj.clone_ref(py).cast_into::<#cls>(py)?)
}
}
2017-05-18 18:15:06 +00:00
impl <'source> _pyo3::FromPyObject<'source> for &'source #cls {
2017-05-16 05:24:06 +00:00
#[inline]
2017-05-18 18:15:06 +00:00
fn extract(py: _pyo3::Python, obj: &'source _pyo3::PyObject)
-> _pyo3::PyResult<&'source #cls> {
2017-05-16 05:24:06 +00:00
Ok(obj.cast_as::<#cls>(py)?)
}
}
}
}
fn impl_python_object(cls: &syn::Ident) -> Tokens {
quote! {
2017-05-18 18:15:06 +00:00
impl _pyo3::PythonObject for #cls {
2017-05-16 05:24:06 +00:00
#[inline]
2017-05-18 18:15:06 +00:00
fn as_object(&self) -> &_pyo3::PyObject {
2017-05-16 05:24:06 +00:00
&self._unsafe_inner
}
#[inline]
2017-05-18 18:15:06 +00:00
fn into_object(self) -> _pyo3::PyObject {
2017-05-16 05:24:06 +00:00
self._unsafe_inner
}
/// Unchecked downcast from PyObject to Self.
/// Undefined behavior if the input object does not have the expected type.
#[inline]
2017-05-18 18:15:06 +00:00
unsafe fn unchecked_downcast_from(obj: _pyo3::PyObject) -> Self {
2017-05-16 05:24:06 +00:00
#cls { _unsafe_inner: obj }
}
/// Unchecked downcast from PyObject to Self.
/// Undefined behavior if the input object does not have the expected type.
#[inline]
2017-05-18 18:15:06 +00:00
unsafe fn unchecked_downcast_borrow_from<'a>(obj: &'a _pyo3::PyObject) -> &'a Self {
2017-05-16 05:24:06 +00:00
std::mem::transmute(obj)
}
}
}
}
fn impl_checked_downcast(cls: &syn::Ident) -> Tokens {
quote! {
2017-05-18 18:15:06 +00:00
impl _pyo3::PythonObjectWithCheckedDowncast for #cls {
2017-05-16 05:24:06 +00:00
#[inline]
2017-05-18 18:15:06 +00:00
fn downcast_from<'p>(py: _pyo3::Python<'p>, obj: _pyo3::PyObject)
-> Result<#cls, _pyo3::PythonObjectDowncastError<'p>> {
2017-05-16 05:24:06 +00:00
if py.get_type::<#cls>().is_instance(py, &obj) {
Ok(#cls { _unsafe_inner: obj })
} else {
2017-05-18 18:15:06 +00:00
Err(_pyo3::PythonObjectDowncastError(py))
2017-05-16 05:24:06 +00:00
}
}
#[inline]
2017-05-18 18:15:06 +00:00
fn downcast_borrow_from<'a, 'p>(py: _pyo3::Python<'p>, obj: &'a _pyo3::PyObject)
-> Result<&'a #cls, _pyo3::PythonObjectDowncastError<'p>> {
2017-05-16 05:24:06 +00:00
if py.get_type::<#cls>().is_instance(py, obj) {
unsafe { Ok(std::mem::transmute(obj)) }
} else {
2017-05-18 18:15:06 +00:00
Err(_pyo3::PythonObjectDowncastError(py))
2017-05-16 05:24:06 +00:00
}
}
}
}
}