add weakref support #56

This commit is contained in:
Nikolay Kim 2017-07-24 13:03:18 -07:00
parent ce15dda5b6
commit 3ab5e4526c
5 changed files with 52 additions and 1 deletions

View File

@ -4,6 +4,8 @@ Changes
0.1.x (xx-xx-2017)
^^^^^^^^^^^^^^^^^^
* Added weakref support #56
* Allow to add gc support without implementing PyGCProtocol #57

View File

@ -44,6 +44,23 @@ are generated only if struct contains `PyToken` attribute.
TODO - continue
## py::class macro
Python class generation is powered by [Procedural Macros](https://doc.rust-lang.org/book/first-edition/procedural-macros.html).
To define python custom class, rust struct needs to be annotated with `#[py::class]` attribute.
`py::class` macro accepts following parameters:
* `name=XXX` - customize class name visible to python code. By default struct name is used as
a class name.
* `freelist=XXX` - `freelist` parameter add support of free allocation list to custom class.
The performance improvement applies to types that are often created and deleted in a row,
so that they can benefit from a freelist. `XXX` is a number of items for free list.
* `gc` - adds support for python garbage collector. classes that build with `gc` parameter
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
## Constructor
By default it is not possible to create instance of custom class from python code.

View File

@ -158,6 +158,19 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
}
};
// insert space for weak ref
let mut has_weakref = false;
for f in flags.iter() {
if *f == syn::Ident::from("_pyo3::typeob::PY_TYPE_FLAG_WEAKREF") {
has_weakref = true;
}
}
let weakref = if has_weakref {
syn::Ident::from("std::mem::size_of::<*const _pyo3::ffi::PyObject>()")
} else {
syn::Ident::from("0")
};
quote! {
impl _pyo3::typeob::PyTypeInfo for #cls {
type Type = #cls;
@ -167,7 +180,8 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
const SIZE: usize = Self::OFFSET as usize + std::mem::size_of::<#cls>();
const OFFSET: isize = {
// round base_size up to next multiple of align
((<#base as _pyo3::typeob::PyTypeInfo>::SIZE + std::mem::align_of::<#cls>() - 1) /
((<#base as _pyo3::typeob::PyTypeInfo>::SIZE + #weakref +
std::mem::align_of::<#cls>() - 1) /
std::mem::align_of::<#cls>() * std::mem::align_of::<#cls>()) as isize
};

View File

@ -170,6 +170,12 @@ pub fn initialize_type<'p, T>(py: Python<'p>,
// type size
type_object.tp_basicsize = <T as PyTypeInfo>::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::OFFSET -
(std::mem::size_of::<*const ffi::PyObject>() as isize);
}
// GC support
<T as class::gc::PyGCProtocolImpl>::update_type_object(type_object);

View File

@ -488,6 +488,18 @@ fn gc_integration2() {
py_run!(py, inst, "import gc; assert inst in gc.get_objects()");
}
#[py::class(weakref)]
struct WeakRefSupport {
token: PyToken,
}
#[test]
fn weakref_support() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = Py::new_ref(py, |t| WeakRefSupport{token: t}).unwrap();
py_run!(py, inst, "import weakref; assert weakref.ref(inst)() is inst");
}
#[py::class]
pub struct Len {
l: usize,