From 7c906511578ae54de3284f680d33369dd2c5d632 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 6 Mar 2016 00:03:17 +0100 Subject: [PATCH] Make use of nightly const_fn + shared features optional. --- Cargo.toml | 3 + Makefile | 12 +- extensions/Makefile | 14 +- src/lib.rs | 7 +- src/objects/object.rs | 41 +++++- src/pythonrun.rs | 8 ++ src/rustobject/typebuilder.rs | 258 +++++++++++++++++++--------------- 7 files changed, 219 insertions(+), 124 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 28026cf6..a135e767 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,9 @@ version = "0.1.2" [features] default = ["python3-sys"] +# Enable additional features that require nightly rust +nightly = [] + # Optional features to support explicitly specifying python minor version. # If you don't care which minor version, just specify python3-sys as a # feature. diff --git a/Makefile b/Makefile index 581d46e1..d6bf5855 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,13 @@ ifndef PY PY := $(word 2, $(subst ., ,$(shell python --version 2>&1))) endif +ifndef NIGHTLY +ifeq ($(word 3, $(subst -, ,$(shell rustc --version 2>&1))),nightly) +NIGHTLY := 1 +else +NIGHTLY := 0 +endif +endif ifeq ($(PY),2) FEATURES := python27-sys @@ -14,8 +21,11 @@ export PEP384=1 FEATURES := $(FEATURES) pep-384 endif endif +ifeq ($(NIGHTLY),1) +FEATURES := $(FEATURES) nightly +endif -CARGO_FLAGS := --features $(FEATURES) --no-default-features +CARGO_FLAGS := --features "$(FEATURES)" --no-default-features default: test extensions diff --git a/extensions/Makefile b/extensions/Makefile index b707b05b..4178863d 100644 --- a/extensions/Makefile +++ b/extensions/Makefile @@ -3,11 +3,18 @@ rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst .PHONY: all clean +SHELL=/bin/bash -o pipefail + ifndef PY PY := $(word 2, $(subst ., ,$(shell python --version 2>&1))) endif - -SHELL=/bin/bash -o pipefail +ifndef NIGHTLY +ifeq ($(word 3, $(subst -, ,$(shell rustc --version 2>&1))),nightly) +NIGHTLY := 1 +else +NIGHTLY := 0 +endif +endif all: @@ -35,13 +42,14 @@ stamps/test-hello: hello.out @grep "Rust says: Hello Python!" hello.out >/dev/null @grep "Rust got 42" hello.out >/dev/null +ifeq ($(NIGHTLY),1) custom_type.out: custom_type.so python$(PY) -c "import custom_type; custom_type.MyType(42).a()" 2>&1 | tee $@ all: stamps/test-custom_type stamps/test-custom_type: custom_type.out @grep "a() was called with self=42" custom_type.out >/dev/null - +endif all: inheritance.so diff --git a/src/lib.rs b/src/lib.rs index 176705d1..59dcc56f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,9 +19,10 @@ #![feature(unsafe_no_drop_flag)] // crucial so that PyObject<'p> is binary compatible with *mut ffi::PyObject #![feature(filling_drop)] // necessary to avoid segfault with unsafe_no_drop_flag (#5016) #![feature(optin_builtin_traits)] // for opting out of Sync/Send (#13231) -#![feature(stmt_expr_attributes)] // easier python 2.x/3.x distinction (#15701) -#![feature(const_fn)] // for GILProtected::new (#24111) -#![feature(shared)] // for std::ptr::Shared (#27730) +#![cfg_attr(feature="nightly", feature( + const_fn, // for GILProtected::new (#24111) + shared, // for std::ptr::Shared (#27730) +))] #![allow(unused_imports)] // because some imports are only necessary with python 2.x or 3.x diff --git a/src/objects/object.rs b/src/objects/object.rs index 17f79644..6066adb5 100644 --- a/src/objects/object.rs +++ b/src/objects/object.rs @@ -47,7 +47,10 @@ use err::PyResult; pub struct PyObject { // PyObject owns one reference to the *PyObject // ptr is not null - ptr: ptr::Shared + #[cfg(feature="nightly")] + ptr: ptr::Shared, + #[cfg(not(feature="nightly"))] + ptr: *mut ffi::PyObject, } // PyObject is thread-safe, because all operations on it require a Python<'p> token. @@ -59,13 +62,37 @@ impl Drop for PyObject { #[inline] fn drop(&mut self) { // TODO: remove `if` when #[unsafe_no_drop_flag] disappears - if *self.ptr as usize != mem::POST_DROP_USIZE { + if unpack_shared(self.ptr) as usize != mem::POST_DROP_USIZE { let _gil_guard = Python::acquire_gil(); - unsafe { ffi::Py_DECREF(*self.ptr); } + unsafe { ffi::Py_DECREF(unpack_shared(self.ptr)); } } } } +#[inline] +#[cfg(feature="nightly")] +unsafe fn make_shared(ptr: *mut ffi::PyObject) -> ptr::Shared { + ptr::Shared::new(ptr) +} + +#[inline] +#[cfg(not(feature="nightly"))] +unsafe fn make_shared(ptr: *mut ffi::PyObject) -> *mut ffi::PyObject { + ptr +} + +#[inline] +#[cfg(feature="nightly")] +fn unpack_shared(ptr: ptr::Shared) -> *mut ffi::PyObject { + *ptr +} + +#[inline] +#[cfg(not(feature="nightly"))] +fn unpack_shared(ptr: *mut ffi::PyObject) -> *mut ffi::PyObject { + ptr +} + pyobject_to_pyobject!(PyObject); impl PythonObject for PyObject { @@ -114,9 +141,9 @@ impl PyObject { /// This moves ownership over the pointer into the PyObject. /// Undefined behavior if the pointer is NULL or invalid. #[inline] - pub unsafe fn from_owned_ptr(_py : Python, ptr : *mut ffi::PyObject) -> PyObject { + pub unsafe fn from_owned_ptr(_py: Python, ptr: *mut ffi::PyObject) -> PyObject { debug_assert!(!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0); - PyObject { ptr: ptr::Shared::new(ptr) } + PyObject { ptr: make_shared(ptr) } } /// Creates a PyObject instance for the given FFI pointer. @@ -126,7 +153,7 @@ impl PyObject { pub unsafe fn from_borrowed_ptr(_py : Python, ptr : *mut ffi::PyObject) -> PyObject { debug_assert!(!ptr.is_null() && ffi::Py_REFCNT(ptr) > 0); ffi::Py_INCREF(ptr); - PyObject { ptr: ptr::Shared::new(ptr) } + PyObject { ptr: make_shared(ptr) } } /// Creates a PyObject instance for the given FFI pointer. @@ -155,7 +182,7 @@ impl PyObject { /// Returns a borrowed pointer. #[inline] pub fn as_ptr(&self) -> *mut ffi::PyObject { - *self.ptr + unpack_shared(self.ptr) } /// Gets the underlying FFI pointer. diff --git a/src/pythonrun.rs b/src/pythonrun.rs index 80f8535a..7f3ffad3 100644 --- a/src/pythonrun.rs +++ b/src/pythonrun.rs @@ -153,10 +153,18 @@ unsafe impl Sync for GILProtected { } impl GILProtected { /// Creates a new instance of `GILProtected`. #[inline] + #[cfg(feature="nightly")] pub const fn new(data: T) -> GILProtected { GILProtected { data: data } } + /// Creates a new instance of `GILProtected`. + #[inline] + #[cfg(not(feature="nightly"))] + pub fn new(data: T) -> GILProtected { + GILProtected { data: data } + } + /// Returns a shared reference to the data stored in the `GILProtected`. /// /// Requires a `Python` instance as proof that the GIL is acquired. diff --git a/src/rustobject/typebuilder.rs b/src/rustobject/typebuilder.rs index e710185c..62a59895 100644 --- a/src/rustobject/typebuilder.rs +++ b/src/rustobject/typebuilder.rs @@ -97,40 +97,51 @@ impl <'p, T> PyRustTypeBuilder<'p, T> where T: 'static + Send { /// py: proof that the GIL is held by the current thread. /// name: name of the new type pub fn new(py: Python<'p>, name: &str) -> PyRustTypeBuilder<'p, T> { - #[cfg(feature="python27-sys")] unsafe { - let obj = (ffi::PyType_Type.tp_alloc.unwrap())(&mut ffi::PyType_Type, 0); - if obj.is_null() { - panic!("Out of memory") + #[cfg(feature="python27-sys")] + fn new_impl<'p, T>(py: Python<'p>, name: &str) -> PyRustTypeBuilder<'p, T> + where T: 'static + Send + { + unsafe { + let obj = (ffi::PyType_Type.tp_alloc.unwrap())(&mut ffi::PyType_Type, 0); + if obj.is_null() { + panic!("Out of memory") + } + debug_assert!(ffi::Py_REFCNT(obj) == 1); + let ht = obj as *mut ffi::PyHeapTypeObject; + // flags must be set first, before the GC traverses the object + (*ht).ht_type.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HEAPTYPE; + (*ht).ht_name = PyString::new(py, name.as_bytes()).steal_ptr(py); + (*ht).ht_type.tp_name = ffi::PyString_AS_STRING((*ht).ht_name); + (*ht).ht_type.tp_new = Some(disabled_tp_new_callback); + return PyRustTypeBuilder { + type_obj: PyType::unchecked_downcast_from(PyObject::from_owned_ptr(py, obj)), + doc_str: None, + target_module: None, + ht: ht, + can_change_base: true, + py: py, + phantom: marker::PhantomData + } } - debug_assert!(ffi::Py_REFCNT(obj) == 1); - let ht = obj as *mut ffi::PyHeapTypeObject; - // flags must be set first, before the GC traverses the object - (*ht).ht_type.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HEAPTYPE; - (*ht).ht_name = PyString::new(py, name.as_bytes()).steal_ptr(py); - (*ht).ht_type.tp_name = ffi::PyString_AS_STRING((*ht).ht_name); - (*ht).ht_type.tp_new = Some(disabled_tp_new_callback); - return PyRustTypeBuilder { - type_obj: PyType::unchecked_downcast_from(PyObject::from_owned_ptr(py, obj)), - doc_str: None, + } + #[cfg(feature="python3-sys")] + fn new_impl<'p, T>(py: Python<'p>, name: &str) -> PyRustTypeBuilder<'p, T> + where T: 'static + Send + { + PyRustTypeBuilder { + name: CString::new(name).unwrap(), + flags: ffi::Py_TPFLAGS_DEFAULT as libc::c_uint, + slots: Vec::new(), + tp_base: None, + members: Vec::new(), target_module: None, - ht: ht, + doc_str: None, can_change_base: true, py: py, phantom: marker::PhantomData } } - #[cfg(feature="python3-sys")] PyRustTypeBuilder { - name: CString::new(name).unwrap(), - flags: ffi::Py_TPFLAGS_DEFAULT as libc::c_uint, - slots: Vec::new(), - tp_base: None, - members: Vec::new(), - target_module: None, - doc_str: None, - can_change_base: true, - py: py, - phantom: marker::PhantomData - } + new_impl(py, name) } /// Sets the base class that this type is inheriting from. @@ -141,37 +152,46 @@ impl <'p, T> PyRustTypeBuilder<'p, T> where T: 'static + Send { // Ensure we can't change the base after any callbacks are registered. assert!(self.can_change_base, "base() must be called before any members are added to the type"); - #[cfg(feature="python27-sys")] { + #[cfg(feature="python27-sys")] + fn base_impl<'p, T, T2, B2>(slf: PyRustTypeBuilder<'p, T>, base_type: &PyRustType) + -> PyRustTypeBuilder<'p, T, PyRustObject> + where T: 'static + Send, T2: 'static + Send, B2: PythonBaseObject + { unsafe { - ffi::Py_XDECREF((*self.ht).ht_type.tp_base as *mut ffi::PyObject); - (*self.ht).ht_type.tp_base = base_type.as_type_ptr(); + ffi::Py_XDECREF((*slf.ht).ht_type.tp_base as *mut ffi::PyObject); + (*slf.ht).ht_type.tp_base = base_type.as_type_ptr(); ffi::Py_INCREF(base_type.as_object().as_ptr()); } return PyRustTypeBuilder { - type_obj: self.type_obj, - doc_str: self.doc_str, - target_module: self.target_module, - ht: self.ht, + type_obj: slf.type_obj, + doc_str: slf.doc_str, + target_module: slf.target_module, + ht: slf.ht, can_change_base: false, - py: self.py, + py: slf.py, phantom: marker::PhantomData } } - #[cfg(feature="python3-sys")] { + #[cfg(feature="python3-sys")] + fn base_impl<'p, T, T2, B2>(slf: PyRustTypeBuilder<'p, T>, base_type: &PyRustType) + -> PyRustTypeBuilder<'p, T, PyRustObject> + where T: 'static + Send, T2: 'static + Send, B2: PythonBaseObject + { let base_type_obj: &PyType = base_type; return PyRustTypeBuilder { - name: self.name, - flags: self.flags, - slots: self.slots, - tp_base: Some(base_type_obj.clone_ref(self.py)), + name: slf.name, + flags: slf.flags, + slots: slf.slots, + tp_base: Some(base_type_obj.clone_ref(slf.py)), members: Vec::new(), - target_module: self.target_module, - doc_str: self.doc_str, + target_module: slf.target_module, + doc_str: slf.doc_str, can_change_base: false, - py: self.py, + py: slf.py, phantom: marker::PhantomData } } + base_impl(self, base_type) } } @@ -195,99 +215,61 @@ impl <'p, T, B> PyRustTypeBuilder<'p, T, B> where T: 'static + Send, B: PythonBa /// Adds a new member to the type. pub fn add(mut self, name: &str, val: M) -> Self - where M: TypeMember> + 'static { + where M: TypeMember> + 'static + { self.can_change_base = false; - #[cfg(feature="python27-sys")] { - self.dict().set_item(self.py, name, val.to_descriptor(self.py, &self.type_obj, name)).unwrap(); - } - #[cfg(feature="python3-sys")] { - self.members.push((name.to_owned(), Box::new(val))); - } + self.add_impl(name, val); self } + #[cfg(feature="python27-sys")] + fn add_impl(&mut self, name: &str, val: M) + where M: TypeMember> + 'static + { + self.dict().set_item(self.py, name, val.to_descriptor(self.py, &self.type_obj, name)).unwrap(); + } + + #[cfg(feature="python3-sys")] + fn add_impl(&mut self, name: &str, val: M) + where M: TypeMember> + 'static + { + self.members.push((name.to_owned(), Box::new(val))); + } + /// Sets the constructor (__new__ method) /// /// As `new` argument, use either the `py_fn!()` or the `py_class_method!()` macro. pub fn set_new(mut self, new: N) -> Self where N: TypeConstructor { - let tp_new = new.tp_new(); + self.set_new_impl(new.tp_new()); + self + } - #[cfg(feature="python27-sys")] unsafe { + #[cfg(feature="python27-sys")] + fn set_new_impl(&mut self, tp_new: ffi::newfunc) { + unsafe { (*self.ht).ht_type.tp_new = Some(tp_new); } - - #[cfg(feature="python3-sys")] + } + #[cfg(feature="python3-sys")] + fn set_new_impl(&mut self, tp_new: ffi::newfunc) { self.slots.push(ffi::PyType_Slot { slot: ffi::Py_tp_new, pfunc: tp_new as *mut libc::c_void }); - self } /// Finalize construction of the new type. pub fn finish(mut self) -> PyResult> { let py = self.py; - let type_obj; - #[cfg(feature="python27-sys")] { - unsafe { - (*self.ht).ht_type.tp_basicsize = PyRustObject::::size() as ffi::Py_ssize_t; - (*self.ht).ht_type.tp_dealloc = Some(tp_dealloc_callback::); - if let Some(s) = self.doc_str { - (*self.ht).ht_type.tp_doc = copy_str_to_py_malloc_heap(&s); - } - try!(err::error_on_minusone(py, ffi::PyType_Ready(self.type_obj.as_type_ptr()))) - } - type_obj = self.type_obj; - } - #[cfg(feature="python3-sys")] { - // push some more slots - self.slots.push(ffi::PyType_Slot { - slot: ffi::Py_tp_dealloc, - pfunc: tp_dealloc_callback:: as ffi::destructor as *mut libc::c_void - }); - if let Some(s) = self.doc_str { - self.slots.push(ffi::PyType_Slot { - slot: ffi::Py_tp_doc, - pfunc: copy_str_to_py_malloc_heap(&s) as *mut libc::c_void - }); - } - if let Some(base_type) = self.tp_base { - self.slots.push(ffi::PyType_Slot { - slot: ffi::Py_tp_base, - pfunc: base_type.as_type_ptr() as *mut libc::c_void - }); - } - - type_obj = try!(unsafe { create_type_from_slots( - py, &self.name, PyRustObject::::size(), - self.flags, &mut self.slots) }); - for (name, member) in self.members { - let descr = member.to_descriptor(py, &type_obj, &name); - try!(type_obj.as_object().setattr(py, name, descr)); - } - } + let type_obj = try!(self.build_type()); - - if let Some(m) = self.target_module { + if let Some(ref m) = self.target_module { // Set module name for new type if let Ok(mod_name) = m.name(py) { try!(type_obj.as_object().setattr(py, "__module__", mod_name)); } // Register the new type in the target module - #[cfg(feature="python27-sys")] { - let name = unsafe { PyObject::from_borrowed_ptr(py, (*self.ht).ht_name) }; - try!(m.dict(py).set_item(py, name, type_obj.as_object())); - } - #[cfg(feature="python3-sys")] { - unsafe { - try!(err::error_on_minusone(py, - ffi::PyDict_SetItemString( - m.dict(py).as_object().as_ptr(), - self.name.as_ptr(), - type_obj.as_object().as_ptr()) - )); - } - } + try!(m.dict(py).set_item(py, self.name(), &type_obj)); } Ok(PyRustType { type_obj: type_obj, @@ -295,6 +277,62 @@ impl <'p, T, B> PyRustTypeBuilder<'p, T, B> where T: 'static + Send, B: PythonBa }) } + #[cfg(feature="python27-sys")] + fn name(&self) -> PyObject { + unsafe { + PyObject::from_borrowed_ptr(self.py, (*self.ht).ht_name) + } + } + + #[cfg(feature="python3-sys")] + fn name(&self) -> PyString { + self.name.to_str().unwrap().to_py_object(self.py) + } + + #[cfg(feature="python27-sys")] + fn build_type(&mut self) -> PyResult { + let py = self.py; + unsafe { + (*self.ht).ht_type.tp_basicsize = PyRustObject::::size() as ffi::Py_ssize_t; + (*self.ht).ht_type.tp_dealloc = Some(tp_dealloc_callback::); + if let Some(ref s) = self.doc_str { + (*self.ht).ht_type.tp_doc = copy_str_to_py_malloc_heap(s); + } + try!(err::error_on_minusone(py, ffi::PyType_Ready(self.type_obj.as_type_ptr()))) + } + Ok(self.type_obj.clone_ref(py)) + } + #[cfg(feature="python3-sys")] + fn build_type(&mut self) -> PyResult { + let py = self.py; + // push some more slots + self.slots.push(ffi::PyType_Slot { + slot: ffi::Py_tp_dealloc, + pfunc: tp_dealloc_callback:: as ffi::destructor as *mut libc::c_void + }); + if let Some(ref s) = self.doc_str { + self.slots.push(ffi::PyType_Slot { + slot: ffi::Py_tp_doc, + pfunc: copy_str_to_py_malloc_heap(s) as *mut libc::c_void + }); + } + if let Some(ref base_type) = self.tp_base { + self.slots.push(ffi::PyType_Slot { + slot: ffi::Py_tp_base, + pfunc: base_type.as_type_ptr() as *mut libc::c_void + }); + } + + let type_obj = try!(unsafe { create_type_from_slots( + py, &self.name, PyRustObject::::size(), + self.flags, &mut self.slots) }); + for &(ref name, ref member) in &self.members { + let descr = member.to_descriptor(py, &type_obj, &name); + try!(type_obj.as_object().setattr(py, name, descr)); + } + Ok(type_obj) + } + } fn copy_str_to_py_malloc_heap(s: &CStr) -> *mut libc::c_char {