diff --git a/src/conversion.rs b/src/conversion.rs index 2fb1fd83..c4c76bd1 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -1,8 +1,9 @@ use libc::c_char; use std; use ffi; -use err; -use {Python, PyObject, PyResult, PythonObject, PythonObjectDowncast, PyErr}; +use python::{Python, PythonObject, PythonObjectWithCheckedDowncast}; +use object::PyObject; +use err::{self, PyErr, PyResult}; use pyptr::{PyPtr, PythonPointer}; /// ToPyObject is implemented for types that can be converted into a python object. @@ -24,12 +25,14 @@ use pyptr::{PyPtr, PythonPointer}; /// m1(*try!(i.to_py_object(py))) /// m2(i) pub trait ToPyObject<'p, 's> for Sized? { + type ResultType : PythonObject<'p> = PyObject<'p>; + // The returned pointer type is flexible: // it can be either &PyObject or PyPtr, depending on whether // the conversion is allocating a new object. // This lets us avoid a useless IncRef/DecRef pair - type PointerType : PythonPointer + std::ops::Deref //> - = PyPtr<'p, PyObject<'p>>; + type PointerType : PythonPointer + std::ops::Deref // + = PyPtr<'p, Self::ResultType>; fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, Self::PointerType>; @@ -59,6 +62,10 @@ pub trait ToPyObject<'p, 's> for Sized? { // Note that the 'PointerType' associated type is essential to avoid unnecessarily // touching the reference count in cases (2) and (4). + + // Btw: I'm not sure if this type crazyness is worth it. + // If rust had automatic re-borrowing to avoid the '&*p' dance when using a PyPtr as &PyObject, + // we should probably just force the user to manually call .to_py() } /// FromPyObject is implemented by various types that can be extracted from a python object. @@ -72,15 +79,16 @@ pub trait FromPyObject<'p, 's> { // convertible to a python object. impl <'p, 's, T> ToPyObject<'p, 's> for T where T : PythonObject<'p> { - type PointerType = &'s PyObject<'p>; + type ResultType = T; + type PointerType = &'s T; #[inline] - fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, &'s PyObject<'p>> { - Ok(self.as_object()) + fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, &'s T> { + Ok(self) } } -impl <'p, 's, T> FromPyObject<'p, 's> for &'s T where T: PythonObjectDowncast<'p> { +impl <'p, 's, T> FromPyObject<'p, 's> for &'s T where T: PythonObjectWithCheckedDowncast<'p> { #[inline] fn from_py_object(s: &'s PyObject<'p>) -> PyResult<'p, &'s T> { s.downcast() @@ -93,15 +101,16 @@ impl <'p, 's, T> FromPyObject<'p, 's> for &'s T where T: PythonObjectDowncast<'p // convertible to a python object, without having to re-borrow the &PyObject. impl <'p, 's, T> ToPyObject<'p, 's> for PyPtr<'p, T> where T: PythonObject<'p> { - type PointerType = &'s PyObject<'p>; + type ResultType = T; + type PointerType = &'s T; #[inline] - fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, &'s PyObject<'p>> { - Ok(self.as_object()) + fn to_py_object(&'s self, py: Python<'p>) -> PyResult<'p, &'s T> { + Ok(&**self) } } -impl <'p, 's, T> FromPyObject<'p, 's> for PyPtr<'p, T> where T: PythonObjectDowncast<'p> { +impl <'p, 's, T> FromPyObject<'p, 's> for PyPtr<'p, T> where T: PythonObjectWithCheckedDowncast<'p> { #[inline] fn from_py_object(s : &'s PyObject<'p>) -> PyResult<'p, PyPtr<'p, T>> { PyPtr::new(s).downcast_into() @@ -112,6 +121,7 @@ impl <'p, 's, T> FromPyObject<'p, 's> for PyPtr<'p, T> where T: PythonObjectDown impl <'p, 's> ToPyObject<'p, 's> for bool { + type ResultType = PyObject<'p>; type PointerType = &'p PyObject<'p>; #[inline] @@ -137,6 +147,7 @@ impl <'p, 'a> FromPyObject<'p, 'a> for bool { // When converting strings to/from python, we need to copy the string data. // This means we can implement ToPyObject for str, but FromPyObject only for String. impl <'p, 's> ToPyObject<'p, 's> for str { + type ResultType = PyObject<'p>; type PointerType = PyPtr<'p, PyObject<'p>>; fn to_py_object(&'s self, py : Python<'p>) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> { diff --git a/src/dict.rs b/src/dict.rs new file mode 100644 index 00000000..c3865613 --- /dev/null +++ b/src/dict.rs @@ -0,0 +1,46 @@ +use std; +use ffi; +use python::{Python, PythonObject, PythonObjectDowncast}; +use object::PyObject; +use typeobject::PyType; +use pyptr::PyPtr; +use err::{self, PyResult}; + +pub struct PyDict<'p>(PyObject<'p>); + +impl <'p> PythonObject<'p> for PyDict<'p> { + #[inline] + fn as_object<'a>(&'a self) -> &'a PyObject<'p> { + &self.0 + } + + #[inline] + unsafe fn unchecked_downcast_from<'a>(obj: &'a PyObject<'p>) -> &'a PyDict<'p> { + std::mem::transmute(obj) + } +} + +impl <'p> PythonObjectDowncast<'p> for PyDict<'p> { + #[inline] + fn downcast_from<'a>(obj : &'a PyObject<'p>) -> Option<&'a PyDict<'p>> { + unsafe { + if ffi::PyDict_Check(obj.as_ptr()) { + Some(std::mem::transmute(obj)) + } else { + None + } + } + } + + #[inline] + fn type_object(py: Python<'p>, _ : Option<&Self>) -> &'p PyType<'p> { + unsafe { PyType::from_type_ptr(py, &mut ffi::PyDict_Type) } + } +} + +impl <'p> PyDict<'p> { + fn new(py: Python<'p>) -> PyResult<'p, PyPtr<'p, PyDict<'p>>> { + unimplemented!() + } +} + diff --git a/src/err.rs b/src/err.rs index 5948e3a1..87e0e88c 100644 --- a/src/err.rs +++ b/src/err.rs @@ -1,15 +1,22 @@ use std; -use {PyObject, PythonObject, PyType, Python, PyPtr}; -use pyptr::PythonPointer; +use {PyObject, PythonObject, PyType, Python}; +use pyptr::{PyPtr, PythonPointer}; use ffi; use libc; +use conversion::ToPyObject; /// Represents a python exception that was raised. #[derive(Clone, Show)] pub struct PyErr<'p> { - ptype : Option>>, - pvalue : Option>>, - ptraceback : Option>> + /// Gets the type of the exception. This should be either a PyClass or a PyType. + pub ptype : PyPtr<'p, PyObject<'p>>, + /// Gets the value of the exception. + /// This can be either an instance of ptype, + /// a tuple of arguments to be passed to ptype's constructor, + /// or a single argument to be passed to ptype's constructor. + /// Call PyErr::instance() to get the exception instance in all cases. + pub pvalue : Option>>, + pub ptraceback : Option>> // is actually a PyTraceBack } @@ -19,23 +26,106 @@ pub type PyPtrResult<'p, T> = PyResult<'p, PyPtr<'p, T>>; impl <'p> PyErr<'p> { /// Gets whether an error is present in the python interpreter's global state. + #[inline] pub fn occurred(_ : Python<'p>) -> bool { unsafe { !ffi::PyErr_Occurred().is_null() } } /// Retrieves the current error from the python interpreter's global state. /// The error is cleared from the python interpreter. + /// If no error is set, returns a RuntimeError. pub fn fetch(py : Python<'p>) -> PyErr<'p> { unsafe { let mut ptype : *mut ffi::PyObject = std::mem::uninitialized(); let mut pvalue : *mut ffi::PyObject = std::mem::uninitialized(); let mut ptraceback : *mut ffi::PyObject = std::mem::uninitialized(); ffi::PyErr_Fetch(&mut ptype, &mut pvalue, &mut ptraceback); - PyErr { - ptype: PyPtr::from_owned_ptr_opt(py, ptype), - pvalue: PyPtr::from_owned_ptr_opt(py, pvalue), - ptraceback: PyPtr::from_owned_ptr_opt(py, pvalue) - } + PyErr::new_from_ffi_tuple(py, ptype, pvalue, ptraceback) + } + } + + unsafe fn new_from_ffi_tuple(py: Python<'p>, ptype: *mut ffi::PyObject, pvalue: *mut ffi::PyObject, ptraceback: *mut ffi::PyObject) -> PyErr<'p> { + // Note: must not panic to ensure all owned pointers get acquired correctly, + // and because we mustn't panic in normalize(). + PyErr { + ptype: if ptype.is_null() { + unimplemented!() + } else { + PyPtr::from_owned_ptr(py, ptype) + }, + pvalue: PyPtr::from_owned_ptr_opt(py, pvalue), + ptraceback: PyPtr::from_owned_ptr_opt(py, ptraceback) + } + } + + /// Construct a new error. + /// `exc` is the exception type; usually one of the standard exceptions like `PyExc::runtime_error()`. + /// `value` is the exception instance, or a tuple of arguments to pass to the exception constructor + pub fn new(exc: &PyObject<'p>, value: Option>>) -> PyErr<'p> { + PyErr { + ptype: PyPtr::new(exc), + pvalue: value, + ptraceback: None + } + } + + /// Print a standard traceback to sys.stderr. + pub fn print(self) { + self.restore(); + unsafe { ffi::PyErr_PrintEx(0) } + } + + /// Print a standard traceback to sys.stderr. + pub fn print_and_set_sys_last_vars(self) { + self.restore(); + 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. + pub fn matches(&self, exc: &PyObject) -> bool { + unsafe { ffi::PyErr_GivenExceptionMatches(self.ptype.as_ptr(), exc.as_ptr()) != 0 } + } + + /// Normalizes the error. This ensures that the exception value is an instance of the exception type. + pub fn normalize(&mut self) { + // The normalization helper function involves temporarily moving out of the &mut self, + // which requires some unsafe trickery: + unsafe { + std::ptr::write(self, std::ptr::read(self).normalized()); + } + // This is safe as long as normalized() doesn't unwind due to a panic. + } + + /// Helper function for normalizing the error by deconstructing and reconstructing the PyErr. + /// Must not panic for safety in normalize() + fn normalized(self) -> PyErr<'p> { + let PyErr { ptype, pvalue, ptraceback } = self; + let py = ptype.python(); + let mut ptype = ptype.steal_ptr(); + let mut pvalue = pvalue.steal_ptr(); + let mut ptraceback = ptraceback.steal_ptr(); + unsafe { + ffi::PyErr_NormalizeException(&mut ptype, &mut pvalue, &mut ptraceback); + PyErr::new_from_ffi_tuple(py, ptype, pvalue, ptraceback) + } + } + + /// 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. + pub fn instance(&mut self) -> &PyObject<'p> { + self.normalize(); + match self.pvalue { + Some(ref instance) => &**instance, + None => self.ptype.python().None() } } @@ -46,18 +136,53 @@ impl <'p> PyErr<'p> { ffi::PyErr_Restore(ptype.steal_ptr(), pvalue.steal_ptr(), ptraceback.steal_ptr()) } } - - #[allow(unused_variables)] - pub fn type_error(obj : &PyObject<'p>, expected_type : &PyType<'p>) -> PyErr<'p> { - let py = obj.python(); - PyErr { - ptype: Some(unsafe { PyPtr::from_borrowed_ptr(py, ffi::PyExc_TypeError) }), - pvalue: None, - ptraceback: None - } - } } +/// Contains getter functions for the python exception types. +#[allow(non_snake_case)] +pub mod exception_types { + + macro_rules! exc_getter( + ($name:ident) => ( + #[inline] + pub fn $name(py: ::python::Python) -> &::object::PyObject { + unsafe { ::object::PyObject::from_ptr(py, ::ffi::$name) } + } + ) + ); + + exc_getter!(PyExc_BaseException); + exc_getter!(PyExc_Exception); + exc_getter!(PyExc_StandardError); + exc_getter!(PyExc_LookupError); + exc_getter!(PyExc_AssertionError); + exc_getter!(PyExc_AttributeError); + exc_getter!(PyExc_EOFError); + exc_getter!(PyExc_EnvironmentError); + exc_getter!(PyExc_FloatingPointError); + exc_getter!(PyExc_IOError); + exc_getter!(PyExc_ImportError); + exc_getter!(PyExc_IndexError); + exc_getter!(PyExc_KeyError); + exc_getter!(PyExc_KeyboardInterrupt); + exc_getter!(PyExc_MemoryError); + exc_getter!(PyExc_NameError); + exc_getter!(PyExc_NotImplementedError); + exc_getter!(PyExc_OSError); + exc_getter!(PyExc_OverflowError); + exc_getter!(PyExc_ReferenceError); + exc_getter!(PyExc_RuntimeError); + exc_getter!(PyExc_SyntaxError); + exc_getter!(PyExc_SystemError); + exc_getter!(PyExc_SystemExit); + exc_getter!(PyExc_TypeError); + exc_getter!(PyExc_ValueError); + #[cfg(target_os="windows")] + exc_getter!(PyExc_WindowsError); + exc_getter!(PyExc_ZeroDivisionError); +} + + /// Construct PyObject from the result of a python FFI call that returns a new reference (owned pointer). /// Returns Err(PyErr) if the pointer is null. /// Unsafe because the pointer might be invalid. @@ -98,8 +223,9 @@ mod tests { fn set_typeerror() { let gil = Python::acquire_gil(); let py = gil.python(); - PyErr::type_error(py.None(), py.get_type::()).restore(); + PyErr::new(::err::exception_types::PyExc_TypeError(py), None).restore(); assert!(PyErr::occurred(py)); + drop(PyErr::fetch(py)) } } diff --git a/src/lib.rs b/src/lib.rs index 52b0e04f..3142a927 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,12 +3,14 @@ #![feature(associated_types)] #![feature(globs)] #![feature(slicing_syntax)] +#![feature(macro_rules)] extern crate libc; extern crate "python27-sys" as ffi; pub use ffi::Py_ssize_t; pub use err::{PyErr, PyResult}; -pub use python::{Python, PythonObject, PythonObjectDowncast}; +pub use err::exception_types::*; +pub use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject}; pub use object::PyObject; pub use typeobject::PyType; pub use pyptr::PyPtr; diff --git a/src/module.rs b/src/module.rs index 1280063e..432e4e9c 100644 --- a/src/module.rs +++ b/src/module.rs @@ -1,6 +1,6 @@ use std; use ffi; -use python::{Python, PythonObject, PythonObjectDowncast}; +use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject}; use object::PyObject; use typeobject::PyType; use pyptr::PyPtr; @@ -13,20 +13,27 @@ impl <'p> PythonObject<'p> for PyModule<'p> { fn as_object<'a>(&'a self) -> &'a PyObject<'p> { &self.0 } + + #[inline] + unsafe fn unchecked_downcast_from<'a>(obj: &'a PyObject<'p>) -> &'a PyModule<'p> { + std::mem::transmute(obj) + } } -impl <'p> PythonObjectDowncast<'p> for PyModule<'p> { +impl <'p> PythonObjectWithCheckedDowncast<'p> for PyModule<'p> { #[inline] - fn from_object<'a>(obj : &'a PyObject<'p>) -> Option<&'a PyModule<'p>> { + fn downcast_from<'a>(obj : &'a PyObject<'p>) -> Option<&'a PyModule<'p>> { unsafe { if ffi::PyModule_Check(obj.as_ptr()) { - Some(std::mem::transmute(obj)) + Some(PythonObject::unchecked_downcast_from(obj)) } else { None } } } - +} + +impl <'p> PythonObjectWithTypeObject<'p> for PyModule<'p> { #[inline] fn type_object(py: Python<'p>, _ : Option<&Self>) -> &'p PyType<'p> { unsafe { PyType::from_type_ptr(py, &mut ffi::PyModule_Type) } diff --git a/src/object.rs b/src/object.rs index 0ec10f99..e2f45de1 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,7 +1,7 @@ use std; use libc; use ffi; -use python::{Python, PythonObject, PythonObjectDowncast}; +use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject}; use typeobject::PyType; use err::{PyErr, PyResult}; @@ -21,6 +21,11 @@ impl <'p> PythonObject<'p> for PyObject<'p> { fn as_object<'a>(&'a self) -> &'a PyObject<'p> { self } + + #[inline] + fn unchecked_downcast_from<'a>(o: &'a PyObject<'p>) -> &'a PyObject<'p> { + o + } /// Retrieves the underlying FFI pointer associated with this python object. #[inline] @@ -34,12 +39,14 @@ impl <'p> PythonObject<'p> for PyObject<'p> { } } -impl <'p> PythonObjectDowncast<'p> for PyObject<'p> { +impl <'p> PythonObjectWithCheckedDowncast<'p> for PyObject<'p> { #[inline] - fn from_object<'a>(obj : &'a PyObject<'p>) -> Option<&'a PyObject<'p>> { + fn downcast_from<'a>(obj : &'a PyObject<'p>) -> Option<&'a PyObject<'p>> { Some(obj) } - +} + +impl <'p> PythonObjectWithTypeObject<'p> for PyObject<'p> { #[inline] fn type_object(py: Python<'p>, _ : Option<&Self>) -> &'p PyType<'p> { unsafe { PyType::from_type_ptr(py, &mut ffi::PyBaseObject_Type) } @@ -73,11 +80,11 @@ impl <'p> PyObject<'p> { /// Casts the PyObject to a concrete python object type. /// Returns a python TypeError if the object is not of the expected type. #[inline] - pub fn downcast>(&self) -> PyResult<'p, &T> { - let obj_opt : Option<&T> = PythonObjectDowncast::from_object(self); + pub fn downcast>(&self) -> PyResult<'p, &T> { + let obj_opt : Option<&T> = PythonObjectWithCheckedDowncast::downcast_from(self); match obj_opt { Some(obj) => Ok(obj), - None => Err(PyErr::type_error(self, PythonObjectDowncast::type_object(self.python(), obj_opt))) + None => Err(unimplemented!()) } } } diff --git a/src/objectprotocol.rs b/src/objectprotocol.rs index 3b248e52..bbcbb872 100644 --- a/src/objectprotocol.rs +++ b/src/objectprotocol.rs @@ -2,7 +2,7 @@ use std; use std::cmp::Ordering; use ffi; use libc; -use python::{Python, PythonObject}; +use python::{Python, PythonObject, PythonObjectWithCheckedDowncast}; use object::PyObject; use pyptr::{PyPtr, PythonPointer, as_ptr}; use conversion::ToPyObject; @@ -204,16 +204,48 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> { /// This is typically a new iterator but if the argument /// is an iterator, this returns itself. #[inline] - fn iter(&self) -> PyResult<'p, PyPtr<'p, PyObject<'p>>> { - unsafe { + fn iter(&self) -> PyResult<'p, PyPtr<'p, PyIterator<'p>>> { + let it = try!(unsafe { result_from_owned_ptr(self.python(), ffi::PyObject_GetIter(self.as_ptr())) - } + }); + it.downcast_into() + } +} + +impl <'p> ObjectProtocol<'p> for PyObject<'p> {} + +pub struct PyIterator<'p>(PyObject<'p>); + +impl <'p> PythonObject<'p> for PyIterator<'p> { + #[inline] + fn as_object<'a>(&'a self) -> &'a PyObject<'p> { + &self.0 } + #[inline] + unsafe fn unchecked_downcast_from<'a>(o: &'a PyObject<'p>) -> &'a PyIterator<'p> { + std::mem::transmute(o) + } +} + +impl <'p> PythonObjectWithCheckedDowncast<'p> for PyIterator<'p> { + #[inline] + fn downcast_from<'a>(o: &'a PyObject<'p>) -> Option<&'a PyIterator<'p>> { + unsafe { + if ffi::PyIter_Check(o.as_ptr()) { + Some(PythonObject::unchecked_downcast_from(o)) + } else { + None + } + } + } +} + +impl <'p> PyIterator<'p> { /// Retrieves the next item from an iterator. /// Returns None when the iterator is exhausted. #[inline] - fn iter_next(&self) -> PyResult<'p, Option>>> { + pub fn iter_next(&self) -> PyResult<'p, Option>>> { let py = self.python(); let r = unsafe { ffi::PyIter_Next(self.as_ptr()) }; if r.is_null() { @@ -228,6 +260,3 @@ pub trait ObjectProtocol<'p> : PythonObject<'p> { } } -impl <'p> ObjectProtocol<'p> for PyObject<'p> {} - - diff --git a/src/pyptr.rs b/src/pyptr.rs index 683126a5..8121c0a7 100644 --- a/src/pyptr.rs +++ b/src/pyptr.rs @@ -1,7 +1,7 @@ use std; use std::ops::Deref; use ffi; -use python::{Python, PythonObject, PythonObjectDowncast}; +use python::{Python, PythonObject, PythonObjectWithCheckedDowncast}; use object::PyObject; use err::{PyResult, PyErr}; //use conversion::{FromPyObject, ToPyObject}; @@ -181,7 +181,7 @@ impl<'p> PyPtr<'p, PyObject<'p>> { /// Casts the PyPtr to a PyPtr of a concrete python object type. /// Returns a python TypeError if the object is not of the expected type. #[inline] - pub fn downcast_into(self) -> PyResult<'p, PyPtr<'p, T>> where T: PythonObjectDowncast<'p> { + pub fn downcast_into(self) -> PyResult<'p, PyPtr<'p, T>> where T: PythonObjectWithCheckedDowncast<'p> { // TODO: avoid unnecessary IncRef/DefRef self.deref().downcast().map(PyPtr::new) } diff --git a/src/python.rs b/src/python.rs index 2c4cb63c..f33018c0 100644 --- a/src/python.rs +++ b/src/python.rs @@ -21,6 +21,10 @@ pub trait PythonObject<'p> : 'p { /// Casts the python object to PyObject. fn as_object(&self) -> &PyObject<'p>; + /// Unsafe downcast from &PyObject to &Self. + /// Undefined behavior if the input object does not have the expected type. + unsafe fn unchecked_downcast_from<'a>(&'a PyObject<'p>) -> &'a Self; + /// Retrieves the underlying FFI pointer associated with this python object. #[inline] fn as_ptr(&self) -> *mut ffi::PyObject { @@ -35,14 +39,14 @@ pub trait PythonObject<'p> : 'p { } /// Trait implemented by python object types that allow a checked downcast. -pub trait PythonObjectDowncast<'p> : PythonObject<'p> { - // TODO: maybe add 'unsafe fn from_object_unchecked' to PythonObject/PythonObjectDowncast, - // and then implement from_object using type_object().is_instance() ? - +pub trait PythonObjectWithCheckedDowncast<'p> : PythonObject<'p> { /// Upcast from PyObject to a concrete python object type. /// Returns None if the python object is not of the specified type. - fn from_object<'a>(&'a PyObject<'p>) -> Option<&'a Self>; + fn downcast_from<'a>(&'a PyObject<'p>) -> Option<&'a Self>; +} +/// Trait implemented by python object types that have a corresponding type object. +pub trait PythonObjectWithTypeObject<'p> : PythonObjectWithCheckedDowncast<'p> { /// Retrieves the type object for this python object type. /// Option<&Self> is necessary until UFCS is implemented. fn type_object(Python<'p>, Option<&Self>) -> &'p PyType<'p>; @@ -99,9 +103,9 @@ impl<'p> Python<'p> { /// Retrieves a reference to the type object for type T. #[inline] - pub fn get_type(self) -> &'p PyType<'p> where T: PythonObjectDowncast<'p> { + pub fn get_type(self) -> &'p PyType<'p> where T: PythonObjectWithTypeObject<'p> { let none : Option<&T> = None; - PythonObjectDowncast::type_object(self, none) + PythonObjectWithTypeObject::type_object(self, none) } } diff --git a/src/typeobject.rs b/src/typeobject.rs index 2ac3ac6d..589f4ebc 100644 --- a/src/typeobject.rs +++ b/src/typeobject.rs @@ -1,4 +1,4 @@ -use python::{Python, PythonObject, PythonObjectDowncast}; +use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject}; use object::PyObject; use ffi; use libc::c_char; @@ -20,25 +20,32 @@ impl <'p> PythonObject<'p> for PyType<'p> { fn as_object<'a>(&'a self) -> &'a PyObject<'p> { unsafe { std::mem::transmute(self) } } - + + #[inline] + unsafe fn unchecked_downcast_from<'a>(obj: &'a PyObject<'p>) -> &'a PyType<'p> { + std::mem::transmute(obj) + } + #[inline] fn python(&self) -> Python<'p> { self.py } } -impl <'p> PythonObjectDowncast<'p> for PyType<'p> { +impl <'p> PythonObjectWithCheckedDowncast<'p> for PyType<'p> { #[inline] - fn from_object<'a>(obj : &'a PyObject<'p>) -> Option<&'a PyType<'p>> { + fn downcast_from<'a>(obj : &'a PyObject<'p>) -> Option<&'a PyType<'p>> { unsafe { if ffi::PyType_Check(obj.as_ptr()) { - Some(std::mem::transmute(obj)) + Some(PythonObject::unchecked_downcast_from(obj)) } else { None } } } +} +impl <'p> PythonObjectWithTypeObject<'p> for PyType<'p> { #[inline] fn type_object(py: Python<'p>, _ : Option<&Self>) -> &'p PyType<'p> { unsafe { PyType::from_type_ptr(py, &mut ffi::PyType_Type) }