py.init(..) return reference

This commit is contained in:
Nikolay Kim 2017-06-23 15:03:52 -07:00
parent f0ece6dfde
commit 8238a141ae
4 changed files with 173 additions and 83 deletions

View File

@ -60,13 +60,23 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
self.#token.py()
}
}
impl _pyo3::ToPyObject for #cls
{
impl _pyo3::ToPyObject for #cls {
#[inline]
fn to_object<'p>(&self, py: _pyo3::Python<'p>) -> _pyo3::PyObject {
unsafe { _pyo3::PyObject::from_borrowed_ptr(py, self.as_ptr()) }
}
#[inline]
fn with_borrowed_ptr<F, R>(&self, _py: _pyo3::Python, f: F) -> R
where F: FnOnce(*mut ffi::PyObject) -> R
{
f(self.as_ptr())
}
}
impl<'a> _pyo3::ToPyObject for &'a mut #cls {
#[inline]
fn to_object<'p>(&self, py: _pyo3::Python<'p>) -> _pyo3::PyObject {
unsafe { _pyo3::PyObject::from_borrowed_ptr(py, self.as_ptr()) }
}
#[inline]
fn with_borrowed_ptr<F, R>(&self, _py: _pyo3::Python, f: F) -> R
where F: FnOnce(*mut ffi::PyObject) -> R
@ -81,11 +91,24 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
unsafe { _pyo3::PyObject::from_borrowed_ptr(py, self.as_ptr()) }
}
}
impl<'a> _pyo3::IntoPyObject for &'a mut #cls
{
#[inline]
fn into_object<'p>(self, py: _pyo3::Python) -> _pyo3::PyObject {
unsafe { _pyo3::PyObject::from_borrowed_ptr(py, self.as_ptr()) }
}
}
impl std::convert::AsRef<PyInstance> for #cls {
fn as_ref(&self) -> &_pyo3::PyInstance {
unsafe{std::mem::transmute(self.as_ptr())}
}
}
impl<'a> std::convert::From<&'a mut #cls> for &'a #cls
{
fn from(ob: &'a mut #cls) -> Self {
unsafe{std::mem::transmute(ob)}
}
}
impl _pyo3::ToPyPointer for #cls {
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
@ -95,7 +118,6 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
}
}
}
impl std::fmt::Debug for #cls {
fn fmt(&self, f : &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
use pyo3::ObjectProtocol;

View File

@ -10,7 +10,7 @@ use pointer::PyObject;
use objects::PyInstance;
use objectprotocol::ObjectProtocol;
use conversion::{ToPyObject, IntoPyObject, FromPyObject};
use python::{Python, IntoPyPointer, ToPyPointer, PyDowncastInto};
use python::{Python, IntoPyPointer, ToPyPointer, PyDowncastInto, PyDowncastFrom};
use typeob::{PyTypeInfo, PyObjectAlloc};
@ -161,8 +161,37 @@ impl<T> Py<T> {
impl<T> Py<T> where T: PyTypeInfo,
{
/// Create new python object and move T instance under python management
pub fn new<F>(py: Python, f: F) -> PyResult<Py<T>>
/// Create new instance of `T` and move under python management.
/// Returns references to `T`
pub fn new<'p, F>(py: Python<'p>, f: F) -> PyResult<&'p T>
where F: FnOnce(::PyToken) -> T,
T: PyObjectAlloc<T> + PyDowncastFrom
{
let ob = f(PyToken(PhantomData));
unsafe {
let ob = try!(<T as PyObjectAlloc<T>>::alloc(py, ob));
Ok(py.cast_from_ptr(ob))
}
}
/// Create new instance of `T` and move under python management.
/// Returns mutable references to `T`
pub fn new_mut<'p, F>(py: Python<'p>, f: F) -> PyResult<&'p mut T>
where F: FnOnce(::PyToken) -> T,
T: PyObjectAlloc<T> + PyDowncastFrom
{
let ob = f(PyToken(PhantomData));
unsafe {
let ob = try!(<T as PyObjectAlloc<T>>::alloc(py, ob));
Ok(py.mut_cast_from_ptr(ob))
}
}
/// Create new instance of T and move under python management
/// Returns `Py<T>`.
pub fn new_ptr<F>(py: Python, f: F) -> PyResult<Py<T>>
where F: FnOnce(::PyToken) -> T,
T: PyObjectAlloc<T>
{

View File

@ -206,6 +206,19 @@ impl<'p> Python<'p> {
PyModule::import(self, name)
}
/// Check whether `obj` is an instance of type `T` like Python `isinstance` function
pub fn is_instance<T: PyTypeObject, V: ToPyPointer>(self, obj: &V) -> PyResult<bool> {
T::type_object(self).is_instance(obj)
}
/// Check whether type `T` is subclass of type `U` like Python `issubclass` function
pub fn is_subclass<T, U>(self) -> PyResult<bool>
where T: PyTypeObject,
U: PyTypeObject
{
T::type_object(self).is_subclass::<U>()
}
/// Gets the Python builtin value `None`.
#[allow(non_snake_case)] // the Python keyword starts with uppercase
#[inline]
@ -219,78 +232,99 @@ impl<'p> Python<'p> {
pub fn NotImplemented(self) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_NotImplemented()) }
}
}
/// Create new python object and move T instance under python management
impl<'p> Python<'p> {
/// Create new instance of T and move under python management.
/// Created object get registered in release pool. Returns references to `T`
#[inline]
pub fn init<T, F>(self, f: F) -> PyResult<Py<T>>
pub fn init<T, F>(self, f: F) -> PyResult<&'p T>
where F: FnOnce(PyToken) -> T,
T: PyTypeInfo + PyObjectAlloc<T>
T: PyTypeInfo + PyObjectAlloc<T> + PyDowncastFrom
{
Py::new(self, f)
}
/// Check whether `obj` is an instance of type `T` like Python `isinstance` function
pub fn is_instance<T: PyTypeObject, V: ToPyPointer>(self, obj: &V) -> PyResult<bool> {
T::type_object(self).is_instance(obj)
/// Create new instance of T and move under python management.
/// Created object get registered in release pool. Returns mutable references to `T`
#[inline]
pub fn init_mut<T, F>(self, f: F) -> PyResult<&'p mut T>
where F: FnOnce(PyToken) -> T,
T: PyTypeInfo + PyObjectAlloc<T> + PyDowncastFrom
{
Py::new_mut(self, f)
}
/// Check whether type `T` is subclass of type `U` like Python `issubclass` function
pub fn is_subclass<T, U>(self) -> PyResult<bool>
where T: PyTypeObject,
U: PyTypeObject
/// Create new instance of T and move under python management.
/// Returns `Py<T>`.
#[inline]
pub fn init_ptr<T, F>(self, f: F) -> PyResult<Py<T>>
where F: FnOnce(PyToken) -> T,
T: PyTypeInfo + PyObjectAlloc<T>
{
T::type_object(self).is_subclass::<U>()
Py::new_ptr(self, f)
}
}
impl<'p> Python<'p> {
/// Register object in release pool, and try to downcast to specific Python object.
pub fn checked_cast_as<D>(self, obj: PyObject) -> Result<&'p D, PyDowncastError<'p>>
where D: PyDowncastFrom
pub fn checked_cast_as<T>(self, obj: PyObject) -> Result<&'p T, PyDowncastError<'p>>
where T: PyDowncastFrom
{
unsafe {
let p = pythonrun::register_owned(self, obj);
<D as PyDowncastFrom>::downcast_from(p)
<T as PyDowncastFrom>::downcast_from(p)
}
}
/// Register object in release pool, and do unchecked downcast to specific Python object.
pub unsafe fn cast_as<D>(self, obj: PyObject) -> &'p D
where D: PyDowncastFrom
pub unsafe fn cast_as<T>(self, obj: PyObject) -> &'p T
where T: PyDowncastFrom
{
let p = pythonrun::register_owned(self, obj);
<D as PyDowncastFrom>::unchecked_downcast_from(p)
<T as PyDowncastFrom>::unchecked_downcast_from(p)
}
/// Register `ffi::PyObject` pointer in release pool,
/// and do unchecked downcast to specific Python object.
pub unsafe fn cast_from_ptr<D>(self, ptr: *mut ffi::PyObject) -> &'p D
where D: PyDowncastFrom
pub unsafe fn cast_from_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p T
where T: PyDowncastFrom
{
let obj = PyObject::from_owned_ptr_or_panic(self, ptr);
let p = pythonrun::register_owned(self, obj);
<D as PyDowncastFrom>::unchecked_downcast_from(p)
<T as PyDowncastFrom>::unchecked_downcast_from(p)
}
/// Register `ffi::PyObject` pointer in release pool,
/// Do unchecked downcast to specific Python object. Returns mutable reference.
pub unsafe fn mut_cast_from_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p mut T
where T: PyDowncastFrom
{
let obj = PyObject::from_owned_ptr_or_panic(self, ptr);
let p = pythonrun::register_owned(self, obj);
<T as PyDowncastFrom>::unchecked_mut_downcast_from(p)
}
/// Register owned `ffi::PyObject` pointer in release pool.
/// Returns `Err(PyErr)` if the pointer is `null`.
/// do unchecked downcast to specific Python object.
pub fn cast_from_ptr_or_err<D>(self, ptr: *mut ffi::PyObject) -> PyResult<&'p D>
where D: PyDowncastFrom
pub fn cast_from_ptr_or_err<T>(self, ptr: *mut ffi::PyObject) -> PyResult<&'p T>
where T: PyDowncastFrom
{
let obj = PyObject::from_owned_ptr_or_err(self, ptr)?;
unsafe {
let p = pythonrun::register_owned(self, obj);
Ok(<D as PyDowncastFrom>::unchecked_downcast_from(p))
Ok(<T as PyDowncastFrom>::unchecked_downcast_from(p))
}
}
/// Register owned `ffi::PyObject` pointer in release pool.
/// Returns `None` if the pointer is `null`.
/// do unchecked downcast to specific Python object.
pub fn cast_from_ptr_or_opt<D>(self, ptr: *mut ffi::PyObject) -> Option<&'p D>
where D: PyDowncastFrom
pub fn cast_from_ptr_or_opt<T>(self, ptr: *mut ffi::PyObject) -> Option<&'p T>
where T: PyDowncastFrom
{
if ptr.is_null() {
None
@ -298,7 +332,7 @@ impl<'p> Python<'p> {
unsafe {
let obj = PyObject::from_owned_ptr(self, ptr);
let p = pythonrun::register_owned(self, obj);
Some(<D as PyDowncastFrom>::unchecked_downcast_from(p))
Some(<T as PyDowncastFrom>::unchecked_downcast_from(p))
}
}
}
@ -306,51 +340,51 @@ impl<'p> Python<'p> {
/// Register borrowed `ffi::PyObject` pointer in release pool.
/// Panics if the pointer is `null`.
/// do unchecked downcast to specific Python object.
pub unsafe fn cast_from_borrowed_ptr<D>(self, ptr: *mut ffi::PyObject) -> &'p D
where D: PyDowncastFrom
pub unsafe fn cast_from_borrowed_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p T
where T: PyDowncastFrom
{
let obj = PyObject::from_borrowed_ptr(self, ptr);
let p = pythonrun::register_borrowed(self, obj);
<D as PyDowncastFrom>::unchecked_downcast_from(p)
<T as PyDowncastFrom>::unchecked_downcast_from(p)
}
/// Register borrowed `ffi::PyObject` pointer in release pool.
/// Returns `Err(PyErr)` if the pointer is `null`.
/// do unchecked downcast to specific Python object.
pub unsafe fn cast_from_borrowed_ptr_or_err<D>(self, ptr: *mut ffi::PyObject)
-> PyResult<&'p D>
where D: PyDowncastFrom
pub unsafe fn cast_from_borrowed_ptr_or_err<T>(self, ptr: *mut ffi::PyObject)
-> PyResult<&'p T>
where T: PyDowncastFrom
{
let obj = PyObject::from_borrowed_ptr_or_err(self, ptr)?;
let p = pythonrun::register_borrowed(self, obj);
Ok(<D as PyDowncastFrom>::unchecked_downcast_from(p))
Ok(<T as PyDowncastFrom>::unchecked_downcast_from(p))
}
/// Register borrowed `ffi::PyObject` pointer in release pool.
/// Returns `None` if the pointer is `null`.
/// do unchecked downcast to specific Python object.
pub unsafe fn cast_from_borrowed_ptr_or_opt<D>(self, ptr: *mut ffi::PyObject)
-> Option<&'p D>
where D: PyDowncastFrom
pub unsafe fn cast_from_borrowed_ptr_or_opt<T>(self, ptr: *mut ffi::PyObject)
-> Option<&'p T>
where T: PyDowncastFrom
{
if ptr.is_null() {
None
} else {
let obj = PyObject::from_borrowed_ptr(self, ptr);
let p = pythonrun::register_borrowed(self, obj);
Some(<D as PyDowncastFrom>::unchecked_downcast_from(p))
Some(<T as PyDowncastFrom>::unchecked_downcast_from(p))
}
}
/// Register borrowed `ffi::PyObject` pointer in release pool.
/// Panics if the pointer is `null`.
/// do unchecked downcast to specific Python object, returns mutable reference.
pub unsafe fn mut_cast_from_borrowed_ptr<D>(self, ptr: *mut ffi::PyObject) -> &'p mut D
where D: PyDowncastFrom
pub unsafe fn mut_cast_from_borrowed_ptr<T>(self, ptr: *mut ffi::PyObject) -> &'p mut T
where T: PyDowncastFrom
{
let obj = PyObject::from_borrowed_ptr(self, ptr);
let p = pythonrun::register_borrowed(self, obj);
<D as PyDowncastFrom>::unchecked_mut_downcast_from(p)
<T as PyDowncastFrom>::unchecked_mut_downcast_from(p)
}
pub fn track_object(self, obj: PyObject) -> &'p PyInstance

View File

@ -106,7 +106,7 @@ struct EmptyClassWithNew {
impl EmptyClassWithNew {
#[__new__]
fn __new__(cls: &PyType) -> PyResult<Py<EmptyClassWithNew>> {
Py::new(cls.token(), |t| EmptyClassWithNew{token: t})
Ok(Py::new(cls.token(), |t| EmptyClassWithNew{token: t})?.into())
}
}
@ -127,8 +127,8 @@ struct NewWithOneArg {
#[py::methods]
impl NewWithOneArg {
#[new]
fn __new__(cls: &PyType, arg: i32) -> PyResult<Py<NewWithOneArg>> {
Py::new(cls.token(), |t| NewWithOneArg{_data: arg, token: t})
fn __new__(cls: &PyType, arg: i32) -> PyResult<&mut NewWithOneArg> {
cls.token().init_mut(|t| NewWithOneArg{_data: arg, token: t})
}
}
@ -155,7 +155,9 @@ impl NewWithTwoArgs {
#[new]
fn __new__(cls: &PyType, arg1: i32, arg2: i32) -> PyResult<Py<NewWithTwoArgs>>
{
Py::new(cls.token(), |t| NewWithTwoArgs{_data1: arg1, _data2: arg2, token: t})
Py::new_ptr(
cls.token(),
|t| NewWithTwoArgs{_data1: arg1, _data2: arg2, token: t})
}
}
@ -178,12 +180,12 @@ fn class_with_freelist() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst = Py::new(py, |t| ClassWithFreelist{token: t}).unwrap();
let inst2 = Py::new(py, |t| ClassWithFreelist{token: t}).unwrap();
let inst = Py::new_ptr(py, |t| ClassWithFreelist{token: t}).unwrap();
let inst2 = Py::new_ptr(py, |t| ClassWithFreelist{token: t}).unwrap();
let ptr = inst.as_ptr();
drop(inst);
let inst3 = Py::new(py, |t| ClassWithFreelist{token: t}).unwrap();
let inst3 = Py::new_ptr(py, |t| ClassWithFreelist{token: t}).unwrap();
assert_eq!(ptr, inst3.as_ptr());
}
@ -210,7 +212,7 @@ fn data_is_dropped() {
let drop_called1 = Arc::new(AtomicBool::new(false));
let drop_called2 = Arc::new(AtomicBool::new(false));
let inst = Py::new(py, |t| DataIsDropped{
let inst = py.init_ptr(|t| DataIsDropped{
member1: TestDropCall { drop_called: drop_called1.clone() },
member2: TestDropCall { drop_called: drop_called2.clone() },
token: t
@ -243,7 +245,7 @@ fn instance_method() {
let py = gil.python();
let obj = Py::new(py, |t| InstanceMethod{member: 42, token: t}).unwrap();
assert!(obj.as_ref(py).method().unwrap() == 42);
assert!(obj.method().unwrap() == 42);
let d = PyDict::new(py);
d.set_item("obj", obj).unwrap();
py.run("assert obj.method() == 42", None, Some(d)).unwrap();
@ -269,7 +271,7 @@ fn instance_method_with_args() {
let py = gil.python();
let obj = Py::new(py, |t| InstanceMethodWithArgs{member: 7, token: t}).unwrap();
assert!(obj.as_ref(py).method(6).unwrap() == 42);
assert!(obj.method(6).unwrap() == 42);
let d = PyDict::new(py);
d.set_item("obj", obj).unwrap();
py.run("assert obj.method(3) == 21", None, Some(d)).unwrap();
@ -284,7 +286,7 @@ struct ClassMethod {token: PyToken}
impl ClassMethod {
#[new]
fn __new__(cls: &PyType) -> PyResult<Py<ClassMethod>> {
Py::new(cls.token(), |t| ClassMethod{token: t})
Py::new_ptr(cls.token(), |t| ClassMethod{token: t})
}
#[classmethod]
@ -334,8 +336,8 @@ struct StaticMethod {
#[py::methods]
impl StaticMethod {
#[new]
fn __new__(cls: &PyType) -> PyResult<Py<StaticMethod>> {
Py::new(cls.token(), |t| StaticMethod{token: t})
fn __new__(cls: &PyType) -> PyResult<&StaticMethod> {
Ok(cls.token().init_mut(|t| StaticMethod{token: t})?.into())
}
#[staticmethod]
@ -400,18 +402,22 @@ impl PyGCProtocol for GCIntegration {
#[test]
fn gc_integration() {
let drop_called = Arc::new(AtomicBool::new(false));
{
let gil = Python::acquire_gil();
let py = gil.python();
let inst = Py::new(py, |t| GCIntegration{
self_ref: RefCell::new(py.None().into()),
dropped: TestDropCall { drop_called: drop_called.clone() },
token: t}).unwrap();
*inst.self_ref.borrow_mut() = inst.into();
drop(inst);
}
let gil = Python::acquire_gil();
let py = gil.python();
let drop_called = Arc::new(AtomicBool::new(false));
let inst = Py::new(py, |t| GCIntegration{
self_ref: RefCell::new(py.None().into()),
dropped: TestDropCall { drop_called: drop_called.clone() },
token: t}).unwrap();
*inst.as_mut(py).self_ref.borrow_mut() = inst.clone_ref(py).into();
drop(inst);
py.run("import gc; gc.collect()", None, None).unwrap();
assert!(drop_called.load(Ordering::Relaxed));
}
@ -638,8 +644,8 @@ fn setitem() {
let c = py.init(|t| SetItem{key: 0, val: 0, token: t}).unwrap();
py_run!(py, c, "c[1] = 2");
assert_eq!(c.as_ref(py).key, 1);
assert_eq!(c.as_ref(py).val, 2);
assert_eq!(c.key, 1);
assert_eq!(c.val, 2);
py_expect_exception!(py, c, "del c[1]", NotImplementedError);
}
@ -664,7 +670,7 @@ fn delitem() {
let c = py.init(|t| DelItem{key:0, token:t}).unwrap();
py_run!(py, c, "del c[1]");
assert_eq!(c.as_ref(py).key, 1);
assert_eq!(c.key, 1);
py_expect_exception!(py, c, "c[1] = 2", NotImplementedError);
}
@ -694,9 +700,9 @@ fn setdelitem() {
let c = py.init(|t| SetDelItem{val: None, token: t}).unwrap();
py_run!(py, c, "c[1] = 2");
assert_eq!(c.as_ref(py).val, Some(2));
assert_eq!(c.val, Some(2));
py_run!(py, c, "del c[1]");
assert_eq!(c.as_ref(py).val, None);
assert_eq!(c.val, None);
}
#[py::class]
@ -1064,19 +1070,18 @@ fn context_manager() {
let gil = Python::acquire_gil();
let py = gil.python();
let c = py.init(|t| ContextManager{exit_called: false, token: t}).unwrap();
let mut c = py.init_mut(|t| ContextManager{exit_called: false, token: t}).unwrap();
py_run!(py, c, "with c as x:\n assert x == 42");
assert!(c.as_ref(py).exit_called);
assert!(c.exit_called);
c.as_mut(py).exit_called = false;
c.exit_called = false;
py_run!(py, c, "with c as x:\n raise ValueError");
assert!(c.as_ref(py).exit_called);
assert!(c.exit_called);
c.as_mut(py).exit_called = false;
c.exit_called = false;
py_expect_exception!(
py, c, "with c as x:\n raise NotImplementedError",
NotImplementedError);
assert!(c.as_ref(py).exit_called);
py, c, "with c as x:\n raise NotImplementedError", NotImplementedError);
assert!(c.exit_called);
}
#[py::class]