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")]
|
#[cfg(feature="python27-sys")]
|
||||||
pub use self::iterator::PyIterator;
|
pub use self::iterator::PyIterator;
|
||||||
pub use self::boolobject::PyBool;
|
pub use self::boolobject::PyBool;
|
||||||
pub use self::tuple::PyTuple;
|
pub use self::tuple::{PyTuple, NoArgs};
|
||||||
pub use self::dict::PyDict;
|
pub use self::dict::PyDict;
|
||||||
pub use self::list::PyList;
|
pub use self::list::PyList;
|
||||||
#[cfg(feature="python27-sys")]
|
#[cfg(feature="python27-sys")]
|
||||||
|
|
|
@ -123,7 +123,8 @@ impl <'p> PyModule<'p> {
|
||||||
/// sets `new_type.__module__` to this module's name.
|
/// 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.
|
/// The new type will be added to this module when `finish()` is called on the builder.
|
||||||
#[cfg(feature="python27-sys")]
|
#[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)
|
::rustobject::new_typebuilder_for_module(self, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
// DEALINGS IN THE SOFTWARE.
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject, ToPythonPointer};
|
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 ffi;
|
||||||
use libc::c_char;
|
use libc::c_char;
|
||||||
use std;
|
use std;
|
||||||
|
@ -49,6 +51,17 @@ impl <'p> PyType<'p> {
|
||||||
pub fn is_instance(&self, obj : &PyObject<'p>) -> bool {
|
pub fn is_instance(&self, obj : &PyObject<'p>) -> bool {
|
||||||
unsafe { ffi::PyObject_TypeCheck(obj.as_ptr(), self.as_type_ptr()) != 0 }
|
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> {
|
impl <'p> PartialEq for PyType<'p> {
|
||||||
|
|
|
@ -27,6 +27,10 @@ use err::{self, PyResult};
|
||||||
pub trait PythonBaseObject<'p> : PythonObject<'p> {
|
pub trait PythonBaseObject<'p> : PythonObject<'p> {
|
||||||
/// Gets the size of the object, in bytes.
|
/// Gets the size of the object, in bytes.
|
||||||
fn size() -> usize;
|
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> {
|
impl <'p> PythonBaseObject<'p> for PyObject<'p> {
|
||||||
|
@ -34,6 +38,10 @@ impl <'p> PythonBaseObject<'p> for PyObject<'p> {
|
||||||
fn size() -> usize {
|
fn size() -> usize {
|
||||||
mem::size_of::<ffi::PyObject>()
|
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,
|
/// 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
|
(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]
|
#[inline]
|
||||||
pub fn base(&self) -> &B {
|
pub fn base(&self) -> &B {
|
||||||
unsafe { B::unchecked_downcast_borrow_from(&self.obj) }
|
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]
|
#[inline]
|
||||||
fn size() -> usize {
|
fn size() -> usize {
|
||||||
PyRustObject::<T, B>::offset() + mem::size_of::<T>()
|
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]
|
#[inline]
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
PyRustObject {
|
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]
|
#[inline]
|
||||||
fn as_ptr(&self) -> *mut ffi::PyObject {
|
fn as_ptr(&self) -> *mut ffi::PyObject {
|
||||||
self.obj.as_ptr()
|
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]
|
#[inline]
|
||||||
fn as_object(&self) -> &PyObject<'p> {
|
fn as_object(&self) -> &PyObject<'p> {
|
||||||
&self.obj
|
&self.obj
|
||||||
|
@ -224,26 +238,35 @@ impl <'p, T: 'p> PythonObject<'p> for PyRustType<'p, T> {
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[must_use]
|
#[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>,
|
type_obj: PyType<'p>,
|
||||||
target_module: Option<PyModule<'p>>,
|
target_module: Option<PyModule<'p>>,
|
||||||
ht: *mut ffi::PyHeapTypeObject,
|
ht: *mut ffi::PyHeapTypeObject,
|
||||||
phantom: marker::PhantomData<&'p (B, T)>
|
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);
|
let b = PyRustTypeBuilder::new(m.python(), name);
|
||||||
if let Ok(mod_name) = m.name() {
|
if let Ok(mod_name) = m.name() {
|
||||||
b.dict().set_item("__module__", mod_name).ok();
|
b.dict().set_item("__module__", mod_name).ok();
|
||||||
}
|
}
|
||||||
PyRustTypeBuilder { target_module: Some(m.clone()), .. b }
|
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.
|
/// Create a new type builder.
|
||||||
pub fn new(py: Python<'p>, name: &str) -> PyRustTypeBuilder<'p, T> {
|
pub fn new(py: Python<'p>, name: &str) -> PyRustTypeBuilder<'p, T> {
|
||||||
unsafe {
|
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() {
|
if obj.is_null() {
|
||||||
panic!("Out of memory")
|
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_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_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_flags = ffi::Py_TPFLAGS_DEFAULT | ffi::Py_TPFLAGS_HEAPTYPE;
|
||||||
|
(*ht).ht_type.tp_new = Some(disabled_new);
|
||||||
PyRustTypeBuilder {
|
PyRustTypeBuilder {
|
||||||
type_obj: PyType::unchecked_downcast_from(PyObject::from_owned_ptr(py, obj)),
|
type_obj: PyType::unchecked_downcast_from(PyObject::from_owned_ptr(py, obj)),
|
||||||
target_module: None,
|
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>)
|
pub fn base<T2, B2>(self, base_type: &PyRustType<'p, T2, B2>)
|
||||||
-> PyRustTypeBuilder<'p, T, PyRustObject<'p, T2, B2>>
|
-> PyRustTypeBuilder<'p, T, PyRustObject<'p, T2, B2>>
|
||||||
where B2: PythonBaseObject<'p>
|
where T2: 'p + Send, B2: PythonBaseObject<'p>
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::Py_XDECREF((*self.ht).ht_type.tp_base as *mut ffi::PyObject);
|
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>> {
|
pub fn finish(self) -> PyResult<'p, PyRustType<'p, T, B>> {
|
||||||
let py = self.type_obj.python();
|
let py = self.type_obj.python();
|
||||||
unsafe {
|
unsafe {
|
||||||
(*self.ht).ht_type.tp_basicsize = PyRustObject::<T, B>::size() as ffi::Py_ssize_t;
|
(*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())))
|
try!(err::error_on_minusone(py, ffi::PyType_Ready(self.type_obj.as_type_ptr())))
|
||||||
}
|
}
|
||||||
if let Some(m) = self.target_module {
|
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