Add __dict__ support (#68)

This commit is contained in:
messense 2017-07-28 22:21:59 +08:00 committed by GitHub
parent 60f6a73217
commit 0e2afb7b8b
5 changed files with 56 additions and 6 deletions

View File

@ -12,6 +12,8 @@ Changes
* Added subclass support #64
* Added `self.__dict__` supoort #68
0.1.0 (07-23-2017)
^^^^^^^^^^^^^^^^^^

View File

@ -62,6 +62,7 @@ python object that can be collector `PyGCProtocol` trait has to be implemented.
* `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
* `dict` - adds `__dict__` support, the instances of this type have a dictionary containing instance variables
## Constructor
@ -127,8 +128,7 @@ parent class.
## Object properties
Instance's `__dict__` attributes is not supported by pyo3 library. But it is
possible to specify instance get/set descriptors. Descriptor methods can be defined in
Descriptor methods can be defined in
`#[methods]` `impl` block only and has to be annotated with `#[getter]` or `[setter]`
attributes. i.e.

View File

@ -162,9 +162,12 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
// insert space for weak ref
let mut has_weakref = false;
let mut has_dict = false;
for f in flags.iter() {
if *f == syn::Ident::from("_pyo3::typeob::PY_TYPE_FLAG_WEAKREF") {
has_weakref = true;
} else if *f == syn::Ident::from("_pyo3::typeob::PY_TYPE_FLAG_DICT") {
has_dict = true;
}
}
let weakref = if has_weakref {
@ -172,6 +175,11 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
} else {
syn::Ident::from("0")
};
let dict = if has_dict {
syn::Ident::from("std::mem::size_of::<*const _pyo3::ffi::PyObject>()")
} else {
syn::Ident::from("0")
};
quote! {
impl _pyo3::typeob::PyTypeInfo for #cls {
@ -184,7 +192,7 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
const SIZE: usize = {
Self::OFFSET as usize +
std::mem::size_of::<#cls>() + #weakref
std::mem::size_of::<#cls>() + #weakref + #dict
};
const OFFSET: isize = {
// round base_size up to next multiple of align
@ -384,6 +392,10 @@ fn parse_attribute(attr: String) -> (HashMap<&'static str, syn::Ident>,
flags.push(syn::Ident::from("_pyo3::typeob::PY_TYPE_FLAG_BASETYPE"));
continue
}
"dict" => {
flags.push(syn::Ident::from("_pyo3::typeob::PY_TYPE_FLAG_DICT"));
continue
}
_ => {
println!("Unsupported parameter: {:?}", key);
}

View File

@ -55,6 +55,9 @@ pub const PY_TYPE_FLAG_WEAKREF: usize = 1<<1;
/// Type object can be used as the base type of another type
pub const PY_TYPE_FLAG_BASETYPE: usize = 1<<2;
/// The instances of this type have a dictionary containing instance variables
pub const PY_TYPE_FLAG_DICT: usize = 1<<3;
impl<'a, T: ?Sized> PyTypeInfo for &'a T where T: PyTypeInfo {
type Type = T::Type;
@ -208,10 +211,17 @@ pub fn initialize_type<'p, T>(py: Python<'p>, module_name: Option<&str>) -> PyRe
// type size
type_object.tp_basicsize = <T as PyTypeInfo>::SIZE as ffi::Py_ssize_t;
let mut offset = T::SIZE;
// 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;
offset -= std::mem::size_of::<*const ffi::PyObject>();
type_object.tp_weaklistoffset = offset as isize;
}
// __dict__ support
if T::FLAGS & PY_TYPE_FLAG_DICT != 0 {
offset -= std::mem::size_of::<*const ffi::PyObject>();
type_object.tp_dictoffset = offset as isize;
}
// GC support

View File

@ -1254,3 +1254,29 @@ fn subclass() {
.map_err(|e| e.print(py))
.unwrap();
}
#[py::class(dict)]
struct DunderDictSupport {
token: PyToken,
}
#[test]
fn dunder_dict_support() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = Py::new_ref(py, |t| DunderDictSupport{token: t}).unwrap();
py_run!(py, inst, "inst.a = 1; assert inst.a == 1");
}
#[py::class(weakref, dict)]
struct WeakRefDunderDictSupport {
token: PyToken,
}
#[test]
fn weakref_dunder_dict_support() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = Py::new_ref(py, |t| WeakRefDunderDictSupport{token: t}).unwrap();
py_run!(py, inst, "import weakref; assert weakref.ref(inst)() is inst; inst.a = 1; assert inst.a == 1");
}