From bc0f9b014ff680b5c936a23c5b98a4910826085c Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Tue, 20 Jun 2017 11:59:07 -0700 Subject: [PATCH] convert PyString object --- pyo3cls/src/py_class.rs | 45 +++++--- src/argparse.rs | 2 +- src/ffi2/stringobject.rs | 6 ++ src/fmt.rs | 5 +- src/objectprotocol.rs | 13 +-- src/objects/mod.rs | 216 ++++++++++++++++++++++++++++++++++--- src/objects/string.rs | 43 ++++---- src/objects/string2.rs | 202 ++++++++++------------------------ src/objects/stringutils.rs | 4 +- src/token.rs | 39 ++++--- src/typeob.rs | 9 ++ tests/test_class.rs | 10 +- 12 files changed, 361 insertions(+), 233 deletions(-) diff --git a/pyo3cls/src/py_class.rs b/pyo3cls/src/py_class.rs index 552ed82a..b6933fe5 100644 --- a/pyo3cls/src/py_class.rs +++ b/pyo3cls/src/py_class.rs @@ -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,13 +101,18 @@ 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)? - }; - let result = f.write_str(&repr.to_string_lossy(py)); - py.release(repr); - result + 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()) + }; + py.release(repr); + result + } } } @@ -114,13 +120,18 @@ 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 str_obj = unsafe { - _pyo3::PyString::downcast_from_ptr( - py, _pyo3::ffi::PyObject_Str(ptr)).map_err(|_| std::fmt::Error)? - }; - let result = f.write_str(&str_obj.to_string_lossy(py)); - py.release(str_obj); - result + 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()) + }; + py.release(repr); + result + } } } }) @@ -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 { diff --git a/src/argparse.rs b/src/argparse.rs index efed25b9..7e78f78a 100644 --- a/src/argparse.rs +++ b/src/argparse.rs @@ -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::(py)).to_string(py)); + let key = try!(try!(key.cast_as::(py)).to_string()); if !params.iter().any(|p| p.name == key) { return Err(err::PyErr::new::( py, diff --git a/src/ffi2/stringobject.rs b/src/ffi2/stringobject.rs index 195dbdfc..a54a816f 100644 --- a/src/ffi2/stringobject.rs +++ b/src/ffi2/stringobject.rs @@ -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; diff --git a/src/fmt.rs b/src/fmt.rs index 9fdc3020..4fac908f 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -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 } diff --git a/src/objectprotocol.rs b/src/objectprotocol.rs index a06963f8..a9bccd04 100644 --- a/src/objectprotocol.rs +++ b/src/objectprotocol.rs @@ -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; + fn repr(&self, py: Python) -> PyResult>; /// Compute the string representation of self. /// This is equivalent to the Python expression 'str(self)'. - fn str(&self, py: Python) -> PyResult; + fn str(&self, py: Python) -> PyResult>; /// Determines whether this object is callable. fn is_callable(&self, py: Python) -> bool; @@ -239,16 +240,16 @@ impl 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 { - Ok(PyString::downcast_from_ptr( + fn repr(&self, py: Python) -> PyResult> { + 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 { - Ok(PyString::downcast_from_ptr( + fn str(&self, py: Python) -> PyResult> { + Ok(Py::downcast_from_ptr( py, unsafe{ffi::PyObject_Str(self.as_ptr())})?) } diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 79e26163..3a83aff8 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -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,18 +142,24 @@ 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( - 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); - result + 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 + } } } @@ -157,15 +169,21 @@ 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( - 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); - result + 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 + } } } ); @@ -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 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 + { + 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::() + } + #[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(&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 { diff --git a/src/objects/string.rs b/src/objects/string.rs index 357ca33a..a1c485e6 100644 --- a/src/objects/string.rs +++ b/src/objects/string.rs @@ -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 { 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 { + -> PyResult> { unsafe { - Ok(PyString( - PyPtr::from_owned_ptr_or_err( - py, ffi::PyUnicode_FromEncodedObject( - src.as_ptr(), - encoding.as_ptr() as *const i8, - errors.as_ptr() as *const i8))?)) + 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))?) } } /// 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> { - self.data(py).to_string(py) + pub fn to_string(&self) -> PyResult> { + 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 { - self.data(py).to_string_lossy() + pub fn to_string_lossy(&self) -> Cow { + 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 { 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)) } } diff --git a/src/objects/string2.rs b/src/objects/string2.rs index aa126279..16629df1 100644 --- a/src/objects/string2.rs +++ b/src/objects/string2.rs @@ -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 { 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 { + encoding: &str, errors: &str) -> PyResult> { 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::(py) { - PyStringData::Utf8(bytes.data(py)) - } else if let Ok(unicode) = ob.cast_as::(py) { - unicode.data(py) + if let Ok(bytes) = ob.cast_as::(self.token()) { + PyStringData::Utf8(bytes.data()) + } else if let Ok(unicode) = ob.cast_as::(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> { - self.data(py).to_string(py) + pub fn to_string(&self) -> PyResult> { + 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 { - 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> - 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> - { - 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 - { - 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 - { - 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 { + 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 { 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 { - ::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 { 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 + -> PyResult> { unsafe { - Ok(PyUnicode( - PyPtr::from_owned_ptr_or_err( - py, ffi::PyUnicode_FromEncodedObject( - src.as_ptr(), - encoding.as_ptr() as *const i8, - errors.as_ptr() as *const i8))?)) + 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))?) } } - /// Converts from `PyUnicode` to `PyString`. - #[inline] - pub fn into_basestring(self) -> PyString { - ::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> { - self.data(py).to_string(py) + pub fn to_string(&self) -> PyResult> { + 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 { - self.data(py).to_string_lossy() + pub fn to_string_lossy(&self) -> Cow { + self.data().to_string_lossy() } } +/// Converts from `PyBytes` to `PyString`. +impl std::convert::From> for Py { + #[inline] + fn from(ob: Py) -> Py { + as ::PyDowncastInto>::unchecked_downcast_into(ob) + } +} + +/// Converts from `PyUnicode` to `PyString`. +impl std::convert::From> for Py { + #[inline] + fn from(ob: Py) -> Py { + as ::PyDowncastInto>::unchecked_downcast_into(ob) + } +} + + #[cfg(test)] mod test { use python::Python; diff --git a/src/objects/stringutils.rs b/src/objects/stringutils.rs index 2bd0fe98..8adc7ce0 100644 --- a/src/objects/stringutils.rs +++ b/src/objects/stringutils.rs @@ -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::(py)).to_string(py) + try!(obj.cast_as::(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::(py)); - s.to_string(py).map(Cow::into_owned) + s.to_string().map(Cow::into_owned) }); diff --git a/src/token.rs b/src/token.rs index 6df66445..c15a152a 100644 --- a/src/token.rs +++ b/src/token.rs @@ -12,10 +12,12 @@ use python::{Python, IntoPyPointer, ToPyPointer, PyDowncastInto}; use typeob::{PyTypeInfo, PyObjectAlloc}; +pub trait PyNativeType {} + pub struct PyToken(PhantomData>); 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 : Sized { } /// Wrapper around unsafe `*mut ffi::PyObject` pointer. Decrement ref counter on `Drop` +#[derive(Debug)] pub struct Py(*mut ffi::PyObject, std::marker::PhantomData); // `PyPtr` is thread-safe, because any python related operations require a Python<'p> token. @@ -179,7 +182,7 @@ impl Py where T: PyTypeInfo, impl AsPyRef for Py where T: PyTypeInfo { #[inline] - fn as_ref(&self, _py: Python) -> &T { + default fn as_ref(&self, _py: Python) -> &T { let offset = ::offset(); unsafe { let ptr = (self.as_ptr() as *mut u8).offset(offset) as *mut T; @@ -187,7 +190,7 @@ impl AsPyRef for Py where T: PyTypeInfo { } } #[inline] - fn as_mut(&self, _py: Python) -> &mut T { + default fn as_mut(&self, _py: Python) -> &mut T { let offset = ::offset(); unsafe { let ptr = (self.as_ptr() as *mut u8).offset(offset) as *mut T; @@ -196,6 +199,18 @@ impl AsPyRef for Py where T: PyTypeInfo { } } +impl InstancePtr for Py 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 ToPyObject for Py { fn to_object(&self, py: Python) -> PyObject { PyObject::from_borrowed_ptr(py, self.as_ptr()) @@ -250,8 +265,9 @@ impl Drop for Py { impl std::convert::From> for PyObject { + #[inline] fn from(ob: Py) -> Self { - unsafe{std::mem::transmute(ob)} + unsafe {std::mem::transmute(ob)} } } @@ -294,11 +310,7 @@ impl PyDowncastInto for Py where T: PyTypeInfo where I: IntoPyPointer { unsafe{ - let ptr = ob.into_ptr(); - let checked = ffi::PyObject_TypeCheck( - ptr, ::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 PyDowncastInto for Py where T: PyTypeInfo -> Result> { unsafe{ - let checked = ffi::PyObject_TypeCheck(ptr, ::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 PyDowncastInto for Py where T: PyTypeInfo } } - impl<'a, T> ::FromPyObject<'a> for Py where T: PyTypeInfo { /// Extracts `Self` from the source `PyObject`. fn extract(py: Python, ob: &'a PyObject) -> PyResult { unsafe { - let checked = ffi::PyObject_TypeCheck( - ob.as_ptr(), ::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()) diff --git a/src/typeob.rs b/src/typeob.rs index c11f4b17..871802df 100644 --- a/src/typeob.rs +++ b/src/typeob.rs @@ -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 { ::type_object() } + + #[inline] + default fn is_instance(ptr: *mut ffi::PyObject) -> bool { + ::is_instance(ptr) + } + } pub trait PyObjectAlloc { diff --git a/tests/test_class.rs b/tests/test_class.rs index f40389c3..13e708fe 100644 --- a/tests/test_class.rs +++ b/tests/test_class.rs @@ -305,7 +305,7 @@ struct ClassMethodWithArgs{token: PyToken} #[py::methods] impl ClassMethodWithArgs { #[classmethod] - fn method(cls: &PyType, py: Python, input: PyString) -> PyResult { + fn method(cls: &PyType, py: Python, input: &PyString) -> PyResult { 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 { - Ok(PyString::new(py, "unicode")) + fn __unicode__(&self, py: Python) -> PyResult { + Ok(PyString::new(py, "unicode").into()) } - fn __bytes__(&self, py: Python) -> PyResult { - Ok(PyBytes::new(py, b"bytes")) + fn __bytes__(&self, py: Python) -> PyResult { + Ok(PyBytes::new(py, b"bytes").into()) } }