2015-01-05 16:05:53 +00:00
|
|
|
use libc::c_char;
|
|
|
|
use std;
|
|
|
|
use ffi;
|
2015-01-07 00:40:48 +00:00
|
|
|
use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, ToPythonPointer};
|
|
|
|
use objects::{PyObject, PyBool, exc};
|
2015-01-05 15:34:12 +00:00
|
|
|
use err::{self, PyErr, PyResult};
|
2015-01-05 16:05:53 +00:00
|
|
|
|
|
|
|
/// 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)
|
2015-01-05 20:14:01 +00:00
|
|
|
pub trait ToPyObject<'p> : Sized {
|
|
|
|
type ObjectType : PythonObject<'p> = PyObject<'p>;
|
2015-01-05 15:34:12 +00:00
|
|
|
|
2015-01-07 00:40:48 +00:00
|
|
|
fn to_py_object(self, py: Python<'p>) -> PyResult<'p, Self::ObjectType>;
|
2015-01-05 16:05:53 +00:00
|
|
|
|
2015-01-05 20:14:01 +00:00
|
|
|
#[inline]
|
2015-01-07 00:40:48 +00:00
|
|
|
fn with_borrowed_ptr<F, R>(self, py: Python<'p>, f: F) -> PyResult<'p, R> where F: FnOnce(*mut ffi::PyObject) -> PyResult<'p, R> {
|
2015-01-05 20:14:01 +00:00
|
|
|
let obj = try!(self.to_py_object(py));
|
2015-01-07 00:40:48 +00:00
|
|
|
f(ToPythonPointer::as_ptr(&obj))
|
2015-01-05 20:14:01 +00:00
|
|
|
}
|
2015-01-04 01:54:54 +00:00
|
|
|
|
2015-01-05 20:14:01 +00:00
|
|
|
// FFI functions that accept a borrowed reference will use:
|
2015-01-07 00:40:48 +00:00
|
|
|
// input.with_borrowed_ptr(|obj| ffi::Call(obj.as_ptr())
|
2015-01-05 20:14:01 +00:00
|
|
|
// 1) input is &PyObject
|
|
|
|
// -> with_py_object() just forwards to the closure
|
|
|
|
// 2) input is PyPtr<PyObject>
|
|
|
|
// -> to_py_object() is no-op; FFI call happens; PyPtr::drop() calls Py_DECREF()
|
|
|
|
// 3) input is &str, int, ...
|
|
|
|
// -> to_py_object() allocates new python object; FFI call happens; PyPtr::drop() calls Py_DECREF()
|
2015-01-05 15:34:12 +00:00
|
|
|
|
2015-01-05 20:14:01 +00:00
|
|
|
// FFI functions that steal a reference will use:
|
2015-01-07 00:40:48 +00:00
|
|
|
// let input = try!(input.to_py_object()); ffi::Call(input.into_owned_ptr())
|
2015-01-05 20:14:01 +00:00
|
|
|
// 1) input is &PyObject
|
|
|
|
// -> to_py_object() calls Py_INCREF
|
|
|
|
// 2) input is PyPtr<PyObject>
|
|
|
|
// -> to_py_object() is no-op
|
|
|
|
// 3) input is &str, int, ...
|
|
|
|
// -> to_py_object() allocates new python object
|
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-07 00:40:48 +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
|
|
|
|
// convertible to a python object.
|
2015-01-04 01:54:54 +00:00
|
|
|
|
2015-01-07 00:40:48 +00:00
|
|
|
impl <'p, T> ToPyObject<'p> for T where T: PythonObject<'p> {
|
2015-01-05 20:14:01 +00:00
|
|
|
type ObjectType = T;
|
2015-01-04 01:54:54 +00:00
|
|
|
|
2015-01-04 23:07:31 +00:00
|
|
|
#[inline]
|
2015-01-07 00:40:48 +00:00
|
|
|
fn to_py_object(self, py: Python<'p>) -> PyResult<'p, T> {
|
|
|
|
Ok(self)
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-07 00:40:48 +00:00
|
|
|
impl <'p, 's, T> FromPyObject<'p, 's> for T where T: PythonObjectWithCheckedDowncast<'p> {
|
2015-01-05 16:05:53 +00:00
|
|
|
#[inline]
|
2015-01-07 00:40:48 +00:00
|
|
|
fn from_py_object(s : &'s PyObject<'p>) -> PyResult<'p, T> {
|
|
|
|
Ok(try!(s.clone().cast_into()))
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-07 00:40:48 +00:00
|
|
|
// &PyObject, &PyModule etc.
|
|
|
|
// 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
|
2015-01-07 00:40:48 +00:00
|
|
|
// convertible to a python object.
|
2015-01-05 16:05:53 +00:00
|
|
|
|
2015-01-07 00:40:48 +00:00
|
|
|
impl <'p, 's, T> ToPyObject<'p> for &'s T where T : PythonObject<'p> {
|
2015-01-05 20:14:01 +00:00
|
|
|
type ObjectType = T;
|
2015-01-04 01:54:54 +00:00
|
|
|
|
2015-01-04 23:07:31 +00:00
|
|
|
#[inline]
|
2015-01-07 00:40:48 +00:00
|
|
|
fn to_py_object(self, py: Python<'p>) -> PyResult<'p, T> {
|
|
|
|
Ok((*self).clone())
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
2015-01-07 00:40:48 +00:00
|
|
|
|
2015-01-05 16:05:53 +00:00
|
|
|
#[inline]
|
2015-01-07 00:40:48 +00:00
|
|
|
fn with_borrowed_ptr<F, R>(self, py: Python<'p>, f: F) -> PyResult<'p, R>
|
|
|
|
where F: FnOnce(*mut ffi::PyObject) -> PyResult<'p, R>
|
|
|
|
{
|
|
|
|
// Avoid unnecessary Py_INCREF/Py_DECREF pair
|
|
|
|
f(self.as_ptr())
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// bool
|
2015-01-04 01:54:54 +00:00
|
|
|
|
2015-01-04 19:11:18 +00:00
|
|
|
|
2015-01-05 20:14:01 +00:00
|
|
|
impl <'p> ToPyObject<'p> for bool {
|
2015-01-06 00:04:25 +00:00
|
|
|
type ObjectType = PyBool<'p>;
|
2015-01-05 20:14:01 +00:00
|
|
|
|
|
|
|
#[inline]
|
2015-01-07 00:40:48 +00:00
|
|
|
fn to_py_object(self, py: Python<'p>) -> PyResult<'p, PyBool<'p>> {
|
|
|
|
Ok(PyBool::get(py, self))
|
2015-01-05 20:14:01 +00:00
|
|
|
}
|
2015-01-04 01:54:54 +00:00
|
|
|
|
2015-01-04 19:11:18 +00:00
|
|
|
#[inline]
|
2015-01-07 00:40:48 +00:00
|
|
|
fn with_borrowed_ptr<F, R>(self, py: Python<'p>, f: F) -> PyResult<'p, R>
|
|
|
|
where F: FnOnce(*mut ffi::PyObject) -> PyResult<'p, R>
|
2015-01-06 00:04:25 +00:00
|
|
|
{
|
2015-01-05 20:14:01 +00:00
|
|
|
// Avoid unnecessary Py_INCREF/Py_DECREF pair
|
2015-01-07 00:40:48 +00:00
|
|
|
f(unsafe { if self { ffi::Py_True() } else { ffi::Py_False() } })
|
2015-01-04 19:11:18 +00:00
|
|
|
}
|
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> {
|
2015-01-07 00:40:48 +00:00
|
|
|
Ok(try!(s.clone().cast_into::<PyBool>()).is_true())
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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-05 20:14:01 +00:00
|
|
|
impl <'p, 's> ToPyObject<'p> for &'s str {
|
|
|
|
type ObjectType = PyObject<'p>;
|
2015-01-04 00:34:13 +00:00
|
|
|
|
2015-01-07 00:40:48 +00:00
|
|
|
fn to_py_object(self, py : Python<'p>) -> PyResult<'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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-07 00:40:48 +00:00
|
|
|
impl <'p, 's> FromPyObject<'p, 's> for &'s str {
|
|
|
|
fn from_py_object(o: &'s PyObject<'p>) -> PyResult<'p, &'s str> {
|
|
|
|
let s = try!(string_as_slice(o));
|
|
|
|
match std::str::from_utf8(s) {
|
|
|
|
Ok(s) => Ok(s),
|
|
|
|
Err(err) => Err(PyErr::new(try!(exc::UnicodeDecodeError::new_utf8(o.python(), s, err))))
|
|
|
|
}
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-07 00:40:48 +00:00
|
|
|
pub fn string_as_slice<'a, 'p>(s: &'a PyObject<'p>) -> PyResult<'p, &'a [u8]> {
|
2015-01-05 16:05:53 +00:00
|
|
|
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 {
|
2015-01-07 00:40:48 +00:00
|
|
|
let buffer = buffer as *const u8; // TODO see std::ffi
|
2015-01-11 03:21:05 +00:00
|
|
|
Ok(std::slice::from_raw_buf(std::mem::copy_lifetime(s, &buffer), length as usize))
|
2015-01-05 16:05:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|