convert PyString object

This commit is contained in:
Nikolay Kim 2017-06-20 11:59:07 -07:00
parent ce53e8230b
commit bc0f9b014f
12 changed files with 361 additions and 233 deletions

View File

@ -55,6 +55,7 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
let extra = if let Some(token) = token {
Some(quote! {
impl _pyo3::PyObjectWithToken for #cls {
#[inline]
fn token<'p>(&'p self) -> _pyo3::Python<'p> {
self.#token.token()
}
@ -100,29 +101,39 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
fn fmt(&self, f : &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let py = _pyo3::PyObjectWithToken::token(self);
let ptr = <#cls as _pyo3::ToPyPointer>::as_ptr(self);
let repr = unsafe {
_pyo3::PyString::downcast_from_ptr(
py, _pyo3::ffi::PyObject_Repr(ptr)).map_err(|_| std::fmt::Error)?
unsafe {
let repr = PyObject::from_borrowed_ptr(
py, _pyo3::ffi::PyObject_Repr(ptr));
let result = {
let s = _pyo3::PyString::downcast_from(py, &repr);
let s = try!(s.map_err(|_| std::fmt::Error));
f.write_str(&s.to_string_lossy())
};
let result = f.write_str(&repr.to_string_lossy(py));
py.release(repr);
result
}
}
}
impl std::fmt::Display for #cls {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let py = _pyo3::PyObjectWithToken::token(self);
let ptr = <#cls as _pyo3::ToPyPointer>::as_ptr(self);
let str_obj = unsafe {
_pyo3::PyString::downcast_from_ptr(
py, _pyo3::ffi::PyObject_Str(ptr)).map_err(|_| std::fmt::Error)?
unsafe {
let repr = PyObject::from_borrowed_ptr(
py, _pyo3::ffi::PyObject_Str(ptr));
let result = {
let s = _pyo3::PyString::downcast_from(py, &repr);
let s = try!(s.map_err(|_| std::fmt::Error));
f.write_str(&s.to_string_lossy())
};
let result = f.write_str(&str_obj.to_string_lossy(py));
py.release(str_obj);
py.release(repr);
result
}
}
}
})
} else {
None
@ -184,6 +195,12 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
static mut TYPE_OBJECT: _pyo3::ffi::PyTypeObject = _pyo3::ffi::PyTypeObject_INIT;
unsafe { &mut TYPE_OBJECT }
}
#[inline]
fn is_instance(ptr: *mut ffi::PyObject) -> bool {
unsafe {ffi::PyObject_TypeCheck(
ptr, <#cls as _pyo3::typeob::PyTypeInfo>::type_object()) != 0}
}
}
impl _pyo3::typeob::PyTypeObject for #cls {

View File

@ -82,7 +82,7 @@ pub fn parse_args<'p>(py: Python<'p>,
if !accept_kwargs && used_keywords != nkeywords {
// check for extraneous keyword arguments
for (key, _value) in kwargs.unwrap().items(py) {
let key = try!(try!(key.cast_as::<PyString>(py)).to_string(py));
let key = try!(try!(key.cast_as::<PyString>(py)).to_string());
if !params.iter().any(|p| p.name == key) {
return Err(err::PyErr::new::<exc::TypeError, _>(
py,

View File

@ -27,6 +27,12 @@ pub unsafe fn PyString_Check(op : *mut PyObject) -> c_int {
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_STRING_SUBCLASS)
}
#[inline(always)]
pub unsafe fn PyBaseString_Check(op : *mut PyObject) -> c_int {
PyType_FastSubclass(
Py_TYPE(op), Py_TPFLAGS_STRING_SUBCLASS | Py_TPFLAGS_UNICODE_SUBCLASS)
}
#[inline(always)]
pub unsafe fn PyString_CheckExact(op : *mut PyObject) -> c_int {
let u : *mut PyTypeObject = &mut PyString_Type;

View File

@ -4,6 +4,7 @@ use std::fmt;
use pointers::PyPtr;
use python::Python;
use token::InstancePtr;
use objectprotocol::ObjectProtocol;
impl fmt::Debug for PyPtr {
@ -14,7 +15,7 @@ impl fmt::Debug for PyPtr {
// TODO: we shouldn't use fmt::Error when repr() fails
let r = self.as_object(py);
let repr_obj = try!(r.repr(py).map_err(|_| fmt::Error));
let result = f.write_str(&repr_obj.to_string_lossy(py));
let result = f.write_str(&repr_obj.as_ref(py).to_string_lossy());
py.release(repr_obj);
result
}
@ -28,7 +29,7 @@ impl fmt::Display for PyPtr {
// TODO: we shouldn't use fmt::Error when repr() fails
let r = self.as_object(py);
let str_obj = try!(r.str(py).map_err(|_| fmt::Error));
let result = f.write_str(&str_obj.to_string_lossy(py));
let result = f.write_str(&str_obj.as_ref(py).to_string_lossy());
py.release(str_obj);
result
}

View File

@ -10,6 +10,7 @@ use err::{PyErr, PyResult, self};
use python::{Python, PyDowncastInto, ToPyPointer};
use objects::{PyObject, PyDict, PyString, PyIterator, PyType};
use conversion::{ToPyObject, IntoPyTuple};
use token::Py;
pub trait ObjectProtocol {
@ -62,11 +63,11 @@ pub trait ObjectProtocol {
/// Compute the string representation of self.
/// This is equivalent to the Python expression 'repr(self)'.
fn repr(&self, py: Python) -> PyResult<PyString>;
fn repr(&self, py: Python) -> PyResult<Py<PyString>>;
/// Compute the string representation of self.
/// This is equivalent to the Python expression 'str(self)'.
fn str(&self, py: Python) -> PyResult<PyString>;
fn str(&self, py: Python) -> PyResult<Py<PyString>>;
/// Determines whether this object is callable.
fn is_callable(&self, py: Python) -> bool;
@ -239,16 +240,16 @@ impl<T> ObjectProtocol for T where T: ToPyPointer {
/// Compute the string representation of self.
/// This is equivalent to the Python expression 'repr(self)'.
#[inline]
fn repr(&self, py: Python) -> PyResult<PyString> {
Ok(PyString::downcast_from_ptr(
fn repr(&self, py: Python) -> PyResult<Py<PyString>> {
Ok(Py::downcast_from_ptr(
py, unsafe{ffi::PyObject_Repr(self.as_ptr())})?)
}
/// Compute the string representation of self.
/// This is equivalent to the Python expression 'str(self)'.
#[inline]
fn str(&self, py: Python) -> PyResult<PyString> {
Ok(PyString::downcast_from_ptr(
fn str(&self, py: Python) -> PyResult<Py<PyString>> {
Ok(Py::downcast_from_ptr(
py, unsafe{ffi::PyObject_Str(self.as_ptr())})?)
}

View File

@ -96,6 +96,12 @@ macro_rules! pyobject_nativetype(
fn type_object() -> &'static mut $crate::ffi::PyTypeObject {
unsafe { &mut $crate::ffi::$typeobject }
}
#[inline]
fn is_instance(ptr: *mut $crate::ffi::PyObject) -> bool {
unsafe {$crate::ffi::PyObject_TypeCheck(
ptr, <$name as $crate::typeob::PyTypeInfo>::type_object()) != 0}
}
}
impl $crate::typeob::PyTypeObject for $name {
@ -136,20 +142,26 @@ macro_rules! pyobject_nativetype(
fn fmt(&self, f: &mut $crate::std::fmt::Formatter)
-> Result<(), $crate::std::fmt::Error>
{
use $crate::python::PyDowncastInto;
use $crate::python::PyDowncastFrom;
let gil = $crate::Python::acquire_gil();
let py = gil.python();
let s = unsafe { $crate::PyString::downcast_from_ptr(
unsafe {
let repr_ob = PyObject::from_borrowed_ptr(
py, $crate::ffi::PyObject_Repr(
$crate::python::ToPyPointer::as_ptr(self))) };
let repr_obj = try!(s.map_err(|_| $crate::std::fmt::Error));
let result = f.write_str(&repr_obj.to_string_lossy(py));
py.release(repr_obj);
$crate::python::ToPyPointer::as_ptr(self)));
let result = {
let s = $crate::PyString::downcast_from(py, &repr_ob);
let s = try!(s.map_err(|_| $crate::std::fmt::Error));
f.write_str(&s.to_string_lossy())
};
py.release(repr_ob);
result
}
}
}
impl $crate::std::fmt::Display for $name {
fn fmt(&self, f: &mut $crate::std::fmt::Formatter)
@ -157,17 +169,23 @@ macro_rules! pyobject_nativetype(
{
let gil = $crate::Python::acquire_gil();
let py = gil.python();
use $crate::python::PyDowncastInto;
use $crate::python::PyDowncastFrom;
let s = unsafe { $crate::PyString::downcast_from_ptr(
unsafe {
let str_ob = PyObject::from_borrowed_ptr(
py, $crate::ffi::PyObject_Str(
$crate::python::ToPyPointer::as_ptr(self))) };
let str_obj = try!(s.map_err(|_| $crate::std::fmt::Error));
let result = f.write_str(&str_obj.to_string_lossy(py));
py.release(str_obj);
$crate::python::ToPyPointer::as_ptr(self)));
let result = {
let s = $crate::PyString::downcast_from(py, &str_ob);
let s = try!(s.map_err(|_| $crate::std::fmt::Error));
f.write_str(&s.to_string_lossy())
};
py.release(str_ob);
result
}
}
}
);
);
@ -261,6 +279,172 @@ macro_rules! pyobject_downcast(
);
);
macro_rules! pyobject_nativetype2(
($name: ident, $typeobject: ident, $checkfunction: ident) => {
impl $crate::token::PyNativeType for $name {}
impl $crate::std::convert::AsRef<PyObject> for $name {
fn as_ref(&self) -> &$crate::PyObject {
unsafe{$crate::std::mem::transmute(self)}
}
}
impl $crate::PyObjectWithToken for $name {
fn token<'p>(&'p self) -> $crate::Python<'p> {
unsafe { $crate::Python::assume_gil_acquired() }
}
}
//impl $crate::PyClone for $name {
// fn clone_ref(&self, _py: $crate::Python) -> Self {
// $name(unsafe{$crate::PyPtr::from_borrowed_ptr(self.as_ptr())})
// }
//}
impl $crate::python::ToPyPointer for $name {
/// Gets the underlying FFI pointer, returns a borrowed pointer.
#[inline]
fn as_ptr(&self) -> *mut $crate::ffi::PyObject {
self.0.as_ptr()
}
}
impl<'a> $crate::python::ToPyPointer for &'a $name {
/// Gets the underlying FFI pointer, returns a borrowed pointer.
#[inline]
fn as_ptr(&self) -> *mut $crate::ffi::PyObject {
self.0.as_ptr()
}
}
impl $crate::python::PyDowncastFrom for $name
{
fn downcast_from<'a, 'p>(py: $crate::Python<'p>, ob: &'a $crate::PyObject)
-> Result<&'a $name, $crate::PyDowncastError<'p>>
{
use $crate::ToPyPointer;
unsafe {
if $crate::ffi::$checkfunction(ob.as_ptr()) > 0 {
Ok($crate::std::mem::transmute(ob))
} else {
Err($crate::PyDowncastError(py, None))
}
}
}
}
impl<'a> $crate::FromPyObject<'a> for &'a $name
{
/// Extracts `Self` from the source `PyObject`.
fn extract(py: Python, ob: &'a $crate::PyObject) -> $crate::PyResult<Self>
{
unsafe {
if ffi::$checkfunction(ob.as_ptr()) != 0 {
Ok($crate::std::mem::transmute(ob))
} else {
Err($crate::PyDowncastError(py, None).into())
}
}
}
}
impl $crate::typeob::PyTypeInfo for $name {
type Type = ();
#[inline]
fn size() -> usize {
$crate::std::mem::size_of::<ffi::PyObject>()
}
#[inline]
fn offset() -> isize {
0
}
#[inline]
fn type_name() -> &'static str {
stringify!($name)
}
#[inline]
fn type_object() -> &'static mut $crate::ffi::PyTypeObject {
unsafe { &mut $crate::ffi::$typeobject }
}
#[inline]
fn is_instance(ptr: *mut $crate::ffi::PyObject) -> bool {
unsafe { $crate::ffi::$checkfunction(ptr) > 0 }
}
}
impl $crate::typeob::PyTypeObject for $name {
#[inline(always)]
fn init_type(_py: Python) {}
#[inline]
fn type_object(py: $crate::Python) -> $crate::PyType {
unsafe { $crate::PyType::from_type_ptr(py, &mut $crate::ffi::$typeobject) }
}
}
impl $crate::ToPyObject for $name
{
#[inline]
fn to_object<'p>(&self, py: $crate::Python<'p>) -> $crate::PyObject {
$crate::PyObject::from_borrowed_ptr(py, self.0.as_ptr())
}
#[inline]
fn with_borrowed_ptr<F, R>(&self, _py: $crate::Python, f: F) -> R
where F: FnOnce(*mut ffi::PyObject) -> R
{
f(self.0.as_ptr())
}
}
impl $crate::std::fmt::Debug for $name {
fn fmt(&self, f: &mut $crate::std::fmt::Formatter)
-> Result<(), $crate::std::fmt::Error>
{
use $crate::python::PyDowncastFrom;
let py = self.token();
unsafe {
let repr_ob = PyObject::from_borrowed_ptr(
py, $crate::ffi::PyObject_Repr(
$crate::python::ToPyPointer::as_ptr(self)));
let result = {
let s = $crate::PyString::downcast_from(py, &repr_ob);
let s = try!(s.map_err(|_| $crate::std::fmt::Error));
f.write_str(&s.to_string_lossy())
};
py.release(repr_ob);
result
}
}
}
impl $crate::std::fmt::Display for $name {
fn fmt(&self, f: &mut $crate::std::fmt::Formatter)
-> Result<(), $crate::std::fmt::Error>
{
use $crate::python::PyDowncastFrom;
let py = self.token();
unsafe {
let str_ob = PyObject::from_borrowed_ptr(
py, $crate::ffi::PyObject_Str(
$crate::python::ToPyPointer::as_ptr(self)));
let result = {
let s = $crate::PyString::downcast_from(py, &str_ob);
let s = try!(s.map_err(|_| $crate::std::fmt::Error));
f.write_str(&s.to_string_lossy())
};
py.release(str_ob);
result
}
}
}
};
);
macro_rules! pyobject_convert(
($name: ident) => (
impl $crate::std::convert::From<$name> for $crate::PyObject {

View File

@ -8,6 +8,7 @@ use std::borrow::Cow;
use std::os::raw::c_char;
use ffi;
use token::{Py, PyObjectWithToken};
use pointers::PyPtr;
use python::{ToPyPointer, Python};
use err::{PyResult, PyErr};
@ -16,8 +17,7 @@ use super::{PyObject, PyStringData};
/// Represents a Python string.
pub struct PyString(PyPtr);
pyobject_convert!(PyString);
pyobject_nativetype!(PyString, PyUnicode_Type, PyUnicode_Check);
pyobject_nativetype2!(PyString, PyUnicode_Type, PyUnicode_Check);
/// Represents a Python unicode string.
/// Corresponds to `unicode` in Python 2, and `str` in Python 3.
@ -25,45 +25,41 @@ pub use PyString as PyUnicode;
/// Represents a Python byte string.
pub struct PyBytes(PyPtr);
pyobject_convert!(PyBytes);
pyobject_nativetype!(PyBytes, PyBytes_Type, PyBytes_Check);
pyobject_nativetype2!(PyBytes, PyBytes_Type, PyBytes_Check);
impl PyString {
/// Creates a new Python string object.
///
/// Panics if out of memory.
pub fn new(_py: Python, s: &str) -> PyString {
pub fn new(_py: Python, s: &str) -> Py<PyString> {
let ptr = s.as_ptr() as *const c_char;
let len = s.len() as ffi::Py_ssize_t;
unsafe {
PyString(PyPtr::from_owned_ptr_or_panic(
ffi::PyUnicode_FromStringAndSize(ptr, len)))
Py::from_owned_ptr_or_panic(ffi::PyUnicode_FromStringAndSize(ptr, len))
}
}
pub fn from_object(py: Python, src: &PyObject, encoding: &str, errors: &str)
-> PyResult<PyString> {
-> PyResult<Py<PyString>> {
unsafe {
Ok(PyString(
PyPtr::from_owned_ptr_or_err(
Ok(Py::from_owned_ptr_or_err(
py, ffi::PyUnicode_FromEncodedObject(
src.as_ptr(),
encoding.as_ptr() as *const i8,
errors.as_ptr() as *const i8))?))
errors.as_ptr() as *const i8))?)
}
}
/// Gets the python string data in its underlying representation.
pub fn data(&self, py: Python) -> PyStringData {
pub fn data(&self) -> PyStringData {
// TODO: return the original representation instead
// of forcing the UTF-8 representation to be created.
unsafe {
let mut size : ffi::Py_ssize_t = mem::uninitialized();
let data = ffi::PyUnicode_AsUTF8AndSize(self.0.as_ptr(), &mut size) as *const u8;
if data.is_null() {
PyErr::fetch(py).print(py);
PyErr::fetch(self.token()).print(self.token());
panic!("PyUnicode_AsUTF8AndSize failed");
}
PyStringData::Utf8(std::slice::from_raw_parts(data, size as usize))
@ -74,16 +70,16 @@ impl PyString {
///
/// Returns a `UnicodeDecodeError` if the input is not valid unicode
/// (containing unpaired surrogates).
pub fn to_string(&self, py: Python) -> PyResult<Cow<str>> {
self.data(py).to_string(py)
pub fn to_string(&self) -> PyResult<Cow<str>> {
self.data().to_string(self.token())
}
/// Convert the `PyString` into a Rust string.
///
/// Unpaired surrogates invalid UTF-8 sequences are
/// replaced with U+FFFD REPLACEMENT CHARACTER.
pub fn to_string_lossy(&self, py: Python) -> Cow<str> {
self.data(py).to_string_lossy()
pub fn to_string_lossy(&self) -> Cow<str> {
self.data().to_string_lossy()
}
}
@ -92,12 +88,11 @@ impl PyBytes {
/// The byte string is initialized by copying the data from the `&[u8]`.
///
/// Panics if out of memory.
pub fn new(_py: Python, s: &[u8]) -> PyBytes {
pub fn new(_py: Python, s: &[u8]) -> Py<PyBytes> {
let ptr = s.as_ptr() as *const c_char;
let len = s.len() as ffi::Py_ssize_t;
unsafe {
PyBytes(PyPtr::from_owned_ptr_or_panic(
ffi::PyBytes_FromStringAndSize(ptr, len)))
Py::from_owned_ptr_or_panic(ffi::PyBytes_FromStringAndSize(ptr, len))
}
}

View File

@ -9,29 +9,27 @@ use std::ascii::AsciiExt;
use std::os::raw::c_char;
use ffi;
use conversion::FromPyObject;
use err::{PyResult, PyDowncastError};
use err::PyResult;
use pointers::PyPtr;
use python::{Python, ToPyPointer, IntoPyPointer, PyDowncastInto, PyDowncastFrom};
use token::{Py, PyObjectWithToken};
use python::{Python, ToPyPointer};
use super::{PyObject, PyStringData};
/// Represents a Python string.
pub struct PyString(PyPtr);
pyobject_convert!(PyString);
pyobject_nativetype!(PyString, PyBaseString_Type);
pyobject_nativetype2!(PyString, PyBaseString_Type, PyBaseString_Check);
/// Represents a Python unicode string.
pub struct PyUnicode(PyPtr);
pyobject_convert!(PyUnicode);
pyobject_nativetype!(PyUnicode, PyUnicode_Type, PyUnicode_Check);
pyobject_nativetype2!(PyUnicode, PyUnicode_Type, PyUnicode_Check);
/// Represents a Python byte string. Corresponds to `str` in Python 2
pub struct PyBytes(PyPtr);
pyobject_convert!(PyBytes);
pyobject_nativetype!(PyBytes, PyBaseString_Type, PyString_Check);
pyobject_nativetype2!(PyBytes, PyBaseString_Type, PyString_Check);
impl PyString {
/// Creates a new Python string object.
@ -41,21 +39,21 @@ impl PyString {
/// Use `PyUnicode::new()` to always create a unicode string.
///
/// Panics if out of memory.
pub fn new(py: Python, s: &str) -> PyString {
pub fn new(py: Python, s: &str) -> Py<PyString> {
if s.is_ascii() {
PyBytes::new(py, s.as_bytes()).into_basestring()
PyBytes::new(py, s.as_bytes()).into()
} else {
PyUnicode::new(py, s).into_basestring()
PyUnicode::new(py, s).into()
}
}
pub fn from_object(py: Python, src: &PyObject,
encoding: &str, errors: &str) -> PyResult<PyString> {
encoding: &str, errors: &str) -> PyResult<Py<PyString>> {
unsafe {
Ok(PyString(PyPtr::from_owned_ptr_or_err(
Ok(Py::from_owned_ptr_or_err(
py, ffi::PyUnicode_FromEncodedObject(
src.as_ptr(), encoding.as_ptr() as *const i8, errors.as_ptr() as *const i8))?
))
)
}
}
@ -64,12 +62,12 @@ impl PyString {
/// For Python 2 byte strings, this function always returns `PyStringData::Utf8`,
/// even if the bytes are not valid UTF-8.
/// For unicode strings, returns the underlying representation used by Python.
pub fn data(&self, py: Python) -> PyStringData {
pub fn data(&self) -> PyStringData {
let ob: &PyObject = self.as_ref();
if let Ok(bytes) = ob.cast_as::<PyBytes>(py) {
PyStringData::Utf8(bytes.data(py))
} else if let Ok(unicode) = ob.cast_as::<PyUnicode>(py) {
unicode.data(py)
if let Ok(bytes) = ob.cast_as::<PyBytes>(self.token()) {
PyStringData::Utf8(bytes.data())
} else if let Ok(unicode) = ob.cast_as::<PyUnicode>(self.token()) {
unicode.data()
} else {
panic!("PyString is neither `str` nor `unicode`")
}
@ -83,8 +81,8 @@ impl PyString {
/// Returns a `UnicodeDecodeError` if the input is not valid unicode
/// (containing unpaired surrogates, or a Python 2.7 byte string that is
/// not valid UTF-8).
pub fn to_string(&self, py: Python) -> PyResult<Cow<str>> {
self.data(py).to_string(py)
pub fn to_string(&self) -> PyResult<Cow<str>> {
self.data().to_string(self.token())
}
/// Convert the `PyString` into a Rust string.
@ -94,100 +92,8 @@ impl PyString {
///
/// Unpaired surrogates and (on Python 2.7) invalid UTF-8 sequences are
/// replaced with U+FFFD REPLACEMENT CHARACTER.
pub fn to_string_lossy(&self, py: Python) -> Cow<str> {
self.data(py).to_string_lossy()
}
#[inline]
pub fn is_base_string(ptr: *mut ffi::PyObject) -> bool {
unsafe {
ffi::PyType_FastSubclass(
ffi::Py_TYPE(ptr),
ffi::Py_TPFLAGS_STRING_SUBCLASS | ffi::Py_TPFLAGS_UNICODE_SUBCLASS) != 0
}
}
}
impl PyDowncastFrom for PyString
{
fn downcast_from<'a, 'p>(py: Python<'p>, ob: &'a PyObject)
-> Result<&'a PyString, PyDowncastError<'p>>
{
unsafe {
if PyString::is_base_string(ob.as_ptr()) {
let ptr = ob as *const _ as *mut u8 as *mut PyString;
Ok(ptr.as_ref().expect("Failed to call as_ref"))
} else {
Err(PyDowncastError(py, None))
}
}
}
}
impl PyDowncastInto for PyString
{
fn downcast_into<'p, I>(py: Python<'p>, ob: I) -> Result<Self, PyDowncastError<'p>>
where I: IntoPyPointer
{
unsafe{
let ptr = ob.into_ptr();
if PyString::is_base_string(ptr) {
Ok(PyString(PyPtr::from_owned_ptr(ptr)))
} else {
ffi::Py_DECREF(ptr);
Err(PyDowncastError(py, None))
}
}
}
fn downcast_from_ptr<'p>(py: Python<'p>, ptr: *mut ffi::PyObject)
-> Result<PyString, PyDowncastError<'p>>
{
unsafe{
if PyString::is_base_string(ptr) {
Ok(PyString(PyPtr::from_owned_ptr(ptr)))
} else {
ffi::Py_DECREF(ptr);
Err(PyDowncastError(py, None))
}
}
}
fn unchecked_downcast_into<'p, I>(ob: I) -> Self where I: IntoPyPointer
{
unsafe{
PyString(PyPtr::from_owned_ptr(ob.into_ptr()))
}
}
}
impl<'a> FromPyObject<'a> for PyString
{
/// Extracts `Self` from the source `PyObject`.
fn extract(py: Python, ob: &'a PyObject) -> PyResult<Self>
{
unsafe {
if PyString::is_base_string(ob.as_ptr()) {
Ok( PyString(PyPtr::from_borrowed_ptr(ob.as_ptr())) )
} else {
Err(PyDowncastError(py, None).into())
}
}
}
}
impl<'a> FromPyObject<'a> for &'a PyString
{
/// Extracts `Self` from the source `PyObject`.
fn extract(py: Python, ob: &'a PyObject) -> PyResult<Self>
{
unsafe {
if PyString::is_base_string(ob.as_ptr()) {
Ok(std::mem::transmute(ob))
} else {
Err(PyDowncastError(py, None).into())
}
}
pub fn to_string_lossy(&self) -> Cow<str> {
self.data().to_string_lossy()
}
}
@ -196,23 +102,16 @@ impl PyBytes {
/// The byte string is initialized by copying the data from the `&[u8]`.
///
/// Panics if out of memory.
pub fn new(_py: Python, s: &[u8]) -> PyBytes {
pub fn new(_py: Python, s: &[u8]) -> Py<PyBytes> {
let ptr = s.as_ptr() as *const c_char;
let len = s.len() as ffi::Py_ssize_t;
unsafe {
PyBytes(PyPtr::from_owned_ptr_or_panic(
ffi::PyBytes_FromStringAndSize(ptr, len)))
Py::from_owned_ptr_or_panic(ffi::PyBytes_FromStringAndSize(ptr, len))
}
}
/// Converts from `PyBytes` to `PyString`.
#[inline]
pub fn into_basestring(self) -> PyString {
<PyString as ::PyDowncastInto>::unchecked_downcast_into(self)
}
/// Gets the Python string data as byte slice.
pub fn data(&self, _py: Python) -> &[u8] {
pub fn data(&self) -> &[u8] {
unsafe {
let buffer = ffi::PyBytes_AsString(self.as_ptr()) as *const u8;
let length = ffi::PyBytes_Size(self.as_ptr()) as usize;
@ -226,36 +125,28 @@ impl PyUnicode {
/// Creates a new Python unicode string object.
///
/// Panics if out of memory.
pub fn new(_py: Python, s: &str) -> PyUnicode {
pub fn new(_py: Python, s: &str) -> Py<PyUnicode> {
let ptr = s.as_ptr() as *const c_char;
let len = s.len() as ffi::Py_ssize_t;
unsafe {
PyUnicode(PyPtr::from_owned_ptr_or_panic(
ffi::PyUnicode_FromStringAndSize(ptr, len)))
Py::from_owned_ptr_or_panic(ffi::PyUnicode_FromStringAndSize(ptr, len))
}
}
pub fn from_object(py: Python, src: &PyObject, encoding: &str, errors: &str)
-> PyResult<PyUnicode>
-> PyResult<Py<PyUnicode>>
{
unsafe {
Ok(PyUnicode(
PyPtr::from_owned_ptr_or_err(
Ok(Py::from_owned_ptr_or_err(
py, ffi::PyUnicode_FromEncodedObject(
src.as_ptr(),
encoding.as_ptr() as *const i8,
errors.as_ptr() as *const i8))?))
errors.as_ptr() as *const i8))?)
}
}
/// Converts from `PyUnicode` to `PyString`.
#[inline]
pub fn into_basestring(self) -> PyString {
<PyString as ::PyDowncastInto>::unchecked_downcast_into(self)
}
/// Gets the python string data in its underlying representation.
pub fn data(&self, _py: Python) -> PyStringData {
pub fn data(&self) -> PyStringData {
unsafe {
let buffer = ffi::PyUnicode_AS_UNICODE(self.as_ptr());
let length = ffi::PyUnicode_GET_SIZE(self.as_ptr()) as usize;
@ -267,18 +158,35 @@ impl PyUnicode {
///
/// Returns a `UnicodeDecodeError` if the input is not valid unicode
/// (containing unpaired surrogates).
pub fn to_string(&self, py: Python) -> PyResult<Cow<str>> {
self.data(py).to_string(py)
pub fn to_string(&self) -> PyResult<Cow<str>> {
self.data().to_string(self.token())
}
/// Convert the `PyString` into a Rust string.
///
/// Unpaired surrogates are replaced with U+FFFD REPLACEMENT CHARACTER.
pub fn to_string_lossy(&self, py: Python) -> Cow<str> {
self.data(py).to_string_lossy()
pub fn to_string_lossy(&self) -> Cow<str> {
self.data().to_string_lossy()
}
}
/// Converts from `PyBytes` to `PyString`.
impl std::convert::From<Py<PyBytes>> for Py<PyString> {
#[inline]
fn from(ob: Py<PyBytes>) -> Py<PyString> {
<Py<PyString> as ::PyDowncastInto>::unchecked_downcast_into(ob)
}
}
/// Converts from `PyUnicode` to `PyString`.
impl std::convert::From<Py<PyUnicode>> for Py<PyString> {
#[inline]
fn from(ob: Py<PyUnicode>) -> Py<PyString> {
<Py<PyString> as ::PyDowncastInto>::unchecked_downcast_into(ob)
}
}
#[cfg(test)]
mod test {
use python::Python;

View File

@ -53,7 +53,7 @@ impl<'a> IntoPyObject for &'a String {
/// Allows extracting strings from Python objects.
/// Accepts Python `str` and `unicode` objects.
pyobject_extract!(py, obj to Cow<'source, str> => {
try!(obj.cast_as::<PyString>(py)).to_string(py)
try!(obj.cast_as::<PyString>(py)).to_string()
});
@ -61,7 +61,7 @@ pyobject_extract!(py, obj to Cow<'source, str> => {
/// Accepts Python `str` and `unicode` objects.
pyobject_extract!(py, obj to String => {
let s = try!(obj.cast_as::<PyString>(py));
s.to_string(py).map(Cow::into_owned)
s.to_string().map(Cow::into_owned)
});

View File

@ -12,10 +12,12 @@ use python::{Python, IntoPyPointer, ToPyPointer, PyDowncastInto};
use typeob::{PyTypeInfo, PyObjectAlloc};
pub trait PyNativeType {}
pub struct PyToken(PhantomData<Rc<()>>);
impl PyToken {
pub fn token(&self) -> Python {
pub fn token<'p>(&'p self) -> Python<'p> {
unsafe { Python::assume_gil_acquired() }
}
}
@ -70,6 +72,7 @@ pub trait AsPyRef<T> : Sized {
}
/// Wrapper around unsafe `*mut ffi::PyObject` pointer. Decrement ref counter on `Drop`
#[derive(Debug)]
pub struct Py<T>(*mut ffi::PyObject, std::marker::PhantomData<T>);
// `PyPtr` is thread-safe, because any python related operations require a Python<'p> token.
@ -179,7 +182,7 @@ impl<T> Py<T> where T: PyTypeInfo,
impl<T> AsPyRef<T> for Py<T> where T: PyTypeInfo {
#[inline]
fn as_ref(&self, _py: Python) -> &T {
default fn as_ref(&self, _py: Python) -> &T {
let offset = <T as PyTypeInfo>::offset();
unsafe {
let ptr = (self.as_ptr() as *mut u8).offset(offset) as *mut T;
@ -187,7 +190,7 @@ impl<T> AsPyRef<T> for Py<T> where T: PyTypeInfo {
}
}
#[inline]
fn as_mut(&self, _py: Python) -> &mut T {
default fn as_mut(&self, _py: Python) -> &mut T {
let offset = <T as PyTypeInfo>::offset();
unsafe {
let ptr = (self.as_ptr() as *mut u8).offset(offset) as *mut T;
@ -196,6 +199,18 @@ impl<T> AsPyRef<T> for Py<T> where T: PyTypeInfo {
}
}
impl<T> InstancePtr<T> for Py<T> where T: PyTypeInfo + PyNativeType {
#[inline]
fn as_ref(&self, _py: Python) -> &T {
unsafe {std::mem::transmute(self)}
}
#[inline]
fn as_mut(&self, _py: Python) -> &mut T {
unsafe {std::mem::transmute(self as *const _ as *mut T)}
}
}
impl<T> ToPyObject for Py<T> {
fn to_object(&self, py: Python) -> PyObject {
PyObject::from_borrowed_ptr(py, self.as_ptr())
@ -250,6 +265,7 @@ impl<T> Drop for Py<T> {
impl<T> std::convert::From<Py<T>> for PyObject {
#[inline]
fn from(ob: Py<T>) -> Self {
unsafe {std::mem::transmute(ob)}
}
@ -294,11 +310,7 @@ impl<T> PyDowncastInto for Py<T> where T: PyTypeInfo
where I: IntoPyPointer
{
unsafe{
let ptr = ob.into_ptr();
let checked = ffi::PyObject_TypeCheck(
ptr, <T as PyTypeInfo>::type_object()) != 0;
if checked {
if T::is_instance(ptr) {
Ok(Py::from_owned_ptr(ptr))
} else {
ffi::Py_DECREF(ptr);
@ -311,8 +323,7 @@ impl<T> PyDowncastInto for Py<T> where T: PyTypeInfo
-> Result<Self, PyDowncastError<'p>>
{
unsafe{
let checked = ffi::PyObject_TypeCheck(ptr, <T as PyTypeInfo>::type_object()) != 0;
if checked {
if T::is_instance(ptr) {
Ok(Py::from_owned_ptr(ptr))
} else {
ffi::Py_DECREF(ptr);
@ -330,17 +341,13 @@ impl<T> PyDowncastInto for Py<T> where T: PyTypeInfo
}
}
impl<'a, T> ::FromPyObject<'a> for Py<T> where T: PyTypeInfo
{
/// Extracts `Self` from the source `PyObject`.
fn extract(py: Python, ob: &'a PyObject) -> PyResult<Self>
{
unsafe {
let checked = ffi::PyObject_TypeCheck(
ob.as_ptr(), <T as PyTypeInfo>::type_object()) != 0;
if checked {
if T::is_instance(ob.as_ptr()) {
Ok( Py::from_borrowed_ptr(ob.as_ptr()) )
} else {
Err(::PyDowncastError(py, None).into())

View File

@ -35,6 +35,9 @@ pub trait PyTypeInfo {
/// PyTypeObject instance for this type
fn type_object() -> &'static mut ffi::PyTypeObject;
/// Check `*mut ffi::PyObject` if it is the same type
fn is_instance(ptr: *mut ffi::PyObject) -> bool;
}
@ -60,6 +63,12 @@ impl<'a, T: ?Sized> PyTypeInfo for &'a T where T: PyTypeInfo {
default fn type_object() -> &'static mut ffi::PyTypeObject {
<T as PyTypeInfo>::type_object()
}
#[inline]
default fn is_instance(ptr: *mut ffi::PyObject) -> bool {
<T as PyTypeInfo>::is_instance(ptr)
}
}
pub trait PyObjectAlloc<T> {

View File

@ -305,7 +305,7 @@ struct ClassMethodWithArgs{token: PyToken}
#[py::methods]
impl ClassMethodWithArgs {
#[classmethod]
fn method(cls: &PyType, py: Python, input: PyString) -> PyResult<String> {
fn method(cls: &PyType, py: Python, input: &PyString) -> PyResult<String> {
Ok(format!("{}.method({})", cls.name(py), input))
}
}
@ -483,12 +483,12 @@ impl<'p> PyObjectProtocol<'p> for StringMethods {
Ok(format!("format({})", format_spec))
}
fn __unicode__(&self, py: Python) -> PyResult<PyString> {
Ok(PyString::new(py, "unicode"))
fn __unicode__(&self, py: Python) -> PyResult<PyObject> {
Ok(PyString::new(py, "unicode").into())
}
fn __bytes__(&self, py: Python) -> PyResult<PyBytes> {
Ok(PyBytes::new(py, b"bytes"))
fn __bytes__(&self, py: Python) -> PyResult<PyObject> {
Ok(PyBytes::new(py, b"bytes").into())
}
}