Remove BorrowAsPyObject, and simplify PyPtr by moving the 'p bound into the PythonObject definition.

This commit is contained in:
Daniel Grunwald 2015-01-04 02:54:54 +01:00
parent d247b138c5
commit 5569ae3bbc
3 changed files with 103 additions and 63 deletions

View file

@ -5,49 +5,76 @@ use err;
use {Python, PyObject, PyResult, PythonObject, PyErr};
use pyptr::{PyPtr, PythonPointer};
/// FromPyObject is implemented by various types that can be extracted from a python object.
pub trait FromPyObject<'p, 'a> {
fn from_py_object(s: &'a PyObject<'p>) -> PyResult<'p, Self>;
}
/// ToPyObject is implemented for types that can be converted into a python object.
pub trait ToPyObject<'p> for Sized? {
type PointerType : 'p + PythonPointer + std::ops::Deref;
/// The goal is to allow methods that take a python object to take anything that
/// can be converted into a python object.
/// For example, compare calling the following method signatures:
/// fn m1(o: &PyObject) {}
/// fn m2<O>(o: &O) where O : ToPyObject {}
///
/// let o: &PyObject = ...;
/// m1(o);
/// m2(o);
///
/// let p: PyPtr<PyObject> = ...;
/// m1(*p)
/// m2(p)
///
/// let i: i32 = ...;
/// m1(*try!(i.to_py_object(py)))
/// m2(i)
pub trait ToPyObject<'p, 's> for Sized? {
// The returned pointer type is flexible:
// it can be either &PyObject or PyPtr<PyObject>, depending on whether
// the conversion is allocating a new object.
// This lets us avoid a useless IncRef/DecRef pair
type PointerType : PythonPointer = PyPtr<'p, PyObject<'p>>;
fn to_py_object(&self, py: Python<'p>) -> PyResult<'p, Self::PointerType>;
//fn to_py_object(&self, py: Python<'p>) -> PyResult<'p, PyPtr<'p, PyObject<'p>>>;
fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, Self::PointerType>;
// Note that there are 6 cases with methods taking ToPyObject:
// 1) input is &PyObject, FFI function steals pointer
// -> ToPyObject is no-op, PythonPointer::steal_ptr() calls Py_IncRef()
// 2) input is &PyObject, FFI function borrows pointer
// -> ToPyObject is no-op, PythonPointer::as_ptr() also is no-op
// 3) input is &PyPtr<PyObject>, FFI function steals pointer
// -> ToPyObject borrows content, PythonPointer::steal_ptr() calls Py_IncRef()
// Not optimal, we'd prefer to take the input PyPtr by value.
// 4) input is &PyPtr<PyObject>, FFI function borrows pointer
// -> ToPyObject borrows content, PythonPointer::as_ptr() is no-op
// 5) input is &str, int, etc., FFI function steals pointer
// -> ToPyObject allocates new object, PythonPointer::steal_ptr() grabs existing owned pointer
// 6) input is &str, int, etc., FFI function borrows pointer
// -> ToPyObject allocates new object, PythonPointer::as_ptr() is no-op,
// PyPtr::drop calls Py_DecRef()
// So the only non-optimal case (3) is the one stealing from a PyPtr<PyObject>,
// which is unavoidable as long as to_py_object takes &self.
// Note that changing ToPyObject to take self by value would cause the PyPtr to become
// unusable in case (4) as well. Users would have to add a .clone() call if the PyPtr
// is still needed after the call, making case (4) non-optimal.
// We could potentially fix this by using separate ToPyObject and IntoPyObject traits
// for the borrowing and stealing cases.
// Note that the 'PointerType' associated type is essential to avoid unnecessarily
// touching the reference count in cases (2) and (4).
}
/// BorrowAsPyObject is implemented for types that can be accessed as a borrowed python object
/// (without having to allocate a temporary python object)
trait BorrowAsPyObject<'p> for Sized? {
fn as_py_object(&self, py: Python<'p>) -> &PyObject<'p>;
}
// Note: I think BorrowAsPyObject is too restricted to be useful, we might as well use &PyObject.
// On the other hand, we might want to optimize ToPyObject so that it doesn't always return a new
// reference: it could return PyResult<A> with associated type A : 'p + PythonPointer + Deref<PyObject<'p>>.
// Then types that can borrow existing python objects can return A=&'p PyObject<'p>,
// while other types can return A=PyPtr<'p, PyObject<'p>>.
// impl ToPyObject for BorrowAsPyObject
impl <'p, T : BorrowAsPyObject<'p>> ToPyObject<'p> for T {
type PointerType = PyPtr<'p, PyObject<'p>>;
#[inline]
fn to_py_object(&self, py: Python<'p>) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
Ok(PyPtr::new(self.as_py_object(py)))
}
/// FromPyObject is implemented by various types that can be extracted from a python object.
pub trait FromPyObject<'p, 's> {
fn from_py_object(s: &'s PyObject<'p>) -> PyResult<'p, Self>;
}
// PyObject, PyModule etc.
// We support all three traits (FromPyObject, ToPyObject, BorrowAsPyObject) for
// borrowed python references.
// We support FromPyObject and ToPyObject for borrowed python references.
// This allows using existing python objects in code that generically expects a value
// convertible to a python object.
impl <'p, T : PythonObject<'p>> BorrowAsPyObject<'p> for T {
#[inline]
fn as_py_object(&self, _: Python<'p>) -> &PyObject<'p> {
self.as_object()
impl <'p, 's, T : PythonObject<'p>> ToPyObject<'p, 's> for T {
type PointerType = &'s PyObject<'p>;
fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, &'s PyObject<'p>> {
Ok(self.as_object())
}
}
@ -64,10 +91,11 @@ impl <'p, 'a, T : PythonObject<'p>> FromPyObject<'p, 'a> for &'a T {
// This allows using existing python objects in code that generically expects a value
// convertible to a python object.
impl <'p, T : PythonObject<'p>> BorrowAsPyObject<'p> for PyPtr<'p, T> {
#[inline]
fn as_py_object(&self, _: Python<'p>) -> &PyObject<'p> {
self.as_object()
impl <'p, 's, T : PythonObject<'p>> ToPyObject<'p, 's> for PyPtr<'p, T> {
type PointerType = &'s PyObject<'p>;
fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, &'s PyObject<'p>> {
Ok(self.as_object())
}
}
@ -78,15 +106,16 @@ impl <'p, 'a, T : PythonObject<'p>> FromPyObject<'p, 'a> for PyPtr<'p, T> {
}
}
// bool
// As the bool instances have lifetime 'p, we can implement BorrowAsPyObject, not just ToPyObject
impl <'p> BorrowAsPyObject<'p> for bool {
#[inline]
fn as_py_object(&self, py: Python<'p>) -> &PyObject<'p> {
/*
impl <'p, T : PythonObject<'p>> ToPyObject<'p> for bool {
type PointerType = &'p PyObject<'p>;
fn to_py_object(&self, py: Python<'p>) -> PyResult<'p, Self::PointerType>;
if *self { py.True() } else { py.False() }
}
}
*/
impl <'p, 'a> FromPyObject<'p, 'a> for bool {
fn from_py_object(s: &'a PyObject<'p>) -> PyResult<'p, bool> {
@ -104,10 +133,10 @@ impl <'p, 'a> FromPyObject<'p, 'a> for bool {
// Strings.
// When converting strings to/from python, we need to copy the string data.
// This means we can implement ToPyObject for str, but FromPyObject only for String.
impl <'p> ToPyObject<'p> for str {
impl <'p, 's> ToPyObject<'p, 's> for str {
type PointerType = PyPtr<'p, PyObject<'p>>;
fn to_py_object(&self, py : Python<'p>) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
fn to_py_object(&'s self, py : Python<'p>) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
let ptr : *const c_char = self.as_ptr() as *const _;
let len : ffi::Py_ssize_t = std::num::from_uint(self.len()).unwrap();
unsafe {

View file

@ -8,7 +8,7 @@ use pyptr::{PyPtr, PythonPointer, as_ptr};
use err;
/// Trait implemented by all python object types.
pub trait PythonObject<'p> {
pub trait PythonObject<'p> : 'p {
/// Upcast from PyObject to a concrete python object type.
/// Returns None if the python object is not of the specified type.
fn from_object<'a>(&'a PyObject<'p>) -> Option<&'a Self>;
@ -99,10 +99,11 @@ impl <'p> PyObject<'p> {
impl <'p> std::fmt::Show for PyObject<'p> {
fn fmt(&self, f : &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let rep = try!(self.repr().map_err(|_| std::fmt::Error));
unimplemented!()
/* let rep = try!(self.repr().map_err(|_| std::fmt::Error));
let slice = try!(::conversion::string_as_slice(&*rep).map_err(|_| std::fmt::Error));
f.write_str(try!(std::str::from_utf8(slice).map_err(|_| std::fmt::Error)))
}
*/ }
}
impl <'p> PartialEq for PyObject<'p> {
@ -116,7 +117,7 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
/// Determines whether this object has the given attribute.
/// This is equivalent to the Python expression 'hasattr(self, attr_name)'.
#[inline]
fn hasattr<Sized? N: ToPyObject<'p>>(&self, attr_name: &N) -> PyResult<'p, bool> {
fn hasattr<'n, Sized? N: ToPyObject<'p, 'n>>(&self, attr_name: &'n N) -> PyResult<'p, bool> {
let py = self.python();
let attr_name = try!(attr_name.to_py_object(py));
unsafe {
@ -127,7 +128,8 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
/// Retrieves an attribute value.
/// This is equivalent to the Python expression 'self.attr_name'.
#[inline]
fn getattr<Sized? N: ToPyObject<'p>>(&self, attr_name: &N) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
fn getattr<'n, Sized? N: ToPyObject<'p, 'n>>(&self, attr_name: &'n N)
-> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
let py = self.python();
let attr_name = try!(attr_name.to_py_object(py));
unsafe {
@ -139,8 +141,8 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
/// Sets an attribute value.
/// This is equivalent to the Python expression 'self.attr_name = value'.
#[inline]
fn setattr<Sized? N: ToPyObject<'p>, Sized? V: ToPyObject<'p>>
(&self, attr_name: &N, value: &V) -> PyResult<'p, ()> {
fn setattr<'n, 'v, Sized? N: ToPyObject<'p, 'n>, Sized? V: ToPyObject<'p, 'v>>
(&self, attr_name: &'n N, value: &'v V) -> PyResult<'p, ()> {
let py = self.python();
let attr_name = try!(attr_name.to_py_object(py));
let value = try!(value.to_py_object(py));
@ -153,7 +155,7 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
/// Deletes an attribute.
/// This is equivalent to the Python expression 'del self.attr_name'.
#[inline]
fn delattr<Sized? N: ToPyObject<'p>>(&self, attr_name: &N) -> PyResult<'p, ()> {
fn delattr<'n, Sized? N: ToPyObject<'p, 'n>>(&self, attr_name: &'n N) -> PyResult<'p, ()> {
let py = self.python();
let attr_name = try!(attr_name.to_py_object(py));
unsafe {
@ -214,7 +216,9 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
unimplemented!()
}
fn call_method<Sized? N: ToPyObject<'p>>(&self, name: &N, args: &PyObject<'p>, kw: Option<&PyObject<'p>>) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
fn call_method<'n, Sized? N: ToPyObject<'p, 'n>>
(&self, name: &'n N, args: &PyObject<'p>, kw: Option<&PyObject<'p>>)
-> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
try!(self.getattr(name)).call(args, kw)
}
}

View file

@ -12,10 +12,10 @@ use python::Python;
/// The PyPtr<T> owns one reference to a python object.
/// Python objects are reference-counted, so it is possible to have
/// multiple PyPtr<T> objects pointing to the same object, like Rc<T>.
pub struct PyPtr<'p, T : 'p + PythonObject<'p>>(&'p T);
pub struct PyPtr<'p, T : PythonObject<'p>>(&'p T);
// impl Deref for PyPtr
impl <'p, T : 'p + PythonObject<'p>> Deref for PyPtr<'p, T> {
impl <'p, T : PythonObject<'p>> Deref for PyPtr<'p, T> {
type Target = T;
#[inline]
@ -27,7 +27,7 @@ impl <'p, T : 'p + PythonObject<'p>> Deref for PyPtr<'p, T> {
// impl Drop for PyPtr
#[unsafe_destructor]
impl<'p, T : 'p + PythonObject<'p>> Drop for PyPtr<'p, T> {
impl<'p, T : PythonObject<'p>> Drop for PyPtr<'p, T> {
#[inline]
fn drop(&mut self) {
debug_assert!(self.0.as_object().get_refcnt() > 0);
@ -36,7 +36,7 @@ impl<'p, T : 'p + PythonObject<'p>> Drop for PyPtr<'p, T> {
}
// impl Clone for PyPtr
impl<'p, T : 'p + PythonObject<'p>> Clone for PyPtr<'p, T> {
impl<'p, T : PythonObject<'p>> Clone for PyPtr<'p, T> {
#[inline]
fn clone(&self) -> PyPtr<'p, T> {
unsafe { ffi::Py_INCREF(self.as_ptr()) };
@ -45,15 +45,22 @@ impl<'p, T : 'p + PythonObject<'p>> Clone for PyPtr<'p, T> {
}
// impl Show for PyPtr
impl<'p, T : 'p + PythonObject<'p> + std::fmt::Show> std::fmt::Show for PyPtr<'p, T> {
impl<'p, T : PythonObject<'p> + std::fmt::Show> std::fmt::Show for PyPtr<'p, T> {
#[inline]
fn fmt(&self, f : &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
self.deref().fmt(f)
}
}
// impl BorrowFrom<PyPtr>
impl<'p, T : PythonObject<'p>> std::borrow::BorrowFrom<PyPtr<'p, T>> for T {
fn borrow_from<'a>(owned: &'a PyPtr<'p, T>) -> &'a T {
&**owned
}
}
// impl PyPtr
impl<'p, T : 'p + PythonObject<'p>> PyPtr<'p, T> {
impl<'p, T : PythonObject<'p>> PyPtr<'p, T> {
/// Creates a new PyPtr instance from a borrowed reference.
/// This increments the reference count.
#[inline]
@ -89,7 +96,7 @@ pub fn steal_ptr<P: PythonPointer>(p: P) -> *mut ffi::PyObject {
PythonPointer::steal_ptr(p)
}
impl <'p, T : 'p + PythonObject<'p>> PythonPointer for PyPtr<'p, T> {
impl <'p, T : PythonObject<'p>> PythonPointer for PyPtr<'p, T> {
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
self.deref().as_ptr()
@ -106,7 +113,7 @@ impl <'p, T : 'p + PythonObject<'p>> PythonPointer for PyPtr<'p, T> {
// &PyObject (etc.) is also a PythonPointer
// (but steal_ptr increases the reference count)
impl <'p, 'a, T : 'p + PythonObject<'p>> PythonPointer for &'a T {
impl <'p, 'a, T : PythonObject<'p>> PythonPointer for &'a T {
#[inline]
fn as_ptr(&self) -> *mut ffi::PyObject {
self.deref().as_ptr()