Remove BorrowAsPyObject, and simplify PyPtr by moving the 'p bound into the PythonObject definition.
This commit is contained in:
parent
d247b138c5
commit
5569ae3bbc
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
23
src/pyptr.rs
23
src/pyptr.rs
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue