Add __dict__ support (#68)
This commit is contained in:
parent
60f6a73217
commit
0e2afb7b8b
|
@ -12,6 +12,8 @@ Changes
|
|||
|
||||
* Added subclass support #64
|
||||
|
||||
* Added `self.__dict__` supoort #68
|
||||
|
||||
|
||||
0.1.0 (07-23-2017)
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue