Add PyDict and unchecked downcasts.
This commit is contained in:
parent
eca5b4039b
commit
c61e1e51f4
|
@ -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<PyObject>, 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 //<Target = PyObject<'p>>
|
||||
= PyPtr<'p, PyObject<'p>>;
|
||||
type PointerType : PythonPointer + std::ops::Deref // <Target = Self::ResultType>
|
||||
= 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>>> {
|
||||
|
|
46
src/dict.rs
Normal file
46
src/dict.rs
Normal file
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
168
src/err.rs
168
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<PyPtr<'p, PyObject<'p>>>,
|
||||
pvalue : Option<PyPtr<'p, PyObject<'p>>>,
|
||||
ptraceback : Option<PyPtr<'p, PyObject<'p>>>
|
||||
/// 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<PyPtr<'p, PyObject<'p>>>,
|
||||
pub ptraceback : Option<PyPtr<'p, PyObject<'p>>> // 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<PyPtr<'p, PyObject<'p>>>) -> 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::<PyType>()).restore();
|
||||
PyErr::new(::err::exception_types::PyExc_TypeError(py), None).restore();
|
||||
assert!(PyErr::occurred(py));
|
||||
drop(PyErr::fetch(py))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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<T : PythonObjectDowncast<'p>>(&self) -> PyResult<'p, &T> {
|
||||
let obj_opt : Option<&T> = PythonObjectDowncast::from_object(self);
|
||||
pub fn downcast<T : PythonObjectWithCheckedDowncast<'p>>(&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!())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<PyPtr<'p, PyObject<'p>>>> {
|
||||
pub fn iter_next(&self) -> PyResult<'p, Option<PyPtr<'p, PyObject<'p>>>> {
|
||||
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> {}
|
||||
|
||||
|
||||
|
|
|
@ -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<PyObject> 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<T>(self) -> PyResult<'p, PyPtr<'p, T>> where T: PythonObjectDowncast<'p> {
|
||||
pub fn downcast_into<T>(self) -> PyResult<'p, PyPtr<'p, T>> where T: PythonObjectWithCheckedDowncast<'p> {
|
||||
// TODO: avoid unnecessary IncRef/DefRef
|
||||
self.deref().downcast().map(PyPtr::new)
|
||||
}
|
||||
|
|
|
@ -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<T>(self) -> &'p PyType<'p> where T: PythonObjectDowncast<'p> {
|
||||
pub fn get_type<T>(self) -> &'p PyType<'p> where T: PythonObjectWithTypeObject<'p> {
|
||||
let none : Option<&T> = None;
|
||||
PythonObjectDowncast::type_object(self, none)
|
||||
PythonObjectWithTypeObject::type_object(self, none)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) }
|
||||
|
|
Loading…
Reference in a new issue