2015-01-05 16:05:53 +00:00
|
|
|
use libc::c_char;
|
|
|
|
use std;
|
|
|
|
use ffi;
|
2015-01-05 15:34:12 +00:00
|
|
|
use python::{Python, PythonObject, PythonObjectWithCheckedDowncast};
|
|
|
|
use object::PyObject;
|
|
|
|
use err::{self, PyErr, PyResult};
|
2015-01-05 16:05:53 +00:00
|
|
|
use pyptr::{PyPtr, PythonPointer};
|
|
|
|
|
|
|
|
/// ToPyObject is implemented for types that can be converted into a python object.
|
2015-01-04 01:54:54 +00:00
|
|
|
/// 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? {
|
2015-01-05 15:34:12 +00:00
|
|
|
type ResultType : PythonObject<'p> = PyObject<'p>;
|
|
|
|
|
2015-01-04 01:54:54 +00:00
|
|
|
// 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
|
2015-01-05 15:34:12 +00:00
|
|
|
type PointerType : PythonPointer + std::ops::Deref // <Target = Self::ResultType>
|
|
|
|
= PyPtr<'p, Self::ResultType>;
|
2015-01-05 16:05:53 +00:00
|
|
|
|
2015-01-04 01:54:54 +00:00
|
|
|
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()
|
2015-01-05 16:05:53 +00:00
|
|
|
|
2015-01-04 01:54:54 +00:00
|
|
|
// 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).
|
2015-01-05 15:34:12 +00:00
|
|
|
|
|
|
|
// Btw: I'm not sure if this type crazyness is worth it.
|
|
|
|
// If rust had automatic re-borrowing to avoid the '&*p' dance when using a PyPtr as &PyObject,
|
|
|
|
// we should probably just force the user to manually call .to_py()
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
|
2015-01-04 01:54:54 +00:00
|
|
|
/// 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>;
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// PyObject, PyModule etc.
|
2015-01-04 01:54:54 +00:00
|
|
|
// We support FromPyObject and ToPyObject for borrowed python references.
|
2015-01-05 16:05:53 +00:00
|
|
|
// This allows using existing python objects in code that generically expects a value
|
|
|
|
// convertible to a python object.
|
2015-01-04 01:54:54 +00:00
|
|
|
|
2015-01-04 23:07:31 +00:00
|
|
|
impl <'p, 's, T> ToPyObject<'p, 's> for T where T : PythonObject<'p> {
|
2015-01-05 15:34:12 +00:00
|
|
|
type ResultType = T;
|
|
|
|
type PointerType = &'s T;
|
2015-01-04 01:54:54 +00:00
|
|
|
|
2015-01-04 23:07:31 +00:00
|
|
|
#[inline]
|
2015-01-05 15:34:12 +00:00
|
|
|
fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, &'s T> {
|
|
|
|
Ok(self)
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-05 15:34:12 +00:00
|
|
|
impl <'p, 's, T> FromPyObject<'p, 's> for &'s T where T: PythonObjectWithCheckedDowncast<'p> {
|
2015-01-05 16:05:53 +00:00
|
|
|
#[inline]
|
2015-01-04 19:11:18 +00:00
|
|
|
fn from_py_object(s: &'s PyObject<'p>) -> PyResult<'p, &'s T> {
|
2015-01-05 16:05:53 +00:00
|
|
|
s.downcast()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PyPtr<T>
|
2015-01-04 23:07:31 +00:00
|
|
|
// We support FromPyObject and ToPyObject for owned python references.
|
2015-01-05 16:05:53 +00:00
|
|
|
// This allows using existing python objects in code that generically expects a value
|
2015-01-04 23:07:31 +00:00
|
|
|
// convertible to a python object, without having to re-borrow the &PyObject.
|
2015-01-05 16:05:53 +00:00
|
|
|
|
2015-01-04 23:07:31 +00:00
|
|
|
impl <'p, 's, T> ToPyObject<'p, 's> for PyPtr<'p, T> where T: PythonObject<'p> {
|
2015-01-05 15:34:12 +00:00
|
|
|
type ResultType = T;
|
|
|
|
type PointerType = &'s T;
|
2015-01-04 01:54:54 +00:00
|
|
|
|
2015-01-04 23:07:31 +00:00
|
|
|
#[inline]
|
2015-01-05 15:34:12 +00:00
|
|
|
fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, &'s T> {
|
|
|
|
Ok(&**self)
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-05 15:34:12 +00:00
|
|
|
impl <'p, 's, T> FromPyObject<'p, 's> for PyPtr<'p, T> where T: PythonObjectWithCheckedDowncast<'p> {
|
2015-01-05 16:05:53 +00:00
|
|
|
#[inline]
|
2015-01-04 19:11:18 +00:00
|
|
|
fn from_py_object(s : &'s PyObject<'p>) -> PyResult<'p, PyPtr<'p, T>> {
|
2015-01-05 16:05:53 +00:00
|
|
|
PyPtr::new(s).downcast_into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// bool
|
2015-01-04 01:54:54 +00:00
|
|
|
|
2015-01-04 19:11:18 +00:00
|
|
|
|
|
|
|
impl <'p, 's> ToPyObject<'p, 's> for bool {
|
2015-01-05 15:34:12 +00:00
|
|
|
type ResultType = PyObject<'p>;
|
2015-01-04 01:54:54 +00:00
|
|
|
type PointerType = &'p PyObject<'p>;
|
|
|
|
|
2015-01-04 19:11:18 +00:00
|
|
|
#[inline]
|
|
|
|
fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, &'p PyObject<'p>> {
|
|
|
|
Ok(if *self { py.True() } else { py.False() })
|
|
|
|
}
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl <'p, 'a> FromPyObject<'p, 'a> for bool {
|
|
|
|
fn from_py_object(s: &'a PyObject<'p>) -> PyResult<'p, bool> {
|
|
|
|
let py = s.python();
|
|
|
|
if s == py.True() {
|
|
|
|
Ok(true)
|
|
|
|
} else if s == py.False() {
|
|
|
|
Ok(false)
|
|
|
|
} else {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2015-01-04 01:54:54 +00:00
|
|
|
impl <'p, 's> ToPyObject<'p, 's> for str {
|
2015-01-05 15:34:12 +00:00
|
|
|
type ResultType = PyObject<'p>;
|
2015-01-04 00:34:13 +00:00
|
|
|
type PointerType = PyPtr<'p, PyObject<'p>>;
|
|
|
|
|
2015-01-04 01:54:54 +00:00
|
|
|
fn to_py_object(&'s self, py : Python<'p>) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> {
|
2015-01-05 16:05:53 +00:00
|
|
|
let ptr : *const c_char = self.as_ptr() as *const _;
|
|
|
|
let len : ffi::Py_ssize_t = std::num::from_uint(self.len()).unwrap();
|
|
|
|
unsafe {
|
|
|
|
use std::ascii::AsciiExt;
|
|
|
|
let obj = if self.is_ascii() {
|
|
|
|
ffi::PyString_FromStringAndSize(ptr, len)
|
|
|
|
} else {
|
|
|
|
ffi::PyUnicode_FromStringAndSize(ptr, len)
|
|
|
|
};
|
|
|
|
err::result_from_owned_ptr(py, obj)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl <'p, 'a> FromPyObject<'p, 'a> for String {
|
|
|
|
fn from_py_object(s : &'a PyObject<'p>) -> PyResult<'p, String> {
|
|
|
|
string_as_slice(s).map(|buf| String::from_utf8_lossy(buf).to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn string_as_slice<'a, 'p>(s : &'a PyObject<'p>) -> PyResult<'p, &'a [u8]> {
|
|
|
|
unsafe {
|
|
|
|
let mut buffer : *mut c_char = std::mem::uninitialized();
|
|
|
|
let mut length : ffi::Py_ssize_t = std::mem::uninitialized();
|
|
|
|
if ffi::PyString_AsStringAndSize(s.as_ptr(), &mut buffer, &mut length) == 1 {
|
|
|
|
Err(PyErr::fetch(s.python()))
|
|
|
|
} else {
|
|
|
|
let buffer = buffer as *const u8;
|
|
|
|
Ok(std::slice::from_raw_buf(std::mem::copy_lifetime(s, &buffer), length as uint))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|