Initial support for python 3.

This commit is contained in:
Daniel Grunwald 2015-05-23 23:52:38 +02:00
parent 38b5971e56
commit a106cabc4a
10 changed files with 247 additions and 135 deletions

View File

@ -19,6 +19,7 @@
use std;
use python::{PythonObject, Python, ToPythonPointer, PythonObjectDowncastError};
use objects::{PyObject, PyType, exc};
#[cfg(feature="python27-sys")]
use objects::oldstyle::PyClass;
use ffi;
use libc;
@ -105,7 +106,7 @@ impl <'p> PyErr<'p> {
} else {
PyErr {
ptype: py.get_type::<exc::TypeError>().into_object(),
pvalue: Some("exceptions must derive from BaseException".to_py_object(py)),
pvalue: Some("exceptions must derive from BaseException".to_py_object(py).into_object()),
ptraceback: None
}
}
@ -169,6 +170,7 @@ impl <'p> PyErr<'p> {
/// Retrieves the exception type.
/// If the exception type is an old-style class, returns `oldstyle::PyClass`.
#[cfg(feature="python27-sys")]
pub fn get_type(&self) -> PyType<'p> {
let py = self.ptype.python();
match self.ptype.clone().cast_into::<PyType>() {
@ -180,6 +182,16 @@ impl <'p> PyErr<'p> {
}
}
}
/// Retrieves the exception type.
#[cfg(not(feature="python27-sys"))]
pub fn get_type(&self) -> PyType<'p> {
let py = self.ptype.python();
match self.ptype.clone().cast_into::<PyType>() {
Ok(t) => t,
Err(_) => py.None().get_type().clone()
}
}
/// Retrieves the exception instance for this error.
/// This method takes `&mut self` because the error might need

View File

@ -23,7 +23,6 @@
#![feature(slice_patterns)] // for tuple_conversion macros
#![feature(utf8_error)] // for translating Utf8Error to python exception
#![allow(unused_imports, unused_variables)]
#![feature(unicode)]
//! Rust bindings to the python interpreter.
//!

View File

@ -23,7 +23,7 @@ use std::cmp::Ordering;
use ffi;
use libc;
use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, ToPythonPointer};
use objects::{PyObject, PyTuple, PyDict};
use objects::{PyObject, PyTuple, PyDict, PyString};
use conversion::ToPyObject;
use err::{PyErr, PyResult, result_from_owned_ptr, error_on_minusone};
@ -76,7 +76,7 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
/// Compares two python objects.
/// This is equivalent to the python expression 'cmp(self, other)'.
#[inline]
#[cfg(feature="python27-sys")]
fn compare<O: ?Sized>(&self, other: &O) -> PyResult<'p, Ordering> where O: ToPyObject<'p> {
let py = self.python();
other.with_borrowed_ptr(py, |other| unsafe {
@ -114,6 +114,7 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> {
/// Compute the unicode string representation of self.
/// This is equivalent to the python expression 'unistr(self)'.
#[inline]
#[cfg(feature="python27-sys")]
fn unistr(&self) -> PyResult<'p, PyObject<'p>> {
unsafe {
result_from_owned_ptr(self.python(), ffi::PyObject_Unicode(self.as_ptr()))
@ -251,7 +252,7 @@ impl <'p> fmt::Debug for PyObject<'p> {
fn fmt(&self, f : &mut fmt::Formatter) -> Result<(), fmt::Error> {
use objectprotocol::ObjectProtocol;
let repr_obj = try!(self.str().map_err(|_| fmt::Error));
let repr = try!(repr_obj.extract::<Cow<str>>().map_err(|_| fmt::Error));
let repr = try!(PyString::extract_lossy(&repr_obj).map_err(|_| fmt::Error));
f.write_str(&*repr)
}
}
@ -260,7 +261,7 @@ impl <'p> fmt::Display for PyObject<'p> {
fn fmt(&self, f : &mut fmt::Formatter) -> Result<(), fmt::Error> {
use objectprotocol::ObjectProtocol;
let repr_obj = try!(self.repr().map_err(|_| fmt::Error));
let repr = try!(repr_obj.extract::<Cow<str>>().map_err(|_| fmt::Error));
let repr = try!(PyString::extract_lossy(&repr_obj).map_err(|_| fmt::Error));
f.write_str(&*repr)
}
}

View File

@ -71,6 +71,7 @@ macro_rules! exc_type(
exc_type!(BaseException, PyExc_BaseException);
exc_type!(Exception, PyExc_Exception);
#[cfg(feature="python27-sys")]
exc_type!(StandardError, PyExc_StandardError);
exc_type!(LookupError, PyExc_LookupError);
exc_type!(AssertionError, PyExc_AssertionError);

View File

@ -33,7 +33,7 @@ impl <'p> PyList<'p> {
let ptr = ffi::PyList_New(elements.len() as Py_ssize_t);
let t = err::result_from_owned_ptr(py, ptr).unwrap().unchecked_cast_into::<PyList>();
for (i, e) in elements.iter().enumerate() {
ffi::PyList_SET_ITEM(ptr, i as Py_ssize_t, e.clone().steal_ptr());
ffi::PyList_SetItem(ptr, i as Py_ssize_t, e.clone().steal_ptr());
}
t
}
@ -42,9 +42,9 @@ impl <'p> PyList<'p> {
/// Gets the length of the list.
#[inline]
pub fn len(&self) -> usize {
// non-negative Py_ssize_t should always fit into Rust uint
// non-negative Py_ssize_t should always fit into Rust usize
unsafe {
ffi::PyList_GET_SIZE(self.as_ptr()) as usize
ffi::PyList_Size(self.as_ptr()) as usize
}
}
@ -54,7 +54,7 @@ impl <'p> PyList<'p> {
pub fn get_item(&self, index: usize) -> PyObject<'p> {
assert!(index < self.len());
unsafe {
PyObject::from_borrowed_ptr(self.python(), ffi::PyList_GET_ITEM(self.as_ptr(), index as Py_ssize_t))
PyObject::from_borrowed_ptr(self.python(), ffi::PyList_GetItem(self.as_ptr(), index as Py_ssize_t))
}
}
@ -65,7 +65,7 @@ impl <'p> PyList<'p> {
let r = unsafe { ffi::PyList_SetItem(self.as_ptr(), index as Py_ssize_t, item.steal_ptr()) };
assert!(r == 0);
}
/// Inserts an item at the specified index.
///
/// Panics if the index is out of range.
@ -84,7 +84,7 @@ impl <'p, T> ToPyObject<'p> for [T] where T: ToPyObject<'p> {
let t = err::cast_from_owned_ptr_or_panic(py, ptr);
for (i, e) in self.iter().enumerate() {
let obj = e.to_py_object(py);
ffi::PyList_SET_ITEM(ptr, i as Py_ssize_t, obj.steal_ptr());
ffi::PyList_SetItem(ptr, i as Py_ssize_t, obj.steal_ptr());
}
t
}
@ -94,6 +94,7 @@ impl <'p, T> ToPyObject<'p> for [T] where T: ToPyObject<'p> {
/*
This implementation is not possible, because we allow extracting python strings as CowString<'s>,
but there's no guarantee that the list isn't modified while the CowString borrow exists.
Maybe reconsider whether extraction should be able to borrow the contents of the python object?
impl <'p, 's, T> FromPyObject<'p, 's> for Vec<T> where T: FromPyObject<'p, 's> {
fn from_py_object(s: &'s PyObject<'p>) -> PyResult<'p, Vec<T>> {
let py = s.python();

View File

@ -19,13 +19,25 @@
pub use self::object::PyObject;
pub use self::typeobject::PyType;
pub use self::module::PyModule;
pub use self::string::{PyString, PyUnicode};
pub use self::string::{PyBytes, PyUnicode};
// Directly exporting self::string::PyString causes a compiler error?
#[cfg(feature="python27-sys")]
pub use self::string::PyBytes as PyString;
#[cfg(feature="python3-sys")]
pub use self::string::PyUnicode as PyString;
#[cfg(feature="python27-sys")]
pub use self::iterator::PyIterator;
pub use self::boolobject::PyBool;
pub use self::tuple::PyTuple;
pub use self::dict::PyDict;
pub use self::list::PyList;
pub use self::num::{PyInt, PyLong, PyFloat};
#[cfg(feature="python27-sys")]
pub use self::num::PyInt;
#[cfg(feature="python3-sys")]
pub use self::num::PyLong as PyInt;
pub use self::num::{PyLong, PyFloat};
macro_rules! pyobject_newtype(
($name: ident) => (
@ -115,11 +127,14 @@ mod typeobject;
mod module;
mod string;
mod dict;
#[cfg(feature="python27-sys")]
mod iterator;
mod boolobject;
mod tuple;
mod list;
mod num;
pub mod exc;
#[cfg(feature="python27-sys")]
pub mod oldstyle;

View File

@ -46,6 +46,7 @@ impl <'p> PyModule<'p> {
// Helper method for module_initializer!() macro, do not use directly!
#[doc(hidden)]
#[cfg(feature="python27-sys")]
pub fn _init<F, R>(py: Python<'p>, name: &CStr, init: F) -> PyResult<'p, R>
where F: FnOnce(Python<'p>, PyModule<'p>) -> PyResult<'p, R> {
let module = try!(unsafe {
@ -65,12 +66,11 @@ impl <'p> PyModule<'p> {
}
}
unsafe fn str_from_ptr<'a>(&'a self, ptr: *mut c_char) -> PyResult<'p, &'a str> {
unsafe fn str_from_ptr<'a>(&'a self, ptr: *const c_char) -> PyResult<'p, &'a str> {
let py = self.python();
if ptr == std::ptr::null_mut() {
if ptr == std::ptr::null() {
Err(PyErr::fetch(py))
} else {
let ptr = ptr as *const c_char;
let slice = CStr::from_ptr(ptr).to_bytes();
match std::str::from_utf8(slice) {
Ok(s) => Ok(std::mem::copy_lifetime(self, s)),

View File

@ -27,10 +27,13 @@ use super::exc;
use ffi::{self, Py_ssize_t};
use conversion::{ToPyObject, FromPyObject};
#[cfg(feature="python27-sys")]
pyobject_newtype!(PyInt, PyInt_Check, PyInt_Type);
pyobject_newtype!(PyLong, PyLong_Check, PyLong_Type);
pyobject_newtype!(PyFloat, PyFloat_Check, PyFloat_Type);
#[cfg(feature="python27-sys")]
impl <'p> PyInt<'p> {
/// Creates a new python `int` object.
pub fn new(py: Python<'p>, val: c_long) -> PyInt<'p> {
@ -56,12 +59,13 @@ impl <'p> PyFloat<'p> {
/// Gets the value of this float.
pub fn value(&self) -> c_double {
unsafe { ffi::PyFloat_AS_DOUBLE(self.as_ptr()) }
unsafe { ffi::PyFloat_AsDouble(self.as_ptr()) }
}
}
macro_rules! int_fits_c_long(
($rust_type:ty) => (
#[cfg(feature="python27-sys")]
impl <'p> ToPyObject<'p> for $rust_type {
type ObjectType = PyInt<'p>;
@ -73,6 +77,19 @@ macro_rules! int_fits_c_long(
}
}
#[cfg(feature="python3-sys")]
impl <'p> ToPyObject<'p> for $rust_type {
type ObjectType = PyLong<'p>;
fn to_py_object(&self, py: Python<'p>) -> PyLong<'p> {
unsafe {
err::cast_from_owned_ptr_or_panic(py,
ffi::PyLong_FromLong(*self as c_long))
}
}
}
#[cfg(feature="python27-sys")]
impl <'p, 's> FromPyObject<'p, 's> for $rust_type {
fn from_py_object(s: &'s PyObject<'p>) -> PyResult<'p, $rust_type> {
let py = s.python();
@ -86,6 +103,21 @@ macro_rules! int_fits_c_long(
}
}
}
#[cfg(feature="python3-sys")]
impl <'p, 's> FromPyObject<'p, 's> for $rust_type {
fn from_py_object(s: &'s PyObject<'p>) -> PyResult<'p, $rust_type> {
let py = s.python();
let val = unsafe { ffi::PyLong_AsLong(s.as_ptr()) };
if val == -1 && PyErr::occurred(py) {
return Err(PyErr::fetch(py));
}
match num::traits::cast::<c_long, $rust_type>(val) {
Some(v) => Ok(v),
None => Err(overflow_error(py))
}
}
}
)
);
@ -141,8 +173,13 @@ int_fits_larger_int!(isize, i64);
int_fits_larger_int!(usize, u64);
impl <'p> ToPyObject<'p> for u64 {
#[cfg(feature="python27-sys")]
type ObjectType = PyObject<'p>;
#[cfg(feature="python3-sys")]
type ObjectType = PyLong<'p>;
#[cfg(feature="python27-sys")]
fn to_py_object(&self, py: Python<'p>) -> PyObject<'p> {
unsafe {
let ptr = match num::traits::cast::<u64, c_long>(*self) {
@ -152,6 +189,13 @@ impl <'p> ToPyObject<'p> for u64 {
err::from_owned_ptr_or_panic(py, ptr)
}
}
#[cfg(feature="python3-sys")]
fn to_py_object(&self, py: Python<'p>) -> PyLong<'p> {
unsafe {
err::cast_from_owned_ptr_or_panic(py, ffi::PyLong_FromUnsignedLongLong(*self))
}
}
}
fn pylong_as_u64<'p>(obj: &PyObject<'p>) -> PyResult<'p, u64> {
@ -165,6 +209,7 @@ fn pylong_as_u64<'p>(obj: &PyObject<'p>) -> PyResult<'p, u64> {
}
impl <'p, 's> FromPyObject<'p, 's> for u64 {
#[cfg(feature="python27-sys")]
fn from_py_object(s: &'s PyObject<'p>) -> PyResult<'p, u64> {
let py = s.python();
let ptr = s.as_ptr();
@ -182,6 +227,20 @@ impl <'p, 's> FromPyObject<'p, 's> for u64 {
}
}
}
#[cfg(feature="python3-sys")]
fn from_py_object(s: &'s PyObject<'p>) -> PyResult<'p, u64> {
let py = s.python();
let ptr = s.as_ptr();
unsafe {
if ffi::PyLong_Check(ptr) != 0 {
pylong_as_u64(s)
} else {
let num = try!(err::result_from_owned_ptr(py, ffi::PyNumber_Long(ptr)));
pylong_as_u64(&num)
}
}
}
}
impl <'p> ToPyObject<'p> for f64 {

View File

@ -16,10 +16,6 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
extern crate rustc_unicode;
use self::rustc_unicode::str as unicode_str;
use self::rustc_unicode::str::Utf16Item;
use std;
use std::{char, str};
use std::ascii::AsciiExt;
@ -31,40 +27,45 @@ use super::{exc, PyObject};
use err::{self, PyResult, PyErr};
use conversion::{FromPyObject, ToPyObject};
//pyobject_newtype!(PyBytes, PyBytes_Check, PyBytes_Type);
pyobject_newtype!(PyString, PyString_Check, PyString_Type);
pyobject_newtype!(PyBytes, PyBytes_Check, PyBytes_Type);
pyobject_newtype!(PyUnicode, PyUnicode_Check, PyUnicode_Type);
impl <'p> PyString<'p> {
/// Creates a new python string object from the Rust string.
///
/// Note: on Python 2, this function always creates a `str` object,
/// never a `unicode` object.
/// Use `str::to_py_object()` instead to create `unicode` objects for non-ascii strings.
pub fn new(py: Python<'p>, s: &str) -> PyString<'p> {
#[cfg(feature="python27-sys")]
pub use PyBytes as PyString;
#[cfg(feature="python3-sys")]
pub use PyUnicode as PyString;
impl <'p> PyBytes<'p> {
/// Creates a new python byte string object from the &[u8].
pub fn new(py: Python<'p>, s: &[u8]) -> PyBytes<'p> {
let ptr = s.as_ptr() as *const c_char;
let len = s.len() as ffi::Py_ssize_t;
unsafe {
err::cast_from_owned_ptr_or_panic(py,
ffi::PyString_FromStringAndSize(ptr, len))
ffi::PyBytes_FromStringAndSize(ptr, len))
}
}
/// Gets the python string data as byte slice.
pub fn as_slice(&self) -> &[u8] {
unsafe {
let buffer = ffi::PyString_AS_STRING(self.as_ptr()) as *const u8;
let length = ffi::PyString_GET_SIZE(self.as_ptr()) as usize;
let buffer = ffi::PyBytes_AsString(self.as_ptr()) as *const u8;
let length = ffi::PyBytes_Size(self.as_ptr()) as usize;
std::slice::from_raw_parts(buffer, length)
}
}
/// Gets the python string data as `&str`.
pub fn as_str(&self) -> Result<&str, str::Utf8Error> {
// In python 2.7, PyBytes serves as PyString, so it should offer the
// to_str and to_string_lossy functions:
#[cfg(feature="python27-sys")]
pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
str::from_utf8(self.as_slice())
}
#[cfg(feature="python27-sys")]
pub fn to_string_lossy(&self) -> Cow<str> {
String::from_utf8_lossy(self.as_slice())
}
}
impl <'p> PyUnicode<'p> {
@ -78,12 +79,89 @@ impl <'p> PyUnicode<'p> {
}
}
/* Note: 'as_slice removed temporarily, we need to reconsider
// whether we really should expose the platform-dependent Py_UNICODE to user code.
#[cfg(feature="python27-sys")]
pub fn as_slice(&self) -> &[ffi::Py_UNICODE] {
unsafe {
let buffer = ffi::PyUnicode_AS_UNICODE(self.as_ptr()) as *const _;
let length = ffi::PyUnicode_GET_SIZE(self.as_ptr()) as usize;
std::slice::from_raw_parts(buffer, length)
}
}*/
pub fn to_string(&self) -> PyResult<'p, Cow<str>> {
// TODO: use PyUnicode_AsUTF8AndSize if available
let py = self.python();
let bytes: PyBytes = unsafe {
try!(err::result_cast_from_owned_ptr(py, ffi::PyUnicode_AsUTF8String(self.as_ptr())))
};
match str::from_utf8(bytes.as_slice()) {
Ok(s) => Ok(Cow::Owned(s.to_owned())),
Err(e) => Err(PyErr::new(try!(exc::UnicodeDecodeError::new_utf8(py, bytes.as_slice(), e))))
}
}
pub fn to_string_lossy(&self) -> Cow<str> {
// TODO: use PyUnicode_AsUTF8AndSize if available
// TODO: test how this function handles lone surrogates or otherwise invalid code points
let py = self.python();
let bytes: PyBytes = unsafe {
err::result_cast_from_owned_ptr(py, ffi::PyUnicode_AsUTF8String(self.as_ptr()))
.ok().expect("Error in PyUnicode_AsUTF8String")
};
Cow::Owned(String::from_utf8_lossy(bytes.as_slice()).into_owned())
}
}
// On PyString (i.e. PyBytes in 2.7, PyUnicode otherwise), put static methods
// for extraction as Cow<str>:
impl <'p> PyString<'p> {
#[cfg(feature="python27-sys")]
pub fn extract<'a>(o: &'a PyObject<'p>) -> PyResult<'p, Cow<'a, str>> {
let py = o.python();
if let Ok(s) = o.cast_as::<PyBytes>() {
match s.to_str() {
Ok(s) => Ok(Cow::Borrowed(s)),
Err(e) => Err(PyErr::new(try!(exc::UnicodeDecodeError::new_utf8(py, s.as_slice(), e))))
}
} else if let Ok(u) = o.cast_as::<PyUnicode>() {
u.to_string()
} else {
Err(PyErr::new_lazy_init(py.get_type::<exc::TypeError>(), None))
}
}
#[cfg(feature="python27-sys")]
pub fn extract_lossy<'a>(o: &'a PyObject<'p>) -> PyResult<'p, Cow<'a, str>> {
let py = o.python();
if let Ok(s) = o.cast_as::<PyBytes>() {
Ok(s.to_string_lossy())
} else if let Ok(u) = o.cast_as::<PyUnicode>() {
Ok(u.to_string_lossy())
} else {
Err(PyErr::new_lazy_init(py.get_type::<exc::TypeError>(), None))
}
}
#[cfg(feature="python3-sys")]
pub fn extract<'a>(o: &'a PyObject<'p>) -> PyResult<'p, Cow<'a, str>> {
let py = o.python();
if let Ok(u) = o.cast_as::<PyUnicode>() {
u.to_string()
} else {
Err(PyErr::new_lazy_init(py.get_type::<exc::TypeError>(), None))
}
}
#[cfg(feature="python3-sys")]
pub fn extract_lossy<'a>(o: &'a PyObject<'p>) -> PyResult<'p, Cow<'a, str>> {
let py = o.python();
if let Ok(u) = o.cast_as::<PyUnicode>() {
Ok(u.to_string_lossy())
} else {
Err(PyErr::new_lazy_init(py.get_type::<exc::TypeError>(), None))
}
}
}
@ -94,97 +172,54 @@ impl <'p> PyUnicode<'p> {
/// ASCII-only strings are converted to python `str` objects;
/// other strings are converted to python `unicode` objects.
impl <'p> ToPyObject<'p> for str {
#[cfg(feature="python27-sys")]
type ObjectType = PyObject<'p>;
#[cfg(feature="python3-sys")]
type ObjectType = PyUnicode<'p>;
#[cfg(feature="python27-sys")]
fn to_py_object(&self, py : Python<'p>) -> PyObject<'p> {
if self.is_ascii() {
PyString::new(py, self).into_object()
PyBytes::new(py, self.as_bytes()).into_object()
} else {
PyUnicode::new(py, self).into_object()
}
}
#[cfg(feature="python3-sys")]
#[inline]
fn to_py_object(&self, py : Python<'p>) -> PyUnicode<'p> {
PyUnicode::new(py, self)
}
}
/// Converts rust `&str` to python object:
/// ASCII-only strings are converted to python `str` objects;
/// other strings are converted to python `unicode` objects.
impl <'p, 'a> ToPyObject<'p> for &'a str {
type ObjectType = PyObject<'p>;
type ObjectType = <str as ToPyObject<'p>>::ObjectType;
fn to_py_object(&self, py : Python<'p>) -> PyObject<'p> {
fn to_py_object(&self, py : Python<'p>) -> Self::ObjectType {
(**self).to_py_object(py)
}
}
#[cfg(py_sys_config="Py_UNICODE_SIZE_4")]
fn u32_as_bytes(input: &[u32]) -> &[u8] {
unsafe { std::mem::transmute(input) }
}
#[cfg(not(py_sys_config="Py_UNICODE_SIZE_4"))]
fn u16_as_bytes(input: &[u16]) -> &[u8] {
unsafe { std::mem::transmute(input) }
}
#[cfg(py_sys_config="Py_UNICODE_SIZE_4")]
fn unicode_buf_to_str<'p>(p: Python<'p>, u: &[ffi::Py_UNICODE])
-> Result<String, PyErr<'p> > {
let mut s = String::with_capacity(u.len());
for (i, &c) in u.iter().enumerate() {
match char::from_u32(c) {
Some(c) => s.push(c),
None => {
let e = try!(exc::UnicodeDecodeError::new(
p, cstr!("utf-32"),
u32_as_bytes(u), i .. i+1,
cstr!("invalid code point")));
return Err(PyErr::new(e));
}
}
}
return Ok(s);
}
#[cfg(not(py_sys_config="Py_UNICODE_SIZE_4"))]
fn unicode_buf_to_str<'p>(p: Python<'p>, u: &[ffi::Py_UNICODE])
-> Result<String, PyErr<'p> > {
let mut s = String::with_capacity(u.len());
for (i, c) in unicode_str::utf16_items(u).enumerate() {
match c {
Utf16Item::ScalarValue(c) => s.push(c),
Utf16Item::LoneSurrogate(_) => {
let e = try!(exc::UnicodeDecodeError::new(
p, cstr!("utf-16"),
u16_as_bytes(u), i .. i+1,
cstr!("invalid code point")));
return Err(PyErr::new(e));
}
}
}
return Ok(s);
}
/* Let's disable the Cow extraction for now. We might want to avoid "extract" borrowing the input
python object, see the (also currently disabled) impl FromPyObject for Vec<T>.
impl <'p, 's> FromPyObject<'p, 's> for Cow<'s, str> {
fn from_py_object(o: &'s PyObject<'p>) -> PyResult<'p, Cow<'s, str>> {
let py = o.python();
if let Ok(s) = o.cast_as::<PyString>() {
match s.as_str() {
Ok(s) => Ok(Cow::Borrowed(s)),
Err(e) => Err(PyErr::new(try!(exc::UnicodeDecodeError::new_utf8(py, s.as_slice(), e))))
}
} else if let Ok(u) = o.cast_as::<PyUnicode>() {
let u = u.as_slice();
let s = try!(unicode_buf_to_str(py, u));
Ok(Cow::Owned(s))
} else {
Err(PyErr::new_lazy_init(py.get_type::<exc::TypeError>(), None))
}
PyString::extract(o)
}
}
*/
/// Allows extracting strings from python objects.
/// Accepts python `str` and `unicode` objects.
/// In python 2.7, `str` is expected to be UTF-8 encoded.
impl <'p, 's> FromPyObject<'p, 's> for String {
fn from_py_object(o: &'s PyObject<'p>) -> PyResult<'p, String> {
Ok(try!(o.extract::<Cow<str>>()).into_owned())
PyString::extract(o).map(|s| s.into_owned())
}
}
@ -193,6 +228,6 @@ fn test_non_bmp() {
let gil = Python::acquire_gil();
let py = gil.python();
let s = "\u{1F30F}";
let py_string = s.to_py_object(py);
assert_eq!(s, py_string.extract::<Cow<str>>().unwrap());
let py_string = s.to_py_object(py).into_object();
assert_eq!(s, py_string.extract::<String>().unwrap());
}

View File

@ -33,7 +33,7 @@ impl <'p> PyTuple<'p> {
let ptr = ffi::PyTuple_New(len as Py_ssize_t);
let t = err::result_from_owned_ptr(py, ptr).unwrap().unchecked_cast_into::<PyTuple>();
for (i, e) in elements.iter().enumerate() {
ffi::PyTuple_SET_ITEM(ptr, i as Py_ssize_t, e.clone().steal_ptr());
ffi::PyTuple_SetItem(ptr, i as Py_ssize_t, e.clone().steal_ptr());
}
t
}
@ -50,10 +50,22 @@ impl <'p> PyTuple<'p> {
pub fn len(&self) -> usize {
// non-negative Py_ssize_t should always fit into Rust uint
unsafe {
ffi::PyTuple_GET_SIZE(self.as_ptr()) as usize
ffi::PyTuple_Size(self.as_ptr()) as usize
}
}
/// Gets the item at the specified index.
///
/// Panics if the index is out of range.
pub fn get_item(&self, index: usize) -> PyObject<'p> {
assert!(index < self.len());
unsafe {
PyObject::from_borrowed_ptr(self.python(), ffi::PyTuple_GetItem(self.as_ptr(), index as Py_ssize_t))
}
}
/* Disabled for now; we might want to change the PyObject memory layout for
compatiblity with Rust 1.0.
#[inline]
pub fn as_slice<'a>(&'a self) -> &'a [PyObject<'p>] {
// This is safe because PyObject has the same memory layout as *mut ffi::PyObject,
@ -66,38 +78,13 @@ impl <'p> PyTuple<'p> {
})
}
}
#[inline]
pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, PyObject<'p>> {
self.as_slice().iter()
}
*/
}
impl<'p> std::ops::Index<usize> for PyTuple<'p> {
type Output = PyObject<'p>;
#[inline]
fn index<'a>(&'a self, index: usize) -> &'a PyObject<'p> {
// use as_slice() to use the normal Rust bounds checking when indexing
&self.as_slice()[index]
}
}
impl<'p, 'a> std::iter::IntoIterator for &'a PyTuple<'p> {
type Item = &'a PyObject<'p>;
type IntoIter = std::slice::Iter<'a, PyObject<'p>>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
fn wrong_tuple_length<'p>(t: &PyTuple<'p>, expected_length: usize) -> PyErr<'p> {
let py = t.python();
let msg = format!("Expected tuple of length {}, but got tuple of length {}.", expected_length, t.len());
PyErr::new_lazy_init(py.get_type::<exc::ValueError>(), Some(msg.to_py_object(py)))
PyErr::new_lazy_init(py.get_type::<exc::ValueError>(), Some(msg.to_py_object(py).into_object()))
}
macro_rules! id (($a:expr) => ($a));
@ -119,6 +106,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
}
}
/* TODO: reimplement this without slice matching
impl <'p, 's, $($T: FromPyObject<'p, 's>),+> FromPyObject<'p, 's> for ($($T,)+) {
fn from_py_object(s : &'s PyObject<'p>) -> PyResult<'p, ($($T,)+)> {
let t = try!(s.cast_as::<PyTuple>());
@ -130,6 +118,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
}
}
}
*/
));
tuple_conversion!(1, (ref0, 0, A));