added try downcast methods to PyDowncastFrom trait
This commit is contained in:
parent
9cecfc0ec9
commit
f344c4ce3d
|
@ -215,28 +215,46 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
|
|||
|
||||
impl _pyo3::PyDowncastFrom for #cls
|
||||
{
|
||||
fn downcast_from(ob: &_pyo3::PyObjectRef) -> Result<&#cls, _pyo3::PyDowncastError>
|
||||
fn try_downcast_from(ob: &_pyo3::PyObjectRef) -> Option<&#cls>
|
||||
{
|
||||
unsafe {
|
||||
let ptr = ob.as_ptr();
|
||||
let checked = ffi::PyObject_TypeCheck(
|
||||
ob.as_ptr(), <#cls as _pyo3::typeob::PyTypeInfo>::type_object()) != 0;
|
||||
ptr, <#cls as _pyo3::typeob::PyTypeInfo>::type_object()) != 0;
|
||||
|
||||
if checked {
|
||||
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
|
||||
let ptr = (ob.as_ptr() as *mut u8).offset(offset) as *mut #cls;
|
||||
Ok(ptr.as_ref().unwrap())
|
||||
let ptr = (ptr as *mut u8).offset(offset) as *mut #cls;
|
||||
Some(ptr.as_ref().unwrap())
|
||||
} else {
|
||||
Err(_pyo3::PyDowncastError(ob.py(), None))
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_exact_downcast_from(ob: &_pyo3::PyObjectRef) -> Option<&#cls>
|
||||
{
|
||||
unsafe {
|
||||
let ptr = ob.as_ptr();
|
||||
if (*ptr).ob_type == <#cls as _pyo3::typeob::PyTypeInfo>::type_object()
|
||||
{
|
||||
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
|
||||
let ptr = (ptr as *mut u8).offset(offset) as *mut #cls;
|
||||
Some(ptr.as_ref().unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn unchecked_downcast_from(ob: &_pyo3::PyObjectRef) -> &Self
|
||||
{
|
||||
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
|
||||
let ptr = (ob.as_ptr() as *mut u8).offset(offset) as *mut #cls;
|
||||
&*ptr
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn unchecked_mut_downcast_from(ob: &_pyo3::PyObjectRef) -> &mut Self
|
||||
{
|
||||
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
|
||||
|
@ -246,19 +264,33 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
|
|||
}
|
||||
impl _pyo3::PyMutDowncastFrom for #cls
|
||||
{
|
||||
fn downcast_mut_from(ob: &mut _pyo3::PyObjectRef)
|
||||
-> Result<&mut #cls, _pyo3::PyDowncastError>
|
||||
fn try_mut_downcast_from(ob: &mut _pyo3::PyObjectRef) -> Option<&mut #cls>
|
||||
{
|
||||
unsafe {
|
||||
let ptr = ob.as_ptr();
|
||||
let checked = ffi::PyObject_TypeCheck(
|
||||
ob.as_ptr(), <#cls as _pyo3::typeob::PyTypeInfo>::type_object()) != 0;
|
||||
ptr, <#cls as _pyo3::typeob::PyTypeInfo>::type_object()) != 0;
|
||||
|
||||
if checked {
|
||||
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
|
||||
let ptr = (ob.as_ptr() as *mut u8).offset(offset) as *mut #cls;
|
||||
Ok(ptr.as_mut().unwrap())
|
||||
let ptr = (ptr as *mut u8).offset(offset) as *mut #cls;
|
||||
Some(ptr.as_mut().unwrap())
|
||||
} else {
|
||||
Err(_pyo3::PyDowncastError(ob.py(), None))
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
fn try_mut_exact_downcast_from(ob: &mut _pyo3::PyObjectRef) -> Option<&mut #cls>
|
||||
{
|
||||
unsafe {
|
||||
let ptr = ob.as_ptr();
|
||||
if (*ptr).ob_type == <#cls as _pyo3::typeob::PyTypeInfo>::type_object()
|
||||
{
|
||||
let offset = <#cls as _pyo3::typeob::PyTypeInfo>::offset();
|
||||
let ptr = (ptr as *mut u8).offset(offset) as *mut #cls;
|
||||
Some(ptr.as_mut().unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
//! Python argument parsing
|
||||
|
||||
use ffi;
|
||||
use python::Python;
|
||||
use instance::AsPyRef;
|
||||
use python::{Python, PyDowncastFrom};
|
||||
use objects::{PyObjectRef, PyTuple, PyDict, PyString, exc};
|
||||
use err::{self, PyResult};
|
||||
|
||||
|
@ -88,7 +89,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() {
|
||||
let key = try!(try!(key.cast_as::<PyString>(py)).to_string());
|
||||
let key = PyString::downcast_from(key.as_ref(py))?.to_string()?;
|
||||
if !params.iter().any(|p| p.name == key) {
|
||||
return Err(err::PyErr::new::<exc::TypeError, _>(
|
||||
py,
|
||||
|
|
|
@ -6,7 +6,6 @@ use err::PyResult;
|
|||
use python::{Python, ToPyPointer, PyDowncastFrom};
|
||||
use object::PyObject;
|
||||
use objects::{PyObjectRef, PyTuple};
|
||||
use objectprotocol::ObjectProtocol;
|
||||
use typeob::PyTypeInfo;
|
||||
use instance::Py;
|
||||
|
||||
|
@ -154,7 +153,7 @@ impl<'a, T> FromPyObject<'a> for &'a T
|
|||
#[inline]
|
||||
default fn extract(ob: &'a PyObjectRef) -> PyResult<&'a T>
|
||||
{
|
||||
Ok(ob.cast_as()?)
|
||||
Ok(PyDowncastFrom::downcast_from(ob)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use std;
|
|||
|
||||
use ffi;
|
||||
use pythonrun;
|
||||
use err::{PyErr, PyResult, PyDowncastError};
|
||||
use err::{PyErr, PyResult};
|
||||
use instance::{AsPyRef, PyObjectWithToken};
|
||||
use objects::{PyObjectRef, PyDict};
|
||||
use conversion::{ToPyObject, IntoPyObject, IntoPyTuple, FromPyObject};
|
||||
|
@ -150,12 +150,11 @@ impl PyObject {
|
|||
}
|
||||
|
||||
/// Casts the PyObject to a concrete Python object type.
|
||||
/// Fails with `PyDowncastError` if the object is not of the expected type.
|
||||
#[inline]
|
||||
pub fn cast_as<D>(&self, py: Python) -> Result<&D, PyDowncastError>
|
||||
pub fn cast_as<D>(&self, py: Python) -> Option<&D>
|
||||
where D: ::PyDowncastFrom
|
||||
{
|
||||
<D as ::PyDowncastFrom>::downcast_from(self.as_ref(py))
|
||||
<D as ::PyDowncastFrom>::try_downcast_from(self.as_ref(py))
|
||||
}
|
||||
|
||||
/// Extracts some type from the Python object.
|
||||
|
@ -166,21 +165,6 @@ impl PyObject {
|
|||
FromPyObject::extract(self.as_ref(py))
|
||||
}
|
||||
|
||||
/// Calls the object.
|
||||
/// This is equivalent to the Python expression: 'self(*args, **kwargs)'
|
||||
#[inline]
|
||||
pub fn call<A>(&self, py: Python, args: A, kwargs: Option<&PyDict>) -> PyResult<PyObject>
|
||||
where A: IntoPyTuple
|
||||
{
|
||||
let t = args.into_tuple(py);
|
||||
let result = unsafe {
|
||||
PyObject::from_borrowed_ptr_or_err(
|
||||
py, ffi::PyObject_Call(self.as_ptr(), t.as_ptr(), kwargs.as_ptr()))
|
||||
};
|
||||
py.release(t);
|
||||
result
|
||||
}
|
||||
|
||||
/// Retrieves an attribute value.
|
||||
/// This is equivalent to the Python expression 'self.attr_name'.
|
||||
#[inline]
|
||||
|
@ -193,6 +177,21 @@ impl PyObject {
|
|||
})
|
||||
}
|
||||
|
||||
/// Calls the object.
|
||||
/// This is equivalent to the Python expression: 'self(*args, **kwargs)'
|
||||
#[inline]
|
||||
pub fn call<A>(&self, py: Python, args: A, kwargs: Option<&PyDict>) -> PyResult<PyObject>
|
||||
where A: IntoPyTuple
|
||||
{
|
||||
let t = args.into_tuple(py);
|
||||
let result = unsafe {
|
||||
PyObject::from_owned_ptr_or_err(
|
||||
py, ffi::PyObject_Call(self.as_ptr(), t.as_ptr(), kwargs.as_ptr()))
|
||||
};
|
||||
py.release(t);
|
||||
result
|
||||
}
|
||||
|
||||
/// Calls a method on the object.
|
||||
/// This is equivalent to the Python expression: 'self.name(*args, **kwargs)'
|
||||
#[inline]
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::cmp::Ordering;
|
|||
use std::os::raw::c_int;
|
||||
|
||||
use ffi;
|
||||
use err::{PyErr, PyResult, PyDowncastError, self};
|
||||
use err::{self, PyErr, PyResult};
|
||||
use python::{Python, ToPyPointer, PyDowncastFrom};
|
||||
use object::PyObject;
|
||||
use objects::{PyObjectRef, PyDict, PyString, PyIterator, PyType};
|
||||
|
@ -122,10 +122,9 @@ pub trait ObjectProtocol {
|
|||
fn get_type(&self) -> &PyType;
|
||||
|
||||
/// Casts the PyObject to a concrete Python object type.
|
||||
/// Fails with `PyDowncastError` if the object is not of the expected type.
|
||||
fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError<'a>>
|
||||
fn cast_as<'a, D>(&'a self) -> Option<&'a D>
|
||||
where D: PyDowncastFrom,
|
||||
&'a PyObjectRef: std::convert::From<&'a Self>;
|
||||
&'a PyObjectRef: std::convert::From<&'a Self>;
|
||||
|
||||
/// Extracts some type from the Python object.
|
||||
/// This is a wrapper function around `FromPyObject::extract()`.
|
||||
|
@ -353,11 +352,11 @@ impl<T> ObjectProtocol for T where T: PyObjectWithToken + ToPyPointer {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError<'a>>
|
||||
fn cast_as<'a, D>(&'a self) -> Option<&'a D>
|
||||
where D: PyDowncastFrom,
|
||||
&'a PyObjectRef: std::convert::From<&'a Self>
|
||||
{
|
||||
<D as PyDowncastFrom>::downcast_from(self.into())
|
||||
<D as PyDowncastFrom>::try_downcast_from(self.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
use ffi;
|
||||
use object::PyObject;
|
||||
use python::{ToPyPointer, Python};
|
||||
use python::{ToPyPointer, Python, PyDowncastFrom};
|
||||
use conversion::{ToPyObject, IntoPyObject};
|
||||
|
||||
/// Represents a Python `bool`.
|
||||
|
@ -57,7 +57,7 @@ impl IntoPyObject for bool {
|
|||
///
|
||||
/// Fails with `TypeError` if the input is not a Python `bool`.
|
||||
pyobject_extract!(py, obj to bool => {
|
||||
Ok(try!(obj.cast_as::<PyBool>()).is_true())
|
||||
Ok(PyBool::downcast_from(obj)?.is_true())
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -33,16 +33,14 @@ macro_rules! pyobject_downcast(
|
|||
($name: ident, $checkfunction: ident) => (
|
||||
impl $crate::python::PyDowncastFrom for $name
|
||||
{
|
||||
fn downcast_from(ob: &$crate::PyObjectRef)
|
||||
-> Result<&$name, $crate::PyDowncastError>
|
||||
fn try_downcast_from(ob: &$crate::PyObjectRef) -> Option<&$name>
|
||||
{
|
||||
use $crate::{ToPyPointer, PyObjectWithToken};
|
||||
|
||||
use $crate::ToPyPointer;
|
||||
unsafe {
|
||||
if $crate::ffi::$checkfunction(ob.as_ptr()) > 0 {
|
||||
Ok($crate::std::mem::transmute(ob))
|
||||
Some($crate::std::mem::transmute(ob))
|
||||
} else {
|
||||
Err($crate::PyDowncastError(ob.py(), None))
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -259,7 +259,7 @@ mod test {
|
|||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = 42i32;
|
||||
assert!(v.to_object(py).cast_as::<PySequence>(py).is_err());
|
||||
assert!(PySequence::downcast_from(v.to_object(py).as_ref(py)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -267,7 +267,7 @@ mod test {
|
|||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let v = "London Calling";
|
||||
assert!(v.to_object(py).cast_as::<PySequence>(py).is_ok());
|
||||
assert!(PySequence::downcast_from(v.to_object(py).as_ref(py)).is_ok());
|
||||
}
|
||||
#[test]
|
||||
fn test_seq_empty() {
|
||||
|
|
|
@ -5,7 +5,7 @@ use err::PyResult;
|
|||
use object::PyObject;
|
||||
use objects::{PyObjectRef, PyString};
|
||||
use objectprotocol::ObjectProtocol;
|
||||
use python::Python;
|
||||
use python::{Python, PyDowncastFrom};
|
||||
use conversion::{ToPyObject, IntoPyObject, RefFromPyObject};
|
||||
|
||||
/// Converts Rust `str` to Python object.
|
||||
|
@ -59,15 +59,14 @@ impl<'source> ::FromPyObject<'source> for Cow<'source, str>
|
|||
{
|
||||
fn extract(ob: &'source PyObjectRef) -> PyResult<Self>
|
||||
{
|
||||
try!(ob.cast_as::<PyString>()).to_string()
|
||||
PyString::downcast_from(ob)?.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows extracting strings from Python objects.
|
||||
/// Accepts Python `str` and `unicode` objects.
|
||||
pyobject_extract!(py, obj to String => {
|
||||
let s = try!(obj.cast_as::<PyString>());
|
||||
s.to_string().map(Cow::into_owned)
|
||||
PyString::downcast_from(obj)?.to_string().map(Cow::into_owned)
|
||||
});
|
||||
|
||||
impl RefFromPyObject for str {
|
||||
|
|
|
@ -9,8 +9,7 @@ use err::{PyErr, PyResult};
|
|||
use instance::{Py, PyObjectWithToken};
|
||||
use object::PyObject;
|
||||
use objects::PyObjectRef;
|
||||
use objectprotocol::ObjectProtocol;
|
||||
use python::{Python, ToPyPointer, IntoPyPointer};
|
||||
use python::{Python, ToPyPointer, IntoPyPointer, PyDowncastFrom};
|
||||
use conversion::{FromPyObject, ToPyObject, IntoPyTuple, IntoPyObject};
|
||||
use super::exc;
|
||||
|
||||
|
@ -146,7 +145,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
|
|||
impl<'s, $($T: FromPyObject<'s>),+> FromPyObject<'s> for ($($T,)+) {
|
||||
fn extract(obj: &'s PyObjectRef) -> PyResult<Self>
|
||||
{
|
||||
let t = try!(obj.cast_as::<PyTuple>());
|
||||
let t = PyTuple::downcast_from(obj)?;
|
||||
let slice = t.as_slice();
|
||||
if t.len() == $length {
|
||||
Ok((
|
||||
|
@ -224,7 +223,7 @@ impl IntoPyTuple for () {
|
|||
/// Returns `Ok(NoArgs)` if the input is an empty Python tuple.
|
||||
/// Otherwise, returns an error.
|
||||
pyobject_extract!(py, obj to NoArgs => {
|
||||
let t = try!(obj.cast_as::<PyTuple>());
|
||||
let t = PyTuple::downcast_from(obj)?;
|
||||
if t.len() == 0 {
|
||||
Ok(NoArgs)
|
||||
} else {
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::os::raw::c_int;
|
|||
|
||||
use ffi;
|
||||
use typeob::{PyTypeInfo, PyTypeObject, PyObjectAlloc};
|
||||
use instance::{Py, PyToken};
|
||||
use instance::{Py, PyToken, PyObjectWithToken};
|
||||
use object::PyObject;
|
||||
use objects::{PyObjectRef, PyType, PyDict, PyModule};
|
||||
use err::{PyErr, PyResult, PyDowncastError, ToPyErr};
|
||||
|
@ -34,7 +34,30 @@ pub struct Python<'p>(PhantomData<&'p GILGuard>);
|
|||
pub trait PyDowncastFrom : Sized {
|
||||
|
||||
/// Cast from PyObject to a concrete Python object type.
|
||||
fn downcast_from(&PyObjectRef) -> Result<&Self, PyDowncastError>;
|
||||
fn try_downcast_from(&PyObjectRef) -> Option<&Self>;
|
||||
|
||||
/// Cast from PyObject to a concrete Python object type.
|
||||
fn try_exact_downcast_from(ob: &PyObjectRef) -> Option<&Self> {
|
||||
Self::try_downcast_from(ob)
|
||||
}
|
||||
|
||||
/// Cast from PyObject to a concrete Python object type.
|
||||
fn downcast_from(ob: &PyObjectRef) -> Result<&Self, PyDowncastError> {
|
||||
if let Some(ob) = Self::try_downcast_from(ob) {
|
||||
Ok(ob)
|
||||
} else {
|
||||
Err(PyDowncastError(ob.py(), None))
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast from PyObject to a concrete Python object type.
|
||||
fn exact_downcast_from(ob: &PyObjectRef) -> Result<&Self, PyDowncastError> {
|
||||
if let Some(ob) = Self::try_exact_downcast_from(ob) {
|
||||
Ok(ob)
|
||||
} else {
|
||||
Err(PyDowncastError(ob.py(), None))
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast from PyObject to a concrete Python object type.
|
||||
unsafe fn unchecked_downcast_from(&PyObjectRef) -> &Self;
|
||||
|
@ -47,7 +70,32 @@ pub trait PyDowncastFrom : Sized {
|
|||
pub trait PyMutDowncastFrom : Sized {
|
||||
|
||||
/// Cast from PyObject to a concrete Python object type.
|
||||
fn downcast_mut_from(&mut PyObjectRef) -> Result<&mut Self, PyDowncastError>;
|
||||
fn try_mut_downcast_from(&mut PyObjectRef) -> Option<&mut Self>;
|
||||
|
||||
/// Cast from PyObject to a concrete Python object type.
|
||||
fn try_mut_exact_downcast_from(ob: &mut PyObjectRef) -> Option<&mut Self> {
|
||||
Self::try_mut_downcast_from(ob)
|
||||
}
|
||||
|
||||
/// Cast from PyObject to a concrete Python object type.
|
||||
fn mut_downcast_from(ob: &mut PyObjectRef) -> Result<&mut Self, PyDowncastError> {
|
||||
if let Some(o) = Self::try_mut_downcast_from(ob) {
|
||||
return Ok(o)
|
||||
} else {
|
||||
let py = unsafe { Python::assume_gil_acquired() };
|
||||
Err(PyDowncastError(py, None))
|
||||
}
|
||||
}
|
||||
|
||||
/// Cast from PyObject to a concrete Python object type.
|
||||
fn mut_exact_downcast_from(ob: &mut PyObjectRef) -> Result<&mut Self, PyDowncastError> {
|
||||
if let Some(ob) = Self::try_mut_exact_downcast_from(ob) {
|
||||
Ok(ob)
|
||||
} else {
|
||||
let py = unsafe { Python::assume_gil_acquired() };
|
||||
Err(PyDowncastError(py, None))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait implemented by Python object types that allow a checked downcast.
|
||||
|
|
|
@ -111,7 +111,7 @@ fn empty_class_with_new() {
|
|||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let typeobj = py.get_type::<EmptyClassWithNew>();
|
||||
assert!(typeobj.call(NoArgs, None).unwrap().cast_as::<EmptyClassWithNew>().is_ok());
|
||||
assert!(typeobj.call(NoArgs, None).unwrap().cast_as::<EmptyClassWithNew>().is_some());
|
||||
}
|
||||
|
||||
#[py::class]
|
||||
|
|
|
@ -29,7 +29,7 @@ struct Test {
|
|||
impl<'p> PyMappingProtocol<'p> for Test
|
||||
{
|
||||
fn __getitem__(&self, idx: &PyObjectRef) -> PyResult<PyObject> {
|
||||
if let Ok(slice) = idx.cast_as::<PySlice>() {
|
||||
if let Some(slice) = idx.cast_as::<PySlice>() {
|
||||
let indices = slice.indices(1000)?;
|
||||
if indices.start == 100 && indices.stop == 200 && indices.step == 1 {
|
||||
return Ok("slice".into_object(self.py()))
|
||||
|
|
Loading…
Reference in a new issue