Make use of nightly const_fn + shared features optional.

This commit is contained in:
Daniel Grunwald 2016-03-06 00:03:17 +01:00
parent 53353d374b
commit 7c90651157
7 changed files with 219 additions and 124 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -47,7 +47,10 @@ use err::PyResult;
pub struct PyObject {
// PyObject owns one reference to the *PyObject
// ptr is not null
ptr: ptr::Shared<ffi::PyObject>
#[cfg(feature="nightly")]
ptr: ptr::Shared<ffi::PyObject>,
#[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<ffi::PyObject> {
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<ffi::PyObject>) -> *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.

View File

@ -153,10 +153,18 @@ unsafe impl<T: Send> Sync for GILProtected<T> { }
impl <T> GILProtected<T> {
/// Creates a new instance of `GILProtected`.
#[inline]
#[cfg(feature="nightly")]
pub const fn new(data: T) -> GILProtected<T> {
GILProtected { data: data }
}
/// Creates a new instance of `GILProtected`.
#[inline]
#[cfg(not(feature="nightly"))]
pub fn new(data: T) -> GILProtected<T> {
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.

View File

@ -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<T2, B2>)
-> PyRustTypeBuilder<'p, T, PyRustObject<T2, B2>>
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<T2, B2>)
-> PyRustTypeBuilder<'p, T, PyRustObject<T2, B2>>
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<M>(mut self, name: &str, val: M) -> Self
where M: TypeMember<PyRustObject<T, B>> + 'static {
where M: TypeMember<PyRustObject<T, B>> + '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<M>(&mut self, name: &str, val: M)
where M: TypeMember<PyRustObject<T, B>> + '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<M>(&mut self, name: &str, val: M)
where M: TypeMember<PyRustObject<T, B>> + '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<N>(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<PyRustType<T, B>> {
let py = self.py;
let type_obj;
#[cfg(feature="python27-sys")] {
unsafe {
(*self.ht).ht_type.tp_basicsize = PyRustObject::<T, B>::size() as ffi::Py_ssize_t;
(*self.ht).ht_type.tp_dealloc = Some(tp_dealloc_callback::<T, B>);
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::<T, B> 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
});
}
let type_obj = try!(self.build_type());
type_obj = try!(unsafe { create_type_from_slots(
py, &self.name, PyRustObject::<T, B>::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));
}
}
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<PyType> {
let py = self.py;
unsafe {
(*self.ht).ht_type.tp_basicsize = PyRustObject::<T, B>::size() as ffi::Py_ssize_t;
(*self.ht).ht_type.tp_dealloc = Some(tp_dealloc_callback::<T, B>);
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<PyType> {
let py = self.py;
// push some more slots
self.slots.push(ffi::PyType_Slot {
slot: ffi::Py_tp_dealloc,
pfunc: tp_dealloc_callback::<T, B> 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::<T, B>::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 {