From afcfed492b5e45ff1816af3c5c41c811a74adeab Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Thu, 27 Jul 2017 19:47:01 -0700 Subject: [PATCH] only c classes only as base class --- guide/src/class.md | 24 ++++++-- pyo3cls/src/py_class.rs | 120 +++++++++------------------------------- src/freelist.rs | 4 +- src/instance.rs | 4 +- src/objectprotocol.rs | 12 ++-- src/objects/mod.rs | 23 ++------ src/objects/superobj.rs | 54 ------------------ src/typeob.rs | 45 ++++++--------- 8 files changed, 79 insertions(+), 207 deletions(-) delete mode 100644 src/objects/superobj.rs diff --git a/guide/src/class.md b/guide/src/class.md index a204aaf9..68ae5906 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -59,8 +59,8 @@ so that they can benefit from a freelist. `XXX` is a number of items for free li participate in python garbage collector. If custom class contains references to other python object that can be collector `PyGCProtocol` trait has to be implemented. * `weakref` - adds support for python weak references -* `base=xxx.YYY` - use custom base class. It is not possible to call constructor -of base class at the moment. `xxx.YYY`, `xxx` - module name, `YYY` class name. +* `base=BaseType` - use custom base class. BaseType is type which is +implements `PyTypeInfo` trait. * `subclass` - adds subclass support so that Python classes can inherit from this class @@ -105,12 +105,26 @@ By default `PyObject` is used as default base class. To override default base cl `base` parameter to `py::class` needs to be used. Value is full path to base class. ```rust -#[py::class(base=asyncio.protocols.AbstractEventLoop)] -class MyEventLoop { - ... + +#[py::class] +struct BaseClass { + pub fn method(&self) -> PyResult<() { + Ok(()) + } +} + +#[py::class(base=BaseClass)] +struct MyEventLoop { + fn method2(&self) -> PyResult<()> { + self.get_super().method() + } } ``` +`ObjectProtocol` trait provide `get_super()` method. It returns reference to instance of +parent class. + + ## Object properties Instance's `__dict__` attributes is not supported by pyo3 library. But it is diff --git a/pyo3cls/src/py_class.rs b/pyo3cls/src/py_class.rs index 3ae2f445..6442e1be 100644 --- a/pyo3cls/src/py_class.rs +++ b/pyo3cls/src/py_class.rs @@ -10,10 +10,8 @@ use utils; pub fn build_py_class(ast: &mut syn::DeriveInput, attr: String) -> Tokens { - let (params, flags, base_cls) = parse_attribute(attr); + let (params, flags, base) = parse_attribute(attr); let doc = utils::get_doc(&ast.attrs, true); - - let base = syn::Ident::from("_pyo3::PyObjectRef"); let mut token: Option = None; match ast.body { @@ -29,7 +27,7 @@ pub fn build_py_class(ast: &mut syn::DeriveInput, attr: String) -> Tokens { } let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_CLS_{}", ast.ident)); - let tokens = impl_class(&ast.ident, &base, token, doc, params, flags, base_cls); + let tokens = impl_class(&ast.ident, &base, token, doc, params, flags); quote! { #[allow(non_upper_case_globals, unused_attributes, @@ -45,8 +43,7 @@ pub fn build_py_class(ast: &mut syn::DeriveInput, attr: String) -> Tokens { fn impl_class(cls: &syn::Ident, base: &syn::Ident, token: Option, doc: syn::Lit, - params: HashMap<&'static str, syn::Ident>, - flags: Vec, base_cls: Option<(String, String)>) -> Tokens { + params: HashMap<&'static str, syn::Ident>, flags: Vec) -> Tokens { let cls_name = match params.get("name") { Some(name) => quote! { #name }.as_str().to_string(), None => quote! { #cls }.as_str().to_string() @@ -113,7 +110,7 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident, fn as_ptr(&self) -> *mut ffi::PyObject { unsafe { {self as *const _ as *mut u8} - .offset(-<#cls as _pyo3::typeob::PyTypeInfo>::offset()) + .offset(-<#cls as _pyo3::typeob::PyTypeInfo>::OFFSET) as *mut ffi::PyObject } } @@ -176,86 +173,27 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident, syn::Ident::from("0") }; - // base class - let offset_code = if let Some((m, cls_name)) = base_cls { - quote! { - #[inline] - unsafe fn base_type_object() -> &'static mut ffi::PyTypeObject { - static mut TYPE_OBJECT: *mut ffi::PyTypeObject = 0 as *mut ffi::PyTypeObject; - - if TYPE_OBJECT.is_null() { - // load module - let gil = _pyo3::Python::acquire_gil(); - let py = gil.python(); - - let imp = py.import(#m) - .map_err(|e| e.print(py)) - .expect(format!( - "Can not import module: {}", #m).as_ref()); - let cls = imp.get(#cls_name) - .map_err(|e| e.print(py)) - .expect(format!( - "Can not load exception class: {}.{}", #m, #cls_name).as_ref()); - TYPE_OBJECT = cls.into_ptr() as *mut ffi::PyTypeObject; - } - &mut *TYPE_OBJECT - } - #[inline] - fn offset() -> isize { - static mut OFFSET: isize = 0; - unsafe { - if OFFSET == 0 { - // round base_size up to next multiple of align - OFFSET = ( - ((Self::base_type_object().tp_basicsize as usize) + - std::mem::align_of::<#cls>()-1) / - std::mem::align_of::<#cls>() * std::mem::align_of::<#cls>()) - as isize - } - OFFSET - } - } - } - } else { - quote! { - #[inline] - fn offset() -> isize { - static mut OFFSET: isize = 0; - unsafe { - if OFFSET == 0 { - // round base_size up to next multiple of align - OFFSET = ( - (<#base as _pyo3::typeob::PyTypeInfo>::size() + - std::mem::align_of::<#cls>()-1) / - std::mem::align_of::<#cls>() * std::mem::align_of::<#cls>()) - as isize - } - OFFSET - } - } - } - }; - quote! { impl _pyo3::typeob::PyTypeInfo for #cls { type Type = #cls; + type BaseType = #base; + const NAME: &'static str = #cls_name; const DESCRIPTION: &'static str = #doc; const FLAGS: usize = #(#flags)|*; - #[inline] - fn size() -> usize { - static mut SIZE: usize = 0; - - unsafe { - if SIZE == 0 { - SIZE = Self::offset() as usize + std::mem::size_of::<#cls>() + #weakref; - } - SIZE - } - } - - #offset_code + const SIZE: usize = { + Self::OFFSET as usize + + std::mem::size_of::<#cls>() + #weakref + }; + const OFFSET: isize = { + // round base_size up to next multiple of align + ( + (<#base as _pyo3::typeob::PyTypeInfo>::SIZE + + std::mem::align_of::<#cls>() - 1) / + std::mem::align_of::<#cls>() * std::mem::align_of::<#cls>() + ) as isize + }; #[inline] unsafe fn type_object() -> &'static mut _pyo3::ffi::PyTypeObject { @@ -301,7 +239,7 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident, if checked { let ptr = (ptr as *mut u8) - .offset(<#cls as _pyo3::typeob::PyTypeInfo>::offset()) as *mut #cls; + .offset(<#cls as _pyo3::typeob::PyTypeInfo>::OFFSET) as *mut #cls; Some(ptr.as_ref().unwrap()) } else { None @@ -316,7 +254,7 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident, if (*ptr).ob_type == <#cls as _pyo3::typeob::PyTypeInfo>::type_object() { let ptr = (ptr as *mut u8) - .offset(<#cls as _pyo3::typeob::PyTypeInfo>::offset()) as *mut #cls; + .offset(<#cls as _pyo3::typeob::PyTypeInfo>::OFFSET) as *mut #cls; Some(ptr.as_ref().unwrap()) } else { None @@ -328,14 +266,14 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident, unsafe fn unchecked_downcast_from(ob: &_pyo3::PyObjectRef) -> &Self { let ptr = (ob.as_ptr() as *mut u8) - .offset(<#cls as _pyo3::typeob::PyTypeInfo>::offset()) as *mut #cls; + .offset(<#cls as _pyo3::typeob::PyTypeInfo>::OFFSET) as *mut #cls; &*ptr } #[inline] unsafe fn unchecked_mut_downcast_from(ob: &_pyo3::PyObjectRef) -> &mut Self { let ptr = (ob.as_ptr() as *mut u8) - .offset(<#cls as _pyo3::typeob::PyTypeInfo>::offset()) as *mut #cls; + .offset(<#cls as _pyo3::typeob::PyTypeInfo>::OFFSET) as *mut #cls; &mut *ptr } } @@ -350,7 +288,7 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident, if checked { let ptr = (ptr as *mut u8) - .offset(<#cls as _pyo3::typeob::PyTypeInfo>::offset()) as *mut #cls; + .offset(<#cls as _pyo3::typeob::PyTypeInfo>::OFFSET) as *mut #cls; Some(ptr.as_mut().unwrap()) } else { None @@ -364,7 +302,7 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident, if (*ptr).ob_type == <#cls as _pyo3::typeob::PyTypeInfo>::type_object() { let ptr = (ptr as *mut u8) - .offset(<#cls as _pyo3::typeob::PyTypeInfo>::offset()) as *mut #cls; + .offset(<#cls as _pyo3::typeob::PyTypeInfo>::OFFSET) as *mut #cls; Some(ptr.as_mut().unwrap()) } else { None @@ -390,10 +328,10 @@ fn is_python_token(field: &syn::Field) -> bool { } fn parse_attribute(attr: String) -> (HashMap<&'static str, syn::Ident>, - Vec, Option<(String, String)>) { + Vec, syn::Ident) { let mut params = HashMap::new(); let mut flags = vec![syn::Ident::from("0")]; - let mut base = None; + let mut base = syn::Ident::from("_pyo3::PyObjectRef"); if let Ok(tts) = syn::parse_token_trees(&attr) { let mut elem = Vec::new(); @@ -499,11 +437,7 @@ fn parse_attribute(attr: String) -> (HashMap<&'static str, syn::Ident>, el.to_tokens(&mut t); m += t.as_str().trim(); } - m = m.trim_matches('.').to_owned(); - let mut t = Tokens::new(); - elem[elem.len()-1].to_tokens(&mut t); - let cls = t.as_str().trim().to_owned(); - base = Some((m, cls)); + base = syn::Ident::from(m.as_str()); }, _ => { println!("Unsupported parameter: {:?}", key); diff --git a/src/freelist.rs b/src/freelist.rs index 35525fdf..627dd31f 100644 --- a/src/freelist.rs +++ b/src/freelist.rs @@ -81,14 +81,14 @@ impl PyObjectAlloc for T where T: PyObjectWithFreeList { ffi::PyType_GenericAlloc(::type_object(), 0) }; - let ptr = (obj as *mut u8).offset(::offset()) as *mut T; + let ptr = (obj as *mut u8).offset(::OFFSET) as *mut T; std::ptr::write(ptr, value); Ok(obj) } unsafe fn dealloc(_py: Python, obj: *mut ffi::PyObject) { - let ptr = (obj as *mut u8).offset(::offset()) as *mut T; + let ptr = (obj as *mut u8).offset(::OFFSET) as *mut T; std::ptr::drop_in_place(ptr); if let Some(obj) = ::get_free_list().insert(obj) { diff --git a/src/instance.rs b/src/instance.rs index 987622d9..b76d6d27 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -206,14 +206,14 @@ impl AsPyRef for Py where T: PyTypeInfo { #[inline] default fn as_ref(&self, _py: Python) -> &T { unsafe { - let ptr = (self.as_ptr() as *mut u8).offset(T::offset()) as *mut T; + let ptr = (self.as_ptr() as *mut u8).offset(T::OFFSET) as *mut T; ptr.as_ref().unwrap() } } #[inline] default fn as_mut(&self, _py: Python) -> &mut T { unsafe { - let ptr = (self.as_ptr() as *mut u8).offset(T::offset()) as *mut T; + let ptr = (self.as_ptr() as *mut u8).offset(T::OFFSET) as *mut T; ptr.as_mut().unwrap() } } diff --git a/src/objectprotocol.rs b/src/objectprotocol.rs index f8e38915..ca36cbba 100644 --- a/src/objectprotocol.rs +++ b/src/objectprotocol.rs @@ -8,9 +8,10 @@ use ffi; use err::{self, PyErr, PyResult}; use python::{Python, ToPyPointer, PyDowncastFrom}; use object::PyObject; -use objects::{PyObjectRef, PyDict, PyString, PyIterator, PyType, PySuper}; +use objects::{PyObjectRef, PyDict, PyString, PyIterator, PyType}; use conversion::{ToPyObject, ToBorrowedObject, IntoPyTuple, FromPyObject}; use instance::PyObjectWithToken; +use typeob::PyTypeInfo; /// Python object model helper methods @@ -123,7 +124,8 @@ pub trait ObjectProtocol { /// Gets the Python super object for this object. /// This is equivalent to the Python expression: 'super()' - fn get_super(&self) -> &PySuper; + fn get_super(&self) -> &::BaseType + where Self: PyTypeInfo, ::BaseType: PyDowncastFrom; /// Casts the PyObject to a concrete Python object type. fn cast_as<'a, D>(&'a self) -> Option<&'a D> @@ -355,8 +357,10 @@ impl ObjectProtocol for T where T: PyObjectWithToken + ToPyPointer { } } - fn get_super(&self) -> &PySuper { - PySuper::new(self) + fn get_super(&self) -> &::BaseType + where Self: PyTypeInfo, ::BaseType: PyDowncastFrom + { + unsafe { self.py().cast_from_borrowed_ptr(self.as_ptr()) } } #[inline] diff --git a/src/objects/mod.rs b/src/objects/mod.rs index e078c1ad..37ba0cfd 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -3,7 +3,6 @@ #[macro_use] mod exc_impl; pub use self::typeobject::PyType; -pub use self::superobj::PySuper; pub use self::module::PyModule; pub use self::iterator::PyIterator; pub use self::boolobject::PyBool; @@ -140,24 +139,11 @@ macro_rules! pyobject_nativetype( impl $crate::typeob::PyTypeInfo for $name { type Type = (); + type BaseType = $crate::PyObjectRef; + const NAME: &'static str = stringify!($name); - - #[inline] - fn size() -> usize { - static mut SIZE: usize = 0; - - unsafe { - if SIZE == 0 { - SIZE = $crate::std::mem::size_of::<$crate::ffi::PyObject>(); - } - SIZE - } - } - - #[inline] - fn offset() -> isize { - 0 - } + const SIZE: usize = $crate::std::mem::size_of::<$crate::ffi::PyObject>(); + const OFFSET: isize = 0; #[inline] unsafe fn type_object() -> &'static mut $crate::ffi::PyTypeObject { @@ -266,7 +252,6 @@ mod slice; mod stringdata; mod stringutils; mod set; -mod superobj; pub mod exc; #[cfg(Py_3)] diff --git a/src/objects/superobj.rs b/src/objects/superobj.rs deleted file mode 100644 index 515e7dea..00000000 --- a/src/objects/superobj.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2017-present PyO3 Project and Contributors - -use std; - -use ffi; -use object::PyObject; -use objects::{PyObjectRef, PyDict}; -use objectprotocol::ObjectProtocol; -use conversion::IntoPyTuple; -use python::{ToPyPointer, IntoPyPointer}; -use err::{self, PyResult}; -use instance::PyObjectWithToken; - -/// Represents a reference to a Python `super object`. -pub struct PySuper(PyObject); - -pyobject_convert!(PySuper); -pyobject_nativetype!(PySuper, PySuper_Type, PySuper_Check); - - -impl PySuper { - - pub fn new(object: &T) -> &PySuper where T: PyObjectWithToken + ToPyPointer { - // Create the arguments for super() - unsafe { - let o = object.py().from_borrowed_ptr(object.as_ptr()); - let args = (object.get_type(), o).into_tuple(object.py()).into_ptr(); - - // Creat the class super() - let ptr = ffi::PyType_GenericNew(&mut ffi::PySuper_Type, args, std::ptr::null_mut()); - let oref = object.py().cast_from_ptr(ptr); - - // call __init__ on super object - if (*ffi::Py_TYPE(ptr)).tp_init.unwrap()(ptr, args, std::ptr::null_mut()) == -1 { - err::panic_after_error() - } - oref - } - } - - pub fn __new__(&self, args: A, kwargs: Option<&PyDict>) - -> PyResult<&PyObjectRef> - where A: IntoPyTuple - { - self.call_method("__new__", args, kwargs) - } - - pub fn __init__(&self, args: A, kwargs: Option<&PyDict>) - -> PyResult<&PyObjectRef> - where A: IntoPyTuple - { - self.call_method("__init__", args, kwargs) - } -} diff --git a/src/typeob.rs b/src/typeob.rs index cd6fda2c..f1dd6658 100644 --- a/src/typeob.rs +++ b/src/typeob.rs @@ -27,24 +27,17 @@ pub trait PyTypeInfo { /// Class doc string const DESCRIPTION: &'static str = "\0"; - /// Type flags (ie PyType_GC, PyType_WeakRef) - const FLAGS: usize = 0; - /// Size of the rust PyObject structure (PyObject + rust structure) - fn size() -> usize; + const SIZE: usize; /// `Type` instance offset inside PyObject structure - fn offset() -> isize; + const OFFSET: isize; + + /// Type flags (ie PY_TYPE_FLAG_GC, PY_TYPE_FLAG_WEAKREF) + const FLAGS: usize = 0; /// Base class - unsafe fn base_type_object() -> &'static mut ffi::PyTypeObject { - static mut TYPE_OBJECT: *mut ffi::PyTypeObject = std::ptr::null_mut(); - - if TYPE_OBJECT.is_null() { - TYPE_OBJECT = &mut ffi::PyBaseObject_Type; - } - &mut *TYPE_OBJECT - } + type BaseType: PyTypeInfo; /// PyTypeObject instance for this type unsafe fn type_object() -> &'static mut ffi::PyTypeObject; @@ -66,20 +59,13 @@ pub const PY_TYPE_FLAG_BASETYPE: usize = 1<<2; impl<'a, T: ?Sized> PyTypeInfo for &'a T where T: PyTypeInfo { type Type = T::Type; + type BaseType = T::BaseType; const NAME: &'static str = T::NAME; const DESCRIPTION: &'static str = T::DESCRIPTION; + const SIZE: usize = T::SIZE; + const OFFSET: isize = T::OFFSET; const FLAGS: usize = T::FLAGS; - #[inline] - default fn size() -> usize { - ::size() - } - - #[inline] - default fn offset() -> isize { - ::offset() - } - #[inline] default unsafe fn type_object() -> &'static mut ffi::PyTypeObject { ::type_object() @@ -113,14 +99,14 @@ impl PyObjectAlloc for T where T : PyTypeInfo { let obj = ffi::PyType_GenericAlloc(T::type_object(), 0); - let ptr = (obj as *mut u8).offset(T::offset()) as *mut T; + let ptr = (obj as *mut u8).offset(T::OFFSET) as *mut T; std::ptr::write(ptr, value); Ok(obj) } default unsafe fn dealloc(_py: Python, obj: *mut ffi::PyObject) { - let ptr = (obj as *mut u8).offset(T::offset()) as *mut T; + let ptr = (obj as *mut u8).offset(T::OFFSET) as *mut T; std::ptr::drop_in_place(ptr); if ffi::PyObject_CallFinalizerFromDealloc(obj) < 0 { @@ -179,20 +165,23 @@ pub fn initialize_type<'p, T>(py: Python<'p>, module_name: Option<&str>) -> PyRe "Module name/type name must not contain NUL byte").into_raw(); let type_object: &mut ffi::PyTypeObject = unsafe{&mut *T::type_object()}; + let base_type_object: &mut ffi::PyTypeObject = unsafe{ + &mut *::type_object()}; + type_object.tp_name = name; type_object.tp_doc = T::DESCRIPTION.as_ptr() as *const _; - type_object.tp_base = unsafe{::base_type_object()}; + type_object.tp_base = base_type_object; // dealloc type_object.tp_dealloc = Some(tp_dealloc_callback::); // type size - type_object.tp_basicsize = ::size() as ffi::Py_ssize_t; + type_object.tp_basicsize = ::SIZE as ffi::Py_ssize_t; // weakref support (check py3cls::py_class::impl_class) if T::FLAGS & PY_TYPE_FLAG_WEAKREF != 0 { type_object.tp_weaklistoffset = - (T::size() - std::mem::size_of::<*const ffi::PyObject>()) as isize; + (T::SIZE - std::mem::size_of::<*const ffi::PyObject>()) as isize; } // GC support