From ce15dda5b6917e2dd8a2fc67c619dafdf7c2285f Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 24 Jul 2017 12:19:05 -0700 Subject: [PATCH] Allow to add gc support without implementing PyGCProtocol #57 --- CHANGES.txt | 13 +++++++++++++ Cargo.toml | 2 +- guide/src/class.md | 4 ++++ pyo3cls/Cargo.toml | 2 +- pyo3cls/src/py_class.rs | 42 ++++++++++++++++++++++++++++++----------- src/ffi3/object.rs | 22 +++++++-------------- src/ffi3/objimpl.rs | 30 +++++++++-------------------- src/typeob.rs | 24 ++++++++++++++++++----- tests/test_class.rs | 12 ++++++++++++ 9 files changed, 97 insertions(+), 54 deletions(-) create mode 100644 CHANGES.txt diff --git a/CHANGES.txt b/CHANGES.txt new file mode 100644 index 00000000..cb68fbdf --- /dev/null +++ b/CHANGES.txt @@ -0,0 +1,13 @@ +Changes +------- + +0.1.x (xx-xx-2017) +^^^^^^^^^^^^^^^^^^ + +* Allow to add gc support without implementing PyGCProtocol #57 + + +0.1.0 (07-23-2017) +^^^^^^^^^^^^^^^^^^ + +* Initial release diff --git a/Cargo.toml b/Cargo.toml index 3518e349..8d275516 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pyo3" -version = "0.1.1-dev" +version = "0.1.1" description = "Bindings to Python" authors = ["PyO3 Project and Contributors Tokens { - let params = parse_attribute(attr); + let (params, flags) = parse_attribute(attr); let doc = utils::get_doc(&ast.attrs, true); let base = syn::Ident::from("_pyo3::PyObjectRef"); @@ -29,7 +29,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); + let tokens = impl_class(&ast.ident, &base, token, doc, params, flags); quote! { #[allow(non_upper_case_globals, unused_attributes, @@ -45,7 +45,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>) -> 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() @@ -171,6 +171,8 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident, std::mem::align_of::<#cls>() * std::mem::align_of::<#cls>()) as isize }; + const FLAGS: usize = #(#flags)|*; + #[inline] unsafe fn type_object() -> &'static mut _pyo3::ffi::PyTypeObject { static mut TYPE_OBJECT: _pyo3::ffi::PyTypeObject = _pyo3::ffi::PyTypeObject_INIT; @@ -300,8 +302,9 @@ fn is_python_token(field: &syn::Field) -> bool { return false } -fn parse_attribute(attr: String) -> HashMap<&'static str, syn::Ident> { +fn parse_attribute(attr: String) -> (HashMap<&'static str, syn::Ident>, Vec) { let mut params = HashMap::new(); + let mut flags = vec![syn::Ident::from("0")]; if let Ok(tts) = syn::parse_token_trees(&attr) { let mut elem = Vec::new(); @@ -330,11 +333,6 @@ fn parse_attribute(attr: String) -> HashMap<&'static str, syn::Ident> { } for elem in elems { - if elem.len() < 3 { - println!("Wrong format: {:?}", elem); - continue - } - let key = match elem[0] { syn::TokenTree::Token(syn::Token::Ident(ref ident)) => { ident.as_ref().to_owned().to_lowercase() @@ -345,6 +343,27 @@ fn parse_attribute(attr: String) -> HashMap<&'static str, syn::Ident> { } }; + if elem.len() == 1 { + match key.as_ref() { + "gc" => { + flags.push(syn::Ident::from("_pyo3::typeob::PY_TYPE_FLAG_GC")); + continue + } + "weakref" => { + flags.push(syn::Ident::from("_pyo3::typeob::PY_TYPE_FLAG_WEAKREF")); + continue + } + _ => { + println!("Unsupported parameter: {:?}", key); + } + } + } + + if elem.len() < 3 { + println!("Wrong format: {:?}", elem); + continue + } + match elem[1] { syn::TokenTree::Token(syn::Token::Eq) => (), _ => { @@ -382,12 +401,13 @@ fn parse_attribute(attr: String) -> HashMap<&'static str, syn::Ident> { }, "base" => { - } + }, _ => { println!("Unsupported parameter: {:?}", key); } } } } - params + + (params, flags) } diff --git a/src/ffi3/object.rs b/src/ffi3/object.rs index 3cf85787..5c7f5d99 100644 --- a/src/ffi3/object.rs +++ b/src/ffi3/object.rs @@ -157,14 +157,11 @@ mod bufferinfo { pub use self::bufferinfo::*; pub type objobjproc = - unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) - -> c_int; + unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> c_int; pub type visitproc = - unsafe extern "C" fn(object: *mut PyObject, arg: *mut c_void) - -> c_int; + unsafe extern "C" fn(object: *mut PyObject, arg: *mut c_void) -> c_int; pub type traverseproc = - unsafe extern "C" fn(slf: *mut PyObject, visit: visitproc, - arg: *mut c_void) -> c_int; + unsafe extern "C" fn(slf: *mut PyObject, visit: visitproc, arg: *mut c_void) -> c_int; pub type freefunc = unsafe extern "C" fn(arg1: *mut c_void); @@ -206,8 +203,7 @@ pub type newfunc = unsafe extern "C" fn(arg1: *mut PyTypeObject, arg2: *mut PyObject, arg3: *mut PyObject) -> *mut PyObject; pub type allocfunc = - unsafe extern "C" fn(arg1: *mut PyTypeObject, - arg2: Py_ssize_t) -> *mut PyObject; + unsafe extern "C" fn(arg1: *mut PyTypeObject, arg2: Py_ssize_t) -> *mut PyObject; #[cfg(Py_LIMITED_API)] pub enum PyTypeObject { } @@ -264,7 +260,6 @@ mod typeobject { } macro_rules! as_expr { ($e:expr) => {$e} } - macro_rules! py_number_methods_init { ($($tail:tt)*) => { as_expr! { @@ -598,10 +593,9 @@ impl Default for PyType_Spec { pub fn PyType_FromSpec(arg1: *mut PyType_Spec) -> *mut PyObject; pub fn PyType_FromSpecWithBases(arg1: *mut PyType_Spec, arg2: *mut PyObject) - -> *mut PyObject; + -> *mut PyObject; - pub fn PyType_GetSlot(arg1: *mut PyTypeObject, arg2: c_int) - -> *mut c_void; + pub fn PyType_GetSlot(arg1: *mut PyTypeObject, arg2: c_int) -> *mut c_void; } #[cfg_attr(windows, link(name="pythonXY"))] extern "C" { @@ -727,9 +721,7 @@ pub const Py_TPFLAGS_BASE_EXC_SUBCLASS : c_ulong = (1<<30); pub const Py_TPFLAGS_TYPE_SUBCLASS : c_ulong = (1<<31); pub const Py_TPFLAGS_DEFAULT : c_ulong = ( - Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | - Py_TPFLAGS_HAVE_VERSION_TAG | - 0); + Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | Py_TPFLAGS_HAVE_VERSION_TAG | 0); pub const Py_TPFLAGS_HAVE_FINALIZE : c_ulong = (1<<0); diff --git a/src/ffi3/objimpl.rs b/src/ffi3/objimpl.rs index 707e26da..d9c4f3b5 100644 --- a/src/ffi3/objimpl.rs +++ b/src/ffi3/objimpl.rs @@ -6,19 +6,16 @@ use ffi3::object::*; #[cfg_attr(windows, link(name="pythonXY"))] extern "C" { pub fn PyObject_Malloc(size: size_t) -> *mut c_void; pub fn PyObject_Calloc(nelem: size_t, elsize: size_t) -> *mut c_void; - pub fn PyObject_Realloc(ptr: *mut c_void, new_size: size_t) - -> *mut c_void; + pub fn PyObject_Realloc(ptr: *mut c_void, new_size: size_t) -> *mut c_void; pub fn PyObject_Free(ptr: *mut c_void) -> (); #[cfg(not(Py_LIMITED_API))] pub fn _Py_GetAllocatedBlocks() -> Py_ssize_t; - pub fn PyObject_Init(arg1: *mut PyObject, arg2: *mut PyTypeObject) - -> *mut PyObject; + pub fn PyObject_Init(arg1: *mut PyObject, arg2: *mut PyTypeObject) -> *mut PyObject; pub fn PyObject_InitVar(arg1: *mut PyVarObject, arg2: *mut PyTypeObject, arg3: Py_ssize_t) -> *mut PyVarObject; pub fn _PyObject_New(arg1: *mut PyTypeObject) -> *mut PyObject; - pub fn _PyObject_NewVar(arg1: *mut PyTypeObject, arg2: Py_ssize_t) - -> *mut PyVarObject; + pub fn _PyObject_NewVar(arg1: *mut PyTypeObject, arg2: Py_ssize_t) -> *mut PyVarObject; pub fn PyGC_Collect() -> Py_ssize_t; } @@ -28,12 +25,8 @@ use ffi3::object::*; #[cfg(not(Py_LIMITED_API))] pub struct PyObjectArenaAllocator { pub ctx: *mut c_void, - pub alloc: Option *mut c_void>, - pub free: Option ()>, + pub alloc: Option *mut c_void>, + pub free: Option ()>, } #[cfg(not(Py_LIMITED_API))] @@ -42,10 +35,8 @@ impl Default for PyObjectArenaAllocator { } #[cfg(not(Py_LIMITED_API))] #[cfg_attr(windows, link(name="pythonXY"))] extern "C" { - pub fn PyObject_GetArenaAllocator(allocator: *mut PyObjectArenaAllocator) - -> (); - pub fn PyObject_SetArenaAllocator(allocator: *mut PyObjectArenaAllocator) - -> (); + pub fn PyObject_GetArenaAllocator(allocator: *mut PyObjectArenaAllocator) -> (); + pub fn PyObject_SetArenaAllocator(allocator: *mut PyObjectArenaAllocator) -> (); } /// Test if a type has a GC head @@ -66,16 +57,14 @@ pub unsafe fn PyObject_IS_GC(o : *mut PyObject) -> c_int { } #[cfg_attr(windows, link(name="pythonXY"))] extern "C" { - pub fn _PyObject_GC_Resize(arg1: *mut PyVarObject, arg2: Py_ssize_t) - -> *mut PyVarObject; + pub fn _PyObject_GC_Resize(arg1: *mut PyVarObject, arg2: Py_ssize_t) -> *mut PyVarObject; #[cfg(not(Py_LIMITED_API))] pub fn _PyObject_GC_Malloc(size: size_t) -> *mut PyObject; #[cfg(not(Py_LIMITED_API))] pub fn _PyObject_GC_Calloc(size: size_t) -> *mut PyObject; pub fn _PyObject_GC_New(arg1: *mut PyTypeObject) -> *mut PyObject; - pub fn _PyObject_GC_NewVar(arg1: *mut PyTypeObject, arg2: Py_ssize_t) - -> *mut PyVarObject; + pub fn _PyObject_GC_NewVar(arg1: *mut PyTypeObject, arg2: Py_ssize_t) -> *mut PyVarObject; pub fn PyObject_GC_Track(arg1: *mut c_void) -> (); pub fn PyObject_GC_UnTrack(arg1: *mut c_void) -> (); pub fn PyObject_GC_Del(arg1: *mut c_void) -> (); @@ -94,4 +83,3 @@ pub unsafe fn PyObject_GET_WEAKREFS_LISTPTR(o : *mut PyObject) -> *mut *mut PyOb let weaklistoffset = (*Py_TYPE(o)).tp_weaklistoffset as isize; (o as *mut u8).offset(weaklistoffset) as *mut *mut PyObject } - diff --git a/src/typeob.rs b/src/typeob.rs index 5c160cfc..4d24a2d3 100644 --- a/src/typeob.rs +++ b/src/typeob.rs @@ -31,6 +31,9 @@ pub trait PyTypeInfo { /// `Type` instance offset inside PyObject structure const OFFSET: isize; + /// Type flags (ie PyType_GC, PyType_WeakRef) + const FLAGS: usize = 0; + /// PyTypeObject instance for this type unsafe fn type_object() -> &'static mut ffi::PyTypeObject; @@ -39,6 +42,12 @@ pub trait PyTypeInfo { } +/// type object supports python GC +pub const PY_TYPE_FLAG_GC: usize = 1<<0; + +/// Type object supports python weak references +pub const PY_TYPE_FLAG_WEAKREF: usize = 1<<1; + impl<'a, T: ?Sized> PyTypeInfo for &'a T where T: PyTypeInfo { type Type = T::Type; @@ -46,6 +55,7 @@ impl<'a, T: ?Sized> PyTypeInfo for &'a T where T: PyTypeInfo { const DESCRIPTION: &'static str = T::DESCRIPTION; const SIZE: usize = T::SIZE; const OFFSET: isize = T::OFFSET; + const FLAGS: usize = T::FLAGS; #[inline] default unsafe fn type_object() -> &'static mut ffi::PyTypeObject { @@ -224,7 +234,7 @@ pub fn initialize_type<'p, T>(py: Python<'p>, } // set type flags - py_class_flags(type_object); + py_class_flags::(type_object); // register type object unsafe { @@ -259,8 +269,10 @@ unsafe extern "C" fn tp_dealloc_callback(obj: *mut ffi::PyObject) } #[cfg(Py_3)] -fn py_class_flags(type_object: &mut ffi::PyTypeObject) { - if type_object.tp_traverse != None || type_object.tp_clear != None { +fn py_class_flags(type_object: &mut ffi::PyTypeObject) { + if type_object.tp_traverse != None || type_object.tp_clear != None || + T::FLAGS & PY_TYPE_FLAG_GC != 0 + { type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HAVE_GC; } else { type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT; @@ -268,8 +280,10 @@ fn py_class_flags(type_object: &mut ffi::PyTypeObject) { } #[cfg(not(Py_3))] -fn py_class_flags(type_object: &mut ffi::PyTypeObject) { - if type_object.tp_traverse != None || type_object.tp_clear != None { +fn py_class_flags(type_object: &mut ffi::PyTypeObject) { + if type_object.tp_traverse != None || type_object.tp_clear != None || + T::FLAGS & PY_TYPE_FLAG_GC != 0 + { type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_CHECKTYPES | ffi::Py_TPFLAGS_HAVE_GC; } else { type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_CHECKTYPES; diff --git a/tests/test_class.rs b/tests/test_class.rs index 47c0fd1b..6b18efe6 100644 --- a/tests/test_class.rs +++ b/tests/test_class.rs @@ -476,6 +476,18 @@ fn gc_integration() { assert!(drop_called.load(Ordering::Relaxed)); } +#[py::class(gc)] +struct GCIntegration2 { + token: PyToken, +} +#[test] +fn gc_integration2() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst = Py::new_ref(py, |t| GCIntegration2{token: t}).unwrap(); + py_run!(py, inst, "import gc; assert inst in gc.get_objects()"); +} + #[py::class] pub struct Len { l: usize,