Initial support for python 3.
This commit is contained in:
parent
38b5971e56
commit
a106cabc4a
14
src/err.rs
14
src/err.rs
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
//!
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue