PyRustObject: implement tp_dealloc by dropping the rust object
This commit is contained in:
parent
fa32b52392
commit
7c9b96ee90
|
@ -30,7 +30,7 @@ pub use self::string::PyUnicode as PyString;
|
|||
#[cfg(feature="python27-sys")]
|
||||
pub use self::iterator::PyIterator;
|
||||
pub use self::boolobject::PyBool;
|
||||
pub use self::tuple::PyTuple;
|
||||
pub use self::tuple::{PyTuple, NoArgs};
|
||||
pub use self::dict::PyDict;
|
||||
pub use self::list::PyList;
|
||||
#[cfg(feature="python27-sys")]
|
||||
|
|
|
@ -123,7 +123,8 @@ impl <'p> PyModule<'p> {
|
|||
/// sets `new_type.__module__` to this module's name.
|
||||
/// The new type will be added to this module when `finish()` is called on the builder.
|
||||
#[cfg(feature="python27-sys")]
|
||||
pub fn add_type<T>(&self, name: &str) -> ::rustobject::PyRustTypeBuilder<'p, T> {
|
||||
pub fn add_type<T>(&self, name: &str) -> ::rustobject::PyRustTypeBuilder<'p, T>
|
||||
where T: 'p + Send {
|
||||
::rustobject::new_typebuilder_for_module(self, name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject, ToPythonPointer};
|
||||
use objects::PyObject;
|
||||
use conversion::ToPyObject;
|
||||
use objects::{PyObject, PyTuple, PyDict};
|
||||
use err::{PyResult, result_from_owned_ptr};
|
||||
use ffi;
|
||||
use libc::c_char;
|
||||
use std;
|
||||
|
@ -49,6 +51,17 @@ impl <'p> PyType<'p> {
|
|||
pub fn is_instance(&self, obj : &PyObject<'p>) -> bool {
|
||||
unsafe { ffi::PyObject_TypeCheck(obj.as_ptr(), self.as_type_ptr()) != 0 }
|
||||
}
|
||||
|
||||
/// Calls the type object, thus creating a new instance.
|
||||
/// This is equivalent to the python expression: 'self(*args, **kwargs)'
|
||||
#[inline]
|
||||
pub fn call<A: ?Sized>(&self, args: &A, kwargs: Option<&PyDict<'p>>) -> PyResult<'p, PyObject<'p>>
|
||||
where A: ToPyObject<'p, ObjectType=PyTuple<'p>> {
|
||||
let py = self.python();
|
||||
args.with_borrowed_ptr(py, |args| unsafe {
|
||||
result_from_owned_ptr(py, ffi::PyObject_Call(self.as_ptr(), args, kwargs.as_ptr()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p> PartialEq for PyType<'p> {
|
||||
|
|
|
@ -27,6 +27,10 @@ use err::{self, PyResult};
|
|||
pub trait PythonBaseObject<'p> : PythonObject<'p> {
|
||||
/// Gets the size of the object, in bytes.
|
||||
fn size() -> usize;
|
||||
|
||||
/// Calls the rust destructor for the object and frees the memory.
|
||||
/// This function is the tp_dealloc implementation.
|
||||
unsafe extern "C" fn dealloc(ptr: *mut ffi::PyObject);
|
||||
}
|
||||
|
||||
impl <'p> PythonBaseObject<'p> for PyObject<'p> {
|
||||
|
@ -34,6 +38,10 @@ impl <'p> PythonBaseObject<'p> for PyObject<'p> {
|
|||
fn size() -> usize {
|
||||
mem::size_of::<ffi::PyObject>()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn dealloc(ptr: *mut ffi::PyObject) {
|
||||
((*ffi::Py_TYPE(ptr)).tp_free.unwrap())(ptr as *mut libc::c_void);
|
||||
}
|
||||
}
|
||||
|
||||
/// A python object that contains a rust value of type T,
|
||||
|
@ -56,7 +64,7 @@ impl <'p, T, B> PyRustObject<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
|||
(B::size() + align - 1) / align * align
|
||||
}
|
||||
|
||||
/// Gets a reference to this object,
|
||||
/// Gets a reference to this object, but of the base class type.
|
||||
#[inline]
|
||||
pub fn base(&self) -> &B {
|
||||
unsafe { B::unchecked_downcast_borrow_from(&self.obj) }
|
||||
|
@ -73,14 +81,20 @@ impl <'p, T, B> PyRustObject<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
|||
}
|
||||
}
|
||||
|
||||
impl <'p, T, B> PythonBaseObject<'p> for PyRustObject<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
||||
impl <'p, T, B> PythonBaseObject<'p> for PyRustObject<'p, T, B> where T: 'p + Send, B: PythonBaseObject<'p> {
|
||||
#[inline]
|
||||
fn size() -> usize {
|
||||
PyRustObject::<T, B>::offset() + mem::size_of::<T>()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn dealloc(obj: *mut ffi::PyObject) {
|
||||
let offset = PyRustObject::<T, B>::offset() as isize;
|
||||
ptr::read_and_drop((obj as *mut u8).offset(offset) as *mut T);
|
||||
B::dealloc(obj)
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p, T, B> Clone for PyRustObject<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
||||
impl <'p, T, B> Clone for PyRustObject<'p, T, B> where T: 'p + Send, B: PythonBaseObject<'p> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
PyRustObject {
|
||||
|
@ -91,7 +105,7 @@ impl <'p, T, B> Clone for PyRustObject<'p, T, B> where T: 'p, B: PythonBaseObjec
|
|||
}
|
||||
}
|
||||
|
||||
impl <'p, T, B> ToPythonPointer for PyRustObject<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
||||
impl <'p, T, B> ToPythonPointer for PyRustObject<'p, T, B> where T: 'p + Send, B: PythonBaseObject<'p> {
|
||||
#[inline]
|
||||
fn as_ptr(&self) -> *mut ffi::PyObject {
|
||||
self.obj.as_ptr()
|
||||
|
@ -103,7 +117,7 @@ impl <'p, T, B> ToPythonPointer for PyRustObject<'p, T, B> where T: 'p, B: Pytho
|
|||
}
|
||||
}
|
||||
|
||||
impl <'p, T, B> PythonObject<'p> for PyRustObject<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
||||
impl <'p, T, B> PythonObject<'p> for PyRustObject<'p, T, B> where T: 'p + Send, B: PythonBaseObject<'p> {
|
||||
#[inline]
|
||||
fn as_object(&self) -> &PyObject<'p> {
|
||||
&self.obj
|
||||
|
@ -224,26 +238,35 @@ impl <'p, T: 'p> PythonObject<'p> for PyRustType<'p, T> {
|
|||
|
||||
#[repr(C)]
|
||||
#[must_use]
|
||||
pub struct PyRustTypeBuilder<'p, T, B = PyObject<'p>> where T: 'p, B: PythonBaseObject<'p> {
|
||||
pub struct PyRustTypeBuilder<'p, T, B = PyObject<'p>> where T: 'p + Send, B: PythonBaseObject<'p> {
|
||||
type_obj: PyType<'p>,
|
||||
target_module: Option<PyModule<'p>>,
|
||||
ht: *mut ffi::PyHeapTypeObject,
|
||||
phantom: marker::PhantomData<&'p (B, T)>
|
||||
}
|
||||
|
||||
pub fn new_typebuilder_for_module<'p, T>(m: &PyModule<'p>, name: &str) -> PyRustTypeBuilder<'p, T> {
|
||||
pub fn new_typebuilder_for_module<'p, T>(m: &PyModule<'p>, name: &str) -> PyRustTypeBuilder<'p, T>
|
||||
where T: 'p + Send {
|
||||
let b = PyRustTypeBuilder::new(m.python(), name);
|
||||
if let Ok(mod_name) = m.name() {
|
||||
b.dict().set_item("__module__", mod_name).ok();
|
||||
}
|
||||
PyRustTypeBuilder { target_module: Some(m.clone()), .. b }
|
||||
}
|
||||
|
||||
impl <'p, T> PyRustTypeBuilder<'p, T> where T: 'p {
|
||||
|
||||
unsafe extern "C" fn disabled_new
|
||||
(subtype: *mut ffi::PyTypeObject, args: *mut ffi::PyObject, kwds: *mut ffi::PyObject)
|
||||
-> *mut ffi::PyObject {
|
||||
ffi::PyErr_SetString(ffi::PyExc_TypeError,
|
||||
b"Cannot initialize rust object from python.\0" as *const u8 as *const libc::c_char);
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
impl <'p, T> PyRustTypeBuilder<'p, T> where T: 'p + Send {
|
||||
/// Create a new type builder.
|
||||
pub fn new(py: Python<'p>, name: &str) -> PyRustTypeBuilder<'p, T> {
|
||||
unsafe {
|
||||
let obj = ffi::PyType_GenericAlloc(&mut ffi::PyType_Type, 0);
|
||||
let obj = (ffi::PyType_Type.tp_alloc.unwrap())(&mut ffi::PyType_Type, 0);
|
||||
if obj.is_null() {
|
||||
panic!("Out of memory")
|
||||
}
|
||||
|
@ -251,6 +274,7 @@ impl <'p, T> PyRustTypeBuilder<'p, T> where T: 'p {
|
|||
(*ht).ht_name = PyString::new(py, name.as_bytes()).steal_ptr();
|
||||
(*ht).ht_type.tp_name = ffi::PyString_AS_STRING((*ht).ht_name);
|
||||
(*ht).ht_type.tp_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HEAPTYPE;
|
||||
(*ht).ht_type.tp_new = Some(disabled_new);
|
||||
PyRustTypeBuilder {
|
||||
type_obj: PyType::unchecked_downcast_from(PyObject::from_owned_ptr(py, obj)),
|
||||
target_module: None,
|
||||
|
@ -262,7 +286,7 @@ impl <'p, T> PyRustTypeBuilder<'p, T> where T: 'p {
|
|||
|
||||
pub fn base<T2, B2>(self, base_type: &PyRustType<'p, T2, B2>)
|
||||
-> PyRustTypeBuilder<'p, T, PyRustObject<'p, T2, B2>>
|
||||
where B2: PythonBaseObject<'p>
|
||||
where T2: 'p + Send, B2: PythonBaseObject<'p>
|
||||
{
|
||||
unsafe {
|
||||
ffi::Py_XDECREF((*self.ht).ht_type.tp_base as *mut ffi::PyObject);
|
||||
|
@ -288,11 +312,12 @@ impl <'p, T> PyRustTypeBuilder<'p, T> where T: 'p {
|
|||
}
|
||||
}
|
||||
|
||||
impl <'p, T, B> PyRustTypeBuilder<'p, T, B> where T: 'p, B: PythonBaseObject<'p> {
|
||||
impl <'p, T, B> PyRustTypeBuilder<'p, T, B> where T: 'p + Send, B: PythonBaseObject<'p> {
|
||||
pub fn finish(self) -> PyResult<'p, PyRustType<'p, T, B>> {
|
||||
let py = self.type_obj.python();
|
||||
unsafe {
|
||||
(*self.ht).ht_type.tp_basicsize = PyRustObject::<T, B>::size() as ffi::Py_ssize_t;
|
||||
(*self.ht).ht_type.tp_dealloc = Some(PyRustObject::<T, B>::dealloc);
|
||||
try!(err::error_on_minusone(py, ffi::PyType_Ready(self.type_obj.as_type_ptr())))
|
||||
}
|
||||
if let Some(m) = self.target_module {
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
extern crate cpython;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use cpython::*;
|
||||
|
||||
#[test]
|
||||
fn rustobject_calls_drop() {
|
||||
|
||||
struct MyObj {
|
||||
drop_called: Arc<AtomicBool>
|
||||
}
|
||||
impl Drop for MyObj {
|
||||
fn drop(&mut self) {
|
||||
self.drop_called.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let t = PyRustTypeBuilder::<MyObj>::new(py, "TypeWithDrop").finish().unwrap();
|
||||
|
||||
let drop_called = Arc::new(AtomicBool::new(false));
|
||||
let inst = t.create_instance(MyObj { drop_called: drop_called.clone() });
|
||||
assert!(drop_called.load(Ordering::Relaxed) == false);
|
||||
drop(inst);
|
||||
assert!(drop_called.load(Ordering::Relaxed) == true);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn rustobject_no_init_from_python() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let t = PyRustTypeBuilder::<i32>::new(py, "TypeWithDrop").finish().unwrap();
|
||||
assert!(t.call(&NoArgs, None).is_err());
|
||||
}
|
||||
|
Loading…
Reference in New Issue