drop RefFromPyObject; allow mut refs #106

This commit is contained in:
Nikolay Kim 2018-01-19 09:04:42 -08:00
parent 35e91a5cd1
commit 324a6b2697
11 changed files with 141 additions and 163 deletions

View File

@ -1,7 +1,13 @@
Changes
-------
0.2.4 (2018-01-xx)
0.2.4 (2018-01-19)
* Allow to get mutable ref from PyObject #106
* Drop `RefFromPyObject` trait
* Add Python::register_any() method
* Fix impl `FromPyObject` for `Py<T>`

View File

@ -1,6 +1,6 @@
[package]
name = "pyo3"
version = "0.2.3"
version = "0.2.4"
description = "Bindings to Python interpreter"
authors = ["PyO3 Project and Contributors"]
readme = "README.md"
@ -19,11 +19,11 @@ appveyor = { repository = "PyO3/pyo3" }
codecov = { repository = "PyO3/pyo3", branch = "master", service = "github" }
[dependencies]
log = "0.3"
log = "0.4"
libc = "0.2"
spin = "0.4.6"
num-traits = "0.1"
pyo3cls = { path = "pyo3cls", version = "0.2" }
pyo3cls = { path = "pyo3cls", version = "^0.2.1" }
[build-dependencies]
regex = "0.2"

View File

@ -1,6 +1,6 @@
[package]
name = "pyo3cls"
version = "0.2.0"
version = "0.2.1"
description = "Proc macros for PyO3 package"
authors = ["PyO3 Project and Contributors <https://github.com/PyO3"]
homepage = "https://github.com/pyo3/pyo3"

View File

@ -462,119 +462,52 @@ fn impl_arg_param(arg: &FnArg, spec: &FnSpec, body: &Tokens, idx: usize) -> Toke
syn::Ident::from("None").to_tokens(&mut default);
}
if arg.reference {
quote! {
quote! {
match
match _iter.next().unwrap().as_ref() {
Some(obj) => {
if obj.is_none() {
let #arg_name = #default;
#body
} else {
match _pyo3::RefFromPyObject::with_extracted(
obj, |#name| {
let #arg_name = Some(#name);
#body
})
{
Ok(v) => v,
Err(e) => Err(e)
}
}
},
None => {
let #arg_name = #default;
#body
}
}
}
} else {
quote! {
match
match _iter.next().unwrap().as_ref() {
Some(_obj) => {
if _obj.is_none() {
Ok(#default)
} else {
match _obj.extract() {
Ok(_obj) => Ok(Some(_obj)),
Err(e) => Err(e)
}
}
},
None => Ok(#default) }
{
Ok(#arg_name) => #body,
Err(e) => Err(e)
}
}
}
} else if let Some(default) = spec.default_value(name) {
if arg.reference {
quote! {
match _iter.next().unwrap().as_ref() {
Some(_obj) => {
if _obj.is_none() {
let #arg_name = #default;
#body
} else {
match _pyo3::RefFromPyObject::with_extracted(
_obj, |#arg_name| {
#body
})
{
Ok(v) => v,
Err(e) => Err(e)
}
}
},
None => {
let #arg_name = #default;
#body
}
}
}
} else {
quote! {
match match _iter.next().unwrap().as_ref() {
Some(_obj) => {
if _obj.is_none() {
Ok(#default)
} else {
match _obj.extract() {
Ok(_obj) => Ok(_obj),
Err(e) => Err(e),
Ok(_obj) => Ok(Some(_obj)),
Err(e) => Err(e)
}
}
},
None => Ok(#default)
} {
Ok(#arg_name) => #body,
Err(e) => Err(e)
}
None => Ok(#default) }
{
Ok(#arg_name) => #body,
Err(e) => Err(e)
}
}
} else if let Some(default) = spec.default_value(name) {
quote! {
match match _iter.next().unwrap().as_ref() {
Some(_obj) => {
if _obj.is_none() {
Ok(#default)
} else {
match _obj.extract() {
Ok(_obj) => Ok(_obj),
Err(e) => Err(e),
}
}
},
None => Ok(#default)
} {
Ok(#arg_name) => #body,
Err(e) => Err(e)
}
}
}
else {
if arg.reference {
quote! {
match _pyo3::RefFromPyObject::with_extracted(
_iter.next().unwrap().as_ref().unwrap(), |#arg_name| {
#body
})
{
Ok(v) => v,
Err(e) => Err(e)
}
}
} else {
quote! {
match _iter.next().unwrap().as_ref().unwrap().extract()
{
Ok(#arg_name) => {
#body
}
Err(e) => Err(e)
quote! {
match _iter.next().unwrap().as_ref().unwrap().extract() {
Ok(#arg_name) => {
#body
}
Err(e) => Err(e)
}
}
}

View File

@ -48,7 +48,6 @@ impl<T> ToBorrowedObject for T where T: ToPyObject {
pub trait IntoPyObject {
/// Converts self into a Python object. (Consumes self)
#[inline]
fn into_object(self, py: Python) -> PyObject;
}
@ -86,25 +85,6 @@ pub trait FromPyObject<'source> : Sized {
fn extract(ob: &'source PyObjectRef) -> PyResult<Self>;
}
pub trait RefFromPyObject {
fn with_extracted<F, R>(ob: &PyObjectRef, f: F) -> PyResult<R>
where F: FnOnce(&Self) -> R;
}
impl <T: ?Sized> RefFromPyObject for T
where for<'a> &'a T: FromPyObject<'a> + Sized
{
#[inline]
fn with_extracted<F, R>(obj: &PyObjectRef, f: F) -> PyResult<R>
where F: FnOnce(&Self) -> R
{
match FromPyObject::extract(obj) {
Ok(val) => Ok(f(val)),
Err(e) => Err(e)
}
}
}
/// Identity conversion: allows using existing `PyObject` instances where
/// `T: ToPyObject` is expected.
impl <'a, T: ?Sized> ToPyObject for &'a T where T: ToPyObject {
@ -115,7 +95,6 @@ impl <'a, T: ?Sized> ToPyObject for &'a T where T: ToPyObject {
}
}
/// `Option::Some<T>` is converted like `T`.
/// `Option::None` is converted to Python `None`.
impl <T> ToPyObject for Option<T> where T: ToPyObject {
@ -127,6 +106,7 @@ impl <T> ToPyObject for Option<T> where T: ToPyObject {
}
}
}
impl<T> IntoPyObject for Option<T> where T: IntoPyObject {
fn into_object(self, py: Python) -> PyObject {
@ -158,8 +138,7 @@ impl<'a, T> IntoPyObject for &'a T where T: ToPyPointer
}
}
impl<'a, T> IntoPyObject for &'a mut T where T: ToPyPointer
{
impl<'a, T> IntoPyObject for &'a mut T where T: ToPyPointer {
#[inline]
fn into_object<'p>(self, py: Python) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(py, self.as_ptr()) }
@ -171,16 +150,24 @@ impl<'a, T> FromPyObject<'a> for &'a T
where T: PyTypeInfo
{
#[inline]
default fn extract(ob: &'a PyObjectRef) -> PyResult<&'a T>
{
default fn extract(ob: &'a PyObjectRef) -> PyResult<&'a T> {
Ok(T::try_from(ob)?)
}
}
impl<'source, T> FromPyObject<'source> for Option<T> where T: FromPyObject<'source>
/// Extract mutable reference to instance from `PyObject`
impl<'a, T> FromPyObject<'a> for &'a mut T
where T: PyTypeInfo
{
fn extract(obj: &'source PyObjectRef) -> PyResult<Self>
{
#[inline]
default fn extract(ob: &'a PyObjectRef) -> PyResult<&'a mut T> {
Ok(T::try_from_mut(ob)?)
}
}
impl<'a, T> FromPyObject<'a> for Option<T> where T: FromPyObject<'a>
{
fn extract(obj: &'a PyObjectRef) -> PyResult<Self> {
if obj.as_ptr() == unsafe { ffi::Py_None() } {
Ok(None)
} else {
@ -192,7 +179,6 @@ impl<'source, T> FromPyObject<'source> for Option<T> where T: FromPyObject<'sour
}
}
/// Trait implemented by Python object types that allow a checked downcast.
/// This trait is similar to `std::convert::TryInto`
pub trait PyTryInto<T>: Sized {
@ -232,8 +218,8 @@ pub trait PyTryFrom: Sized {
}
// TryFrom implies TryInto
impl<U> PyTryInto<U> for PyObjectRef where U: PyTryFrom
{
impl<U> PyTryInto<U> for PyObjectRef where U: PyTryFrom {
type Error = U::Error;
fn try_into(&self) -> Result<&U, U::Error> {
@ -251,9 +237,8 @@ impl<U> PyTryInto<U> for PyObjectRef where U: PyTryFrom
}
impl<T> PyTryFrom for T
where T: PyTypeInfo
{
impl<T> PyTryFrom for T where T: PyTypeInfo {
type Error = PyDowncastError;
fn try_from(value: &PyObjectRef) -> Result<&T, Self::Error> {

View File

@ -164,7 +164,7 @@ pub use typeob::{PyTypeInfo, PyRawObject, PyObjectAlloc};
pub use python::{Python, ToPyPointer, IntoPyPointer, IntoPyDictPointer};
pub use pythonrun::{GILGuard, GILPool, prepare_freethreaded_python, prepare_pyo3_library};
pub use instance::{PyToken, PyObjectWithToken, AsPyRef, Py, PyNativeType};
pub use conversion::{FromPyObject, RefFromPyObject, PyTryFrom, PyTryInto,
pub use conversion::{FromPyObject, PyTryFrom, PyTryInto,
ToPyObject, ToBorrowedObject, IntoPyObject, IntoPyTuple};
pub mod class;
pub use class::*;

View File

@ -5,8 +5,8 @@ use err::PyResult;
use python::Python;
use object::PyObject;
use objects::{PyObjectRef, PyString};
use objectprotocol::ObjectProtocol;
use conversion::{ToPyObject, IntoPyObject, RefFromPyObject, PyTryFrom};
use instance::PyObjectWithToken;
use conversion::{ToPyObject, IntoPyObject, PyTryFrom};
/// Converts Rust `str` to Python object.
/// See `PyString::new` for details on the conversion.
@ -55,25 +55,29 @@ impl<'a> IntoPyObject for &'a String {
/// Allows extracting strings from Python objects.
/// Accepts Python `str` and `unicode` objects.
impl<'source> ::FromPyObject<'source> for Cow<'source, str>
{
fn extract(ob: &'source PyObjectRef) -> PyResult<Self>
{
impl<'source> ::FromPyObject<'source> for Cow<'source, str> {
fn extract(ob: &'source PyObjectRef) -> PyResult<Self> {
PyString::try_from(ob)?.to_string()
}
}
/// Allows extracting strings from Python objects.
/// Accepts Python `str` and `unicode` objects.
impl<'a> ::FromPyObject<'a> for &'a str {
fn extract(ob: &'a PyObjectRef) -> PyResult<Self> {
let s: Cow<'a, str> = ::FromPyObject::extract(ob)?;
match s {
Cow::Borrowed(r) => Ok(r),
Cow::Owned(r) => {
let r = ob.py().register_any(r);
Ok(r.as_str())
}
}
}
}
/// Allows extracting strings from Python objects.
/// Accepts Python `str` and `unicode` objects.
pyobject_extract!(obj to String => {
<PyString as PyTryFrom>::try_from(obj)?.to_string().map(Cow::into_owned)
});
impl RefFromPyObject for str {
fn with_extracted<F, R>(obj: &PyObjectRef, f: F) -> PyResult<R>
where F: FnOnce(&str) -> R
{
let s = try!(obj.extract::<Cow<str>>());
Ok(f(&s))
}
}

View File

@ -21,5 +21,5 @@ pub use err::{PyErr, PyErrValue, PyResult, PyDowncastError, PyErrArguments};
pub use pythonrun::GILGuard;
pub use typeob::PyRawObject;
pub use instance::{PyToken, PyObjectWithToken, AsPyRef, Py, PyNativeType};
pub use conversion::{FromPyObject, RefFromPyObject, PyTryFrom, PyTryInto,
pub use conversion::{FromPyObject, PyTryFrom, PyTryInto,
ToPyObject, ToBorrowedObject, IntoPyObject, IntoPyTuple};

View File

@ -391,6 +391,13 @@ impl<'p> Python<'p> {
}
}
#[doc(hidden)]
/// Pass value owneship to `Python` object and get reference back.
/// Value get cleaned up on the GIL release.
pub fn register_any<T: 'static>(self, ob: T) -> &'p T {
unsafe { pythonrun::register_any(ob) }
}
/// Release PyObject reference.
#[inline]
pub fn release<T>(self, ob: T) where T: IntoPyPointer {

View File

@ -1,8 +1,5 @@
// Copyright (c) 2017-present PyO3 Project and Contributors
//
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
use std::{sync, rc, marker, mem};
use std::{any, sync, rc, marker, mem};
use spin;
use ffi;
@ -113,16 +110,18 @@ struct ReleasePool {
owned: Vec<*mut ffi::PyObject>,
borrowed: Vec<*mut ffi::PyObject>,
pointers: *mut Vec<*mut ffi::PyObject>,
obj: Vec<Box<any::Any>>,
p: spin::Mutex<*mut Vec<*mut ffi::PyObject>>,
}
impl ReleasePool {
fn new() -> ReleasePool {
ReleasePool {
owned: Vec::with_capacity(250),
borrowed: Vec::with_capacity(250),
pointers: Box::into_raw(Box::new(Vec::with_capacity(250))),
p: spin::Mutex::new(Box::into_raw(Box::new(Vec::with_capacity(250)))),
owned: Vec::with_capacity(256),
borrowed: Vec::with_capacity(256),
pointers: Box::into_raw(Box::new(Vec::with_capacity(256))),
obj: Vec::with_capacity(8),
p: spin::Mutex::new(Box::into_raw(Box::new(Vec::with_capacity(256)))),
}
}
@ -165,6 +164,8 @@ impl ReleasePool {
if pointers {
self.release_pointers();
}
self.obj.clear();
}
}
@ -206,6 +207,14 @@ impl Drop for GILPool {
}
}
pub unsafe fn register_any<'p, T: 'static>(obj: T) -> &'p T
{
let pool: &'static mut ReleasePool = &mut *POOL;
pool.obj.push(Box::new(obj));
pool.obj.last().unwrap().as_ref().downcast_ref::<T>().unwrap()
}
pub unsafe fn register_pointer(obj: *mut ffi::PyObject)
{
let pool: &'static mut ReleasePool = &mut *POOL;

View File

@ -1411,3 +1411,37 @@ fn inheritance_with_new_methods_with_drop() {
assert!(drop_called1.load(Ordering::Relaxed));
assert!(drop_called2.load(Ordering::Relaxed));
}
#[py::class]
struct MutRefArg {
n: i32,
token: PyToken,
}
#[py::methods]
impl MutRefArg {
fn get(&self) -> PyResult<i32> {
Ok(self.n)
}
fn set_other(&self, other: &mut MutRefArg) -> PyResult<()> {
other.n = 100;
Ok(())
}
}
#[test]
fn mut_ref_arg() {
let gil = Python::acquire_gil();
let py = gil.python();
let inst1 = py.init(|t| MutRefArg{token: t, n: 0}).unwrap();
let inst2 = py.init(|t| MutRefArg{token: t, n: 0}).unwrap();
let d = PyDict::new(py);
d.set_item("inst1", &inst1).unwrap();
d.set_item("inst2", &inst2).unwrap();
py.run("inst1.set_other(inst2)", None, Some(d)).unwrap();
assert_eq!(inst2.as_ref(py).n, 100);
}