diff --git a/src/conversion.rs b/src/conversion.rs index 3586ec35..e420fa66 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -2,7 +2,7 @@ use libc::c_char; use std; use ffi; use python::{Python, PythonObject, PythonObjectWithCheckedDowncast}; -use objects::PyObject; +use objects::{PyObject, PyBool}; use err::{self, PyErr, PyResult}; use pyptr::{PyPtr, PythonPointer}; @@ -111,30 +111,25 @@ impl <'p, 's, T> FromPyObject<'p, 's> for PyPtr<'p, T> where T: PythonObjectWith impl <'p> ToPyObject<'p> for bool { - type ObjectType = PyObject<'p>; + type ObjectType = PyBool<'p>; #[inline] - fn to_py_object(self, py: Python<'p>) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> { - Ok(PyPtr::new(if self { py.True() } else { py.False() })) + fn to_py_object(self, py: Python<'p>) -> PyResult<'p, PyPtr<'p, PyBool<'p>>> { + Ok(PyPtr::new(PyBool::get(py, self))) } #[inline] - fn with_py_object(self, py: Python<'p>, f: F) -> PyResult<'p, R> where F: FnOnce(&PyObject) -> PyResult<'p, R> { + fn with_py_object(self, py: Python<'p>, f: F) -> PyResult<'p, R> + where F: FnOnce(&PyBool) -> PyResult<'p, R> + { // Avoid unnecessary Py_INCREF/Py_DECREF pair - f(if self { py.True() } else { py.False() }) + f(PyBool::get(py, self)) } } impl <'p, 'a> FromPyObject<'p, 'a> for bool { fn from_py_object(s: &'a PyObject<'p>) -> PyResult<'p, bool> { - let py = s.python(); - if s == py.True() { - Ok(true) - } else if s == py.False() { - Ok(false) - } else { - unimplemented!() - } + Ok(try!(s.downcast::()).is_true()) } } diff --git a/src/err.rs b/src/err.rs index 60fe0a4d..866aaf71 100644 --- a/src/err.rs +++ b/src/err.rs @@ -82,16 +82,10 @@ impl <'p> PyErr<'p> { unsafe { ffi::PyErr_PrintEx(1) } } - /// Print a warning message to sys.stderr when an exception has been set but it is impossible for the interpreter to actually raise the exception. - /// It is used, for example, when an exception occurs in an __del__() method.. - pub fn write_unraisable(self, context: &PyObject<'p>) { - self.restore(); - unsafe { ffi::PyErr_WriteUnraisable(context.as_ptr()) } - } - /// Return true if the current exception matches the exception in `exc`. /// If `exc` is a class object, this also returns `true` when `self` is an instance of a subclass. /// If `exc` is a tuple, all exceptions in the tuple (and recursively in subtuples) are searched for a match. + #[inline] pub fn matches(&self, exc: &PyObject) -> bool { unsafe { ffi::PyErr_GivenExceptionMatches(self.ptype.as_ptr(), exc.as_ptr()) != 0 } } @@ -120,8 +114,22 @@ impl <'p> PyErr<'p> { } } + /// Retrieves the exception type. + /// If the exception type is an old-style class, returns oldstyle::PyClass. + pub fn get_type(&self) -> &PyType<'p> { + match self.ptype.downcast::() { + Ok(t) => t, + Err(_) => unimplemented!() + /* match self.ptype.downcast::() { + Ok(_) => py.get_type::(), + Err(_) => py.get_type::() + }*/ + } + } + /// Retrieves the exception instance for this error. - /// This method takes &mut self because the error might need to be normalized in order to create the exception instance. + /// This method takes &mut self because the error might need + /// to be normalized in order to create the exception instance. pub fn instance(&mut self) -> &PyObject<'p> { self.normalize(); match self.pvalue { diff --git a/src/objects/mod.rs b/src/objects/mod.rs index aa7031a5..1010ec63 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -3,6 +3,10 @@ pub use self::typeobject::PyType; pub use self::module::PyModule; pub use self::string::{PyBytes, PyUnicode}; pub use self::iterator::PyIterator; +use python::{Python, PythonObject}; +use pyptr::{PyPtr, PythonPointer}; +use err::{PyErr, PyResult}; +use ffi::{Py_ssize_t}; macro_rules! pythonobject_newtype_only_pythonobject( ($name: ident) => ( @@ -55,7 +59,67 @@ mod string; mod dict; mod iterator; -pyobject_newtype!(PyTuple, PyTuple_Check, PyTuple_Type); pyobject_newtype!(PyList, PyList_Check, PyList_Type); +pyobject_newtype!(PyBool, PyBool_Check, PyBool_Type); + +impl <'p> PyBool<'p> { + #[inline] + pub fn get(py: Python<'p>, val: bool) -> &'p PyBool<'p> { + if val { py.True() } else { py.False() } + } + + #[inline] + pub fn is_true(&self) -> bool { + self.as_ptr() == unsafe { ::ffi::Py_True() } + } +} + +pyobject_newtype!(PyTuple, PyTuple_Check, PyTuple_Type); + +impl <'p> PyTuple<'p> { + pub fn new(py: Python<'p>, elements: &[&PyObject<'p>]) -> PyResult<'p, PyPtr<'p, PyTuple<'p>>> { + unsafe { + let len = elements.len(); + let ptr = ::ffi::PyTuple_New(len as Py_ssize_t); + let t = try!(::err::result_from_owned_ptr(py, ptr)).unchecked_downcast_into::(); + for (i, e) in elements.iter().enumerate() { + ::ffi::PyTuple_SET_ITEM(ptr, i as Py_ssize_t, e.steal_ptr()); + } + Ok(t) + } + } + + #[inline] + pub fn len(&self) -> uint { + // non-negative Py_ssize_t should always fit into Rust uint + unsafe { + ::ffi::PyTuple_GET_SIZE(self.as_ptr()) as uint + } + } + + #[inline] + pub fn as_slice<'a>(&'a self) -> &'a [&'a PyObject<'p>] { + // This is safe because &PyObject has the same memory layout as *mut ffi::PyObject, + // and because tuples are immutable. + unsafe { + let ptr = self.as_ptr() as *mut ::ffi::PyTupleObject; + ::std::mem::transmute(::std::raw::Slice { + data: (*ptr).ob_item.as_ptr(), + len: self.len() + }) + } + } +} + +impl<'p> ::std::ops::Index for PyTuple<'p> { + type Output = PyObject<'p>; + + #[inline] + fn index<'a>(&'a self, index: &uint) -> &'a PyObject<'p> { + // use as_slice() to use the normal Rust bounds checking when indexing + self.as_slice()[*index] + } +} + diff --git a/src/objects/typeobject.rs b/src/objects/typeobject.rs index 1f5303a1..729f0368 100644 --- a/src/objects/typeobject.rs +++ b/src/objects/typeobject.rs @@ -83,3 +83,11 @@ impl <'p> PyType<'p> { } } +impl <'p> PartialEq for PyType<'p> { + #[inline] + fn eq(&self, o : &PyType<'p>) -> bool { + self.as_type_ptr() == o.as_type_ptr() + } +} +impl <'p> Eq for PyType<'p> { } + diff --git a/src/pyptr.rs b/src/pyptr.rs index a9c11735..619f5cef 100644 --- a/src/pyptr.rs +++ b/src/pyptr.rs @@ -176,5 +176,10 @@ impl<'p> PyPtr<'p, PyObject<'p>> { // TODO: avoid unnecessary IncRef/DefRef self.deref().downcast().map(PyPtr::new) } + + pub unsafe fn unchecked_downcast_into(self) -> PyPtr<'p, T> where T: PythonObject<'p> { + // TODO: avoid unnecessary IncRef/DefRef + PyPtr::new(PythonObject::unchecked_downcast_from(self.deref())) + } } diff --git a/src/python.rs b/src/python.rs index 3ae2556b..4637ae5d 100644 --- a/src/python.rs +++ b/src/python.rs @@ -2,7 +2,7 @@ use std; use std::kinds::marker::{NoSend, InvariantLifetime}; use std::ptr; use ffi; -use objects::{PyObject, PyType}; +use objects::{PyObject, PyType, PyBool}; use pythonrun::GILGuard; /// The 'Python' struct is a zero-size marker struct that is required for most python operations. @@ -89,15 +89,15 @@ impl<'p> Python<'p> { /// Retrieves a reference to the 'True' constant value. #[allow(non_snake_case)] // the python keyword starts with uppercase #[inline] - pub fn True(self) -> &'p PyObject<'p> { - unsafe { PyObject::from_ptr(self, ffi::Py_True()) } + pub fn True(self) -> &'p PyBool<'p> { + unsafe { PythonObject::unchecked_downcast_from(PyObject::from_ptr(self, ffi::Py_True())) } } /// Retrieves a reference to the 'False' constant value. #[allow(non_snake_case)] // the python keyword starts with uppercase #[inline] - pub fn False(self) -> &'p PyObject<'p> { - unsafe { PyObject::from_ptr(self, ffi::Py_False()) } + pub fn False(self) -> &'p PyBool<'p> { + unsafe { PythonObject::unchecked_downcast_from(PyObject::from_ptr(self, ffi::Py_False())) } } /// Retrieves a reference to the type object for type T.