Allow to add gc support without implementing PyGCProtocol #57

This commit is contained in:
Nikolay Kim 2017-07-24 12:19:05 -07:00
parent 4b0a8f5019
commit ce15dda5b6
9 changed files with 97 additions and 54 deletions

13
CHANGES.txt Normal file
View File

@ -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

View File

@ -1,6 +1,6 @@
[package] [package]
name = "pyo3" name = "pyo3"
version = "0.1.1-dev" version = "0.1.1"
description = "Bindings to Python" description = "Bindings to Python"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3"] authors = ["PyO3 Project and Contributors <https://github.com/PyO3"]
readme = "README.md" readme = "README.md"

View File

@ -390,6 +390,10 @@ impl PyGCProtocol for ClassWithGCSupport {
Special protocol trait implementation has to be annotated with `#[py::proto]` attribute. Special protocol trait implementation has to be annotated with `#[py::proto]` attribute.
It is also possible to enable gc for custom class using `gc` parameter for `py::class` annotation.
i.e. `#[py::class(gc)]`. In that case instances of custom class participate in python garbage
collector, and it is possible to track them with `gc` module methods.
### Iterator Types ### Iterator Types
Iterators can be defined using the Iterators can be defined using the

View File

@ -1,6 +1,6 @@
[package] [package]
name = "pyo3cls" name = "pyo3cls"
version = "0.1.0" version = "0.1.1"
description = "Proc macros for PyO3 package" description = "Proc macros for PyO3 package"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3"] authors = ["PyO3 Project and Contributors <https://github.com/PyO3"]
homepage = "https://github.com/pyo3/pyo3" homepage = "https://github.com/pyo3/pyo3"

View File

@ -10,7 +10,7 @@ use utils;
pub fn build_py_class(ast: &mut syn::DeriveInput, attr: String) -> Tokens { pub fn build_py_class(ast: &mut syn::DeriveInput, attr: String) -> Tokens {
let params = parse_attribute(attr); let (params, flags) = parse_attribute(attr);
let doc = utils::get_doc(&ast.attrs, true); let doc = utils::get_doc(&ast.attrs, true);
let base = syn::Ident::from("_pyo3::PyObjectRef"); 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 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! { quote! {
#[allow(non_upper_case_globals, unused_attributes, #[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, fn impl_class(cls: &syn::Ident, base: &syn::Ident,
token: Option<syn::Ident>, doc: syn::Lit, token: Option<syn::Ident>, doc: syn::Lit,
params: HashMap<&'static str, syn::Ident>) -> Tokens { params: HashMap<&'static str, syn::Ident>, flags: Vec<syn::Ident>) -> Tokens {
let cls_name = match params.get("name") { let cls_name = match params.get("name") {
Some(name) => quote! { #name }.as_str().to_string(), Some(name) => quote! { #name }.as_str().to_string(),
None => quote! { #cls }.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 std::mem::align_of::<#cls>() * std::mem::align_of::<#cls>()) as isize
}; };
const FLAGS: usize = #(#flags)|*;
#[inline] #[inline]
unsafe fn type_object() -> &'static mut _pyo3::ffi::PyTypeObject { unsafe fn type_object() -> &'static mut _pyo3::ffi::PyTypeObject {
static mut TYPE_OBJECT: _pyo3::ffi::PyTypeObject = _pyo3::ffi::PyTypeObject_INIT; 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 return false
} }
fn parse_attribute(attr: String) -> HashMap<&'static str, syn::Ident> { fn parse_attribute(attr: String) -> (HashMap<&'static str, syn::Ident>, Vec<syn::Ident>) {
let mut params = HashMap::new(); let mut params = HashMap::new();
let mut flags = vec![syn::Ident::from("0")];
if let Ok(tts) = syn::parse_token_trees(&attr) { if let Ok(tts) = syn::parse_token_trees(&attr) {
let mut elem = Vec::new(); let mut elem = Vec::new();
@ -330,11 +333,6 @@ fn parse_attribute(attr: String) -> HashMap<&'static str, syn::Ident> {
} }
for elem in elems { for elem in elems {
if elem.len() < 3 {
println!("Wrong format: {:?}", elem);
continue
}
let key = match elem[0] { let key = match elem[0] {
syn::TokenTree::Token(syn::Token::Ident(ref ident)) => { syn::TokenTree::Token(syn::Token::Ident(ref ident)) => {
ident.as_ref().to_owned().to_lowercase() 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] { match elem[1] {
syn::TokenTree::Token(syn::Token::Eq) => (), syn::TokenTree::Token(syn::Token::Eq) => (),
_ => { _ => {
@ -382,12 +401,13 @@ fn parse_attribute(attr: String) -> HashMap<&'static str, syn::Ident> {
}, },
"base" => { "base" => {
} },
_ => { _ => {
println!("Unsupported parameter: {:?}", key); println!("Unsupported parameter: {:?}", key);
} }
} }
} }
} }
params
(params, flags)
} }

View File

@ -157,14 +157,11 @@ mod bufferinfo {
pub use self::bufferinfo::*; pub use self::bufferinfo::*;
pub type objobjproc = pub type objobjproc =
unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) unsafe extern "C" fn(arg1: *mut PyObject, arg2: *mut PyObject) -> c_int;
-> c_int;
pub type visitproc = pub type visitproc =
unsafe extern "C" fn(object: *mut PyObject, arg: *mut c_void) unsafe extern "C" fn(object: *mut PyObject, arg: *mut c_void) -> c_int;
-> c_int;
pub type traverseproc = pub type traverseproc =
unsafe extern "C" fn(slf: *mut PyObject, visit: visitproc, unsafe extern "C" fn(slf: *mut PyObject, visit: visitproc, arg: *mut c_void) -> c_int;
arg: *mut c_void) -> c_int;
pub type freefunc = pub type freefunc =
unsafe extern "C" fn(arg1: *mut c_void); unsafe extern "C" fn(arg1: *mut c_void);
@ -206,8 +203,7 @@ pub type newfunc =
unsafe extern "C" fn(arg1: *mut PyTypeObject, unsafe extern "C" fn(arg1: *mut PyTypeObject,
arg2: *mut PyObject, arg3: *mut PyObject) -> *mut PyObject; arg2: *mut PyObject, arg3: *mut PyObject) -> *mut PyObject;
pub type allocfunc = pub type allocfunc =
unsafe extern "C" fn(arg1: *mut PyTypeObject, unsafe extern "C" fn(arg1: *mut PyTypeObject, arg2: Py_ssize_t) -> *mut PyObject;
arg2: Py_ssize_t) -> *mut PyObject;
#[cfg(Py_LIMITED_API)] #[cfg(Py_LIMITED_API)]
pub enum PyTypeObject { } pub enum PyTypeObject { }
@ -264,7 +260,6 @@ mod typeobject {
} }
macro_rules! as_expr { ($e:expr) => {$e} } macro_rules! as_expr { ($e:expr) => {$e} }
macro_rules! py_number_methods_init { macro_rules! py_number_methods_init {
($($tail:tt)*) => { ($($tail:tt)*) => {
as_expr! { as_expr! {
@ -600,8 +595,7 @@ impl Default for PyType_Spec {
pub fn PyType_FromSpecWithBases(arg1: *mut PyType_Spec, arg2: *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) pub fn PyType_GetSlot(arg1: *mut PyTypeObject, arg2: c_int) -> *mut c_void;
-> *mut c_void;
} }
#[cfg_attr(windows, link(name="pythonXY"))] extern "C" { #[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_TYPE_SUBCLASS : c_ulong = (1<<31);
pub const Py_TPFLAGS_DEFAULT : c_ulong = ( pub const Py_TPFLAGS_DEFAULT : c_ulong = (
Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | Py_TPFLAGS_HAVE_VERSION_TAG | 0);
Py_TPFLAGS_HAVE_VERSION_TAG |
0);
pub const Py_TPFLAGS_HAVE_FINALIZE : c_ulong = (1<<0); pub const Py_TPFLAGS_HAVE_FINALIZE : c_ulong = (1<<0);

View File

@ -6,19 +6,16 @@ use ffi3::object::*;
#[cfg_attr(windows, link(name="pythonXY"))] extern "C" { #[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
pub fn PyObject_Malloc(size: size_t) -> *mut c_void; 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_Calloc(nelem: size_t, elsize: size_t) -> *mut c_void;
pub fn PyObject_Realloc(ptr: *mut c_void, new_size: size_t) pub fn PyObject_Realloc(ptr: *mut c_void, new_size: size_t) -> *mut c_void;
-> *mut c_void;
pub fn PyObject_Free(ptr: *mut c_void) -> (); pub fn PyObject_Free(ptr: *mut c_void) -> ();
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
pub fn _Py_GetAllocatedBlocks() -> Py_ssize_t; pub fn _Py_GetAllocatedBlocks() -> Py_ssize_t;
pub fn PyObject_Init(arg1: *mut PyObject, arg2: *mut PyTypeObject) pub fn PyObject_Init(arg1: *mut PyObject, arg2: *mut PyTypeObject) -> *mut PyObject;
-> *mut PyObject;
pub fn PyObject_InitVar(arg1: *mut PyVarObject, arg2: *mut PyTypeObject, pub fn PyObject_InitVar(arg1: *mut PyVarObject, arg2: *mut PyTypeObject,
arg3: Py_ssize_t) -> *mut PyVarObject; arg3: Py_ssize_t) -> *mut PyVarObject;
pub fn _PyObject_New(arg1: *mut PyTypeObject) -> *mut PyObject; pub fn _PyObject_New(arg1: *mut PyTypeObject) -> *mut PyObject;
pub fn _PyObject_NewVar(arg1: *mut PyTypeObject, arg2: Py_ssize_t) pub fn _PyObject_NewVar(arg1: *mut PyTypeObject, arg2: Py_ssize_t) -> *mut PyVarObject;
-> *mut PyVarObject;
pub fn PyGC_Collect() -> Py_ssize_t; pub fn PyGC_Collect() -> Py_ssize_t;
} }
@ -28,12 +25,8 @@ use ffi3::object::*;
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
pub struct PyObjectArenaAllocator { pub struct PyObjectArenaAllocator {
pub ctx: *mut c_void, pub ctx: *mut c_void,
pub alloc: Option<extern "C" fn(ctx: *mut c_void, pub alloc: Option<extern "C" fn(ctx: *mut c_void, size: size_t) -> *mut c_void>,
size: size_t) pub free: Option<extern "C" fn(ctx: *mut c_void, ptr: *mut c_void, size: size_t) -> ()>,
-> *mut c_void>,
pub free: Option<extern "C" fn(ctx: *mut c_void,
ptr: *mut c_void,
size: size_t) -> ()>,
} }
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
@ -42,10 +35,8 @@ impl Default for PyObjectArenaAllocator {
} }
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
#[cfg_attr(windows, link(name="pythonXY"))] extern "C" { #[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
pub fn PyObject_GetArenaAllocator(allocator: *mut PyObjectArenaAllocator) pub fn PyObject_GetArenaAllocator(allocator: *mut PyObjectArenaAllocator) -> ();
-> (); pub fn PyObject_SetArenaAllocator(allocator: *mut PyObjectArenaAllocator) -> ();
pub fn PyObject_SetArenaAllocator(allocator: *mut PyObjectArenaAllocator)
-> ();
} }
/// Test if a type has a GC head /// 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" { #[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
pub fn _PyObject_GC_Resize(arg1: *mut PyVarObject, arg2: Py_ssize_t) pub fn _PyObject_GC_Resize(arg1: *mut PyVarObject, arg2: Py_ssize_t) -> *mut PyVarObject;
-> *mut PyVarObject;
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
pub fn _PyObject_GC_Malloc(size: size_t) -> *mut PyObject; pub fn _PyObject_GC_Malloc(size: size_t) -> *mut PyObject;
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
pub fn _PyObject_GC_Calloc(size: size_t) -> *mut PyObject; pub fn _PyObject_GC_Calloc(size: size_t) -> *mut PyObject;
pub fn _PyObject_GC_New(arg1: *mut PyTypeObject) -> *mut PyObject; pub fn _PyObject_GC_New(arg1: *mut PyTypeObject) -> *mut PyObject;
pub fn _PyObject_GC_NewVar(arg1: *mut PyTypeObject, arg2: Py_ssize_t) pub fn _PyObject_GC_NewVar(arg1: *mut PyTypeObject, arg2: Py_ssize_t) -> *mut PyVarObject;
-> *mut PyVarObject;
pub fn PyObject_GC_Track(arg1: *mut c_void) -> (); pub fn PyObject_GC_Track(arg1: *mut c_void) -> ();
pub fn PyObject_GC_UnTrack(arg1: *mut c_void) -> (); pub fn PyObject_GC_UnTrack(arg1: *mut c_void) -> ();
pub fn PyObject_GC_Del(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; let weaklistoffset = (*Py_TYPE(o)).tp_weaklistoffset as isize;
(o as *mut u8).offset(weaklistoffset) as *mut *mut PyObject (o as *mut u8).offset(weaklistoffset) as *mut *mut PyObject
} }

View File

@ -31,6 +31,9 @@ pub trait PyTypeInfo {
/// `Type` instance offset inside PyObject structure /// `Type` instance offset inside PyObject structure
const OFFSET: isize; const OFFSET: isize;
/// Type flags (ie PyType_GC, PyType_WeakRef)
const FLAGS: usize = 0;
/// PyTypeObject instance for this type /// PyTypeObject instance for this type
unsafe fn type_object() -> &'static mut ffi::PyTypeObject; 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 { impl<'a, T: ?Sized> PyTypeInfo for &'a T where T: PyTypeInfo {
type Type = T::Type; 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 DESCRIPTION: &'static str = T::DESCRIPTION;
const SIZE: usize = T::SIZE; const SIZE: usize = T::SIZE;
const OFFSET: isize = T::OFFSET; const OFFSET: isize = T::OFFSET;
const FLAGS: usize = T::FLAGS;
#[inline] #[inline]
default unsafe fn type_object() -> &'static mut ffi::PyTypeObject { 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 // set type flags
py_class_flags(type_object); py_class_flags::<T>(type_object);
// register type object // register type object
unsafe { unsafe {
@ -259,8 +269,10 @@ unsafe extern "C" fn tp_dealloc_callback<T>(obj: *mut ffi::PyObject)
} }
#[cfg(Py_3)] #[cfg(Py_3)]
fn py_class_flags(type_object: &mut ffi::PyTypeObject) { fn py_class_flags<T: PyTypeInfo>(type_object: &mut ffi::PyTypeObject) {
if type_object.tp_traverse != None || type_object.tp_clear != None { 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; type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HAVE_GC;
} else { } else {
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT; 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))] #[cfg(not(Py_3))]
fn py_class_flags(type_object: &mut ffi::PyTypeObject) { fn py_class_flags<T: PyTypeInfo>(type_object: &mut ffi::PyTypeObject) {
if type_object.tp_traverse != None || type_object.tp_clear != None { 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; type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_CHECKTYPES | ffi::Py_TPFLAGS_HAVE_GC;
} else { } else {
type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_CHECKTYPES; type_object.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_CHECKTYPES;

View File

@ -476,6 +476,18 @@ fn gc_integration() {
assert!(drop_called.load(Ordering::Relaxed)); 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] #[py::class]
pub struct Len { pub struct Len {
l: usize, l: usize,