#40: split ExtractPyObject into two traits:

* FromPyObject for all conversions that don't need to borrow temporaries
* RefFromPyObject for extracting references out of temporaries
  (currently only used for extracting `&str`)
This commit is contained in:
Daniel Grunwald 2016-05-08 21:25:09 +02:00
parent 1a6e26f3c3
commit 0222176836
15 changed files with 261 additions and 234 deletions

View file

@ -11,13 +11,15 @@ py_module_initializer!(hello, inithello, PyInit_hello, |py, m| {
Ok(())
});
fn run(py: Python, args: &PyTuple, kwargs: &PyDict) -> PyResult<PyObject> {
fn run(py: Python, args: &PyTuple, kwargs: Option<&PyDict>) -> PyResult<PyObject> {
println!("Rust says: Hello Python!");
for arg in args.iter(py) {
println!("Rust got {}", arg);
}
for (key, val) in kwargs.items(py) {
println!("{} = {}", key, val);
if let Some(kwargs) = kwargs {
for (key, val) in kwargs.items(py) {
println!("{} = {}", key, val);
}
}
Ok(py.None())
}

View file

@ -168,13 +168,23 @@ macro_rules! py_argparse_parse_plist_impl {
{ $callback:ident { $($initial_arg:tt)* } $output:tt ( ) } => {
$callback! { $($initial_arg)* $output }
};
// Kwargs parameter with reference extraction
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
( ** $name:ident : &$t:ty , $($tail:tt)* )
} => {
py_argparse_parse_plist_impl! {
$callback $initial_args
[ $($output)* { $name:&$t = [ {**} {} {$t} ] } ]
($($tail)*)
}
};
// Kwargs parameter
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
( ** $name:ident : $t:ty , $($tail:tt)* )
} => {
py_argparse_parse_plist_impl! {
$callback $initial_args
[ $($output)* { $name:$t = [ {**} {} ] } ]
[ $($output)* { $name:$t = [ {**} {} {} ] } ]
($($tail)*)
}
};
@ -184,7 +194,17 @@ macro_rules! py_argparse_parse_plist_impl {
} => {
py_argparse_parse_plist_impl! {
$callback $initial_args
[ $($output)* { $name:&$crate::PyDict = [ {**} {} ] } ]
[ $($output)* { $name:Option<&$crate::PyDict> = [ {**} {} {} ] } ]
($($tail)*)
}
};
// Varargs parameter with reference extraction
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
( * $name:ident : &$t:ty , $($tail:tt)* )
} => {
py_argparse_parse_plist_impl! {
$callback $initial_args
[ $($output)* { $name:&$t = [ {*} {} {$t} ] } ]
($($tail)*)
}
};
@ -194,7 +214,7 @@ macro_rules! py_argparse_parse_plist_impl {
} => {
py_argparse_parse_plist_impl! {
$callback $initial_args
[ $($output)* { $name:$t = [ {*} {} ] } ]
[ $($output)* { $name:$t = [ {*} {} {} ] } ]
($($tail)*)
}
};
@ -204,7 +224,17 @@ macro_rules! py_argparse_parse_plist_impl {
} => {
py_argparse_parse_plist_impl! {
$callback $initial_args
[ $($output)* { $name:&$crate::PyTuple = [ {*} {} ] } ]
[ $($output)* { $name:&$crate::PyTuple = [ {*} {} {} ] } ]
($($tail)*)
}
};
// Simple parameter with reference extraction
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
( $name:ident : &$t:ty , $($tail:tt)* )
} => {
py_argparse_parse_plist_impl! {
$callback $initial_args
[ $($output)* { $name:&$t = [ {} {} {$t} ] } ]
($($tail)*)
}
};
@ -214,7 +244,7 @@ macro_rules! py_argparse_parse_plist_impl {
} => {
py_argparse_parse_plist_impl! {
$callback $initial_args
[ $($output)* { $name:$t = [ {} {} ] } ]
[ $($output)* { $name:$t = [ {} {} {} ] } ]
($($tail)*)
}
};
@ -224,17 +254,18 @@ macro_rules! py_argparse_parse_plist_impl {
} => {
py_argparse_parse_plist_impl! {
$callback $initial_args
[ $($output)* { $name:&$crate::PyObject = [ {} {} ] } ]
[ $($output)* { $name:&$crate::PyObject = [ {} {} {} ] } ]
($($tail)*)
}
};
// Optional parameter
// TODO: with reference extraction?
{ $callback:ident $initial_args:tt [ $($output:tt)* ]
( $name:ident : $t:ty = $default:expr , $($tail:tt)* )
} => {
py_argparse_parse_plist_impl! {
$callback $initial_args
[ $($output)* { $name:$t = [ {} {$default} ] } ]
[ $($output)* { $name:$t = [ {} {$default} {} ] } ]
($($tail)*)
}
};
@ -249,19 +280,15 @@ macro_rules! py_argparse_impl {
// so we can directly pass along our inputs without calling parse_args().
($py:expr, $fname:expr, $args:expr, $kwargs:expr, $body:block,
[
{ $pargs:ident : $pargs_type:ty = [ {*} {} ] }
{ $pkwargs:ident : $pkwargs_type:ty = [ {**} {} ] }
{ $pargs:ident : $pargs_type:ty = [ {*} {} {} ] }
{ $pkwargs:ident : $pkwargs_type:ty = [ {**} {} {} ] }
]
) => {{
let py: $crate::Python = $py;
let _py: $crate::Python = $py;
// TODO: use extract() to be more flexible in which type is expected
let $pargs: $pargs_type = $args;
let new_dict = if $kwargs.is_none() { Some($crate::PyDict::new(py)) } else { None };
let ret = {
let $pkwargs: $pkwargs_type = $kwargs.unwrap_or_else(|| new_dict.as_ref().unwrap());
$body
};
$crate::PyDrop::release_ref(new_dict, $py);
ret
let $pkwargs: $pkwargs_type = $kwargs;
$body
}};
// normal argparse logic
@ -320,7 +347,7 @@ pub unsafe fn get_kwargs(py: Python, ptr: *mut ffi::PyObject) -> Option<PyDict>
#[doc(hidden)]
macro_rules! py_argparse_param_description {
// normal parameter
{ $pname:ident : $ptype:ty = [ {} {} ] } => (
{ $pname:ident : $ptype:ty = [ {} {} $rtype:tt ] } => (
$crate::argparse::ParamDescription {
name: stringify!($pname),
is_optional: false
@ -335,57 +362,31 @@ macro_rules! py_argparse_extract {
( $py:expr, $iter:expr, $body:block, [] ) => { $body };
// normal parameter
( $py:expr, $iter:expr, $body:block,
[ { $pname:ident : $ptype:ty = [ {} {} ] } $($tail:tt)* ]
[ { $pname:ident : $ptype:ty = [ {} {} {} ] } $($tail:tt)* ]
) => {
// First unwrap() asserts the iterated sequence is long enough (which should be guaranteed);
// second unwrap() asserts the parameter was not missing (which fn parse_args already checked for).
match <$ptype as $crate::ExtractPyObject>::prepare_extract($py, $iter.next().unwrap().as_ref().unwrap()) {
Ok(prepared) => {
match <$ptype as $crate::ExtractPyObject>::extract($py, &prepared) {
Ok($pname) => py_argparse_extract!($py, $iter, $body, [$($tail)*]),
Err(e) => Err(e)
}
},
match <$ptype as $crate::FromPyObject>::extract($py, $iter.next().unwrap().as_ref().unwrap()) {
Ok($pname) => py_argparse_extract!($py, $iter, $body, [$($tail)*]),
Err(e) => Err(e)
}
};
// normal parameter with reference extraction
( $py:expr, $iter:expr, $body:block,
[ { $pname:ident : $ptype:ty = [ {} {} {$rtype:ty} ] } $($tail:tt)* ]
) => {
// First unwrap() asserts the iterated sequence is long enough (which should be guaranteed);
// second unwrap() asserts the parameter was not missing (which fn parse_args already checked for).
match <$rtype as $crate::RefFromPyObject>::with_extracted($py,
$iter.next().unwrap().as_ref().unwrap(),
|$pname: $ptype| py_argparse_extract!($py, $iter, $body, [$($tail)*])
) {
Ok(v) => v,
Err(e) => Err(e)
}
};
}
/*
#[macro_export]
#[doc(hidden)]
macro_rules! py_argparse_declare_item_in_impl {
// argparse_declare_item_in_impl!({implhead} {head} (params) (,plist,) {tail} )
// = implhead { head(params, pname:ptype...) tail }
{ {$($implhead:tt)*} {$($head:tt)*} $params:tt ( $(,)* ) {$($tail:tt)*} } => {
py_coerce_item!{
$($implhead)* {
$($head)* $params $($tail)*
}
}
};
{ $implhead:tt $head:tt ($($params:tt)*) ( ,$pname:ident : $ptype:ty, $($r:tt)* ) $tail:tt } => {
py_argparse_declare_item_in_impl!( $implhead $head ($($params)* , $pname : $ptype) ( ,$($r)* ) $tail)
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! py_argparse_call_with_names {
// py_argparse_call_with_names!(f, (lhs) (,plist,))
// = f(lhs, pnames...)
( $f:expr, $lhs:tt ( $(,)* )) => (
py_coerce_expr!{ $f $lhs }
);
( $f:expr, ($($lhs:tt)*) ( ,$pname:ident : $ptype:ty, $($r:tt)* )) => {
py_argparse_call_with_names!( $f, ($($lhs)* , $pname) ( ,$($r)* ))
};
}
*/
#[cfg(test)]
mod test {
use python::{Python, PythonObject};
@ -405,5 +406,19 @@ mod test {
}).unwrap();
assert!(called);
}
#[test]
pub fn test_default_param_type() {
let gil_guard = Python::acquire_gil();
let py = gil_guard.python();
let mut called = false;
let tuple = ("abc",).to_py_object(py);
py_argparse!(py, None, &tuple, None, (x) {
assert_eq!(*x, tuple.get_item(py, 0));
called = true;
Ok(())
}).unwrap();
assert!(called);
}
}

View file

@ -72,13 +72,16 @@ pub trait ToPyObject {
// -> into_py_object() allocates new Python object
}
py_impl_to_py_object_for_python_object!(PyObject);
/// FromPyObject is implemented by various types that can be extracted from a Python object.
///
/// Usage:
/// Normal usage is through the `PyObject::extract` helper method:
/// ```let obj: PyObject = ...;
/// let prepared = <TargetType as ExtractPyObject>::prepare_extract(&obj);
/// let extracted = try!(extract(&prepared));```
///
/// let value = try!(obj.extract::<TargetType>(py));
/// ```
///
/// TODO: update this documentation
/// Note: depending on the implementation, the lifetime of the extracted result may
/// depend on the lifetime of the `obj` or the `prepared` variable.
///
@ -91,14 +94,36 @@ pub trait ToPyObject {
///
/// In cases where the result does not depend on the `'prepared` lifetime,
/// the inherent method `PyObject::extract()` can be used.
pub trait ExtractPyObject<'prepared> : Sized {
type Prepared : 'static;
fn prepare_extract<'a, 'p>(py: Python<'p>, obj: &'a PyObject) -> PyResult<Self::Prepared>;
fn extract<'p>(py: Python<'p>, prepared: &'prepared Self::Prepared) -> PyResult<Self>;
pub trait FromPyObject<'source> : Sized {
/// Extracts `Self` from the source `PyObject`.
fn extract(py: Python, obj: &'source PyObject) -> PyResult<Self>;
}
py_impl_from_py_object_for_python_object!(PyObject);
pub trait RefFromPyObject {
fn with_extracted<F, R>(py: Python, obj: &PyObject, f: F) -> PyResult<R>
where F: FnOnce(&Self) -> R;
}
impl <T: ?Sized> RefFromPyObject for T
where for<'a> &'a T: FromPyObject<'a>
{
#[inline]
fn with_extracted<F, R>(py: Python, obj: &PyObject, f: F) -> PyResult<R>
where F: FnOnce(&Self) -> R
{
match FromPyObject::extract(py, obj) {
Ok(val) => Ok(f(val)),
Err(e) => Err(e)
}
}
}
/*
impl <'prepared, T> ExtractPyObject<'prepared> for T
where T: PythonObjectWithCheckedDowncast
{
@ -114,6 +139,7 @@ where T: PythonObjectWithCheckedDowncast
Ok(try!(obj.clone_ref(py).cast_into(py)))
}
}
*/
// ToPyObject for references
impl <'a, T: ?Sized> ToPyObject for &'a T where T: ToPyObject {
@ -157,7 +183,20 @@ impl <T> ToPyObject for Option<T> where T: ToPyObject {
}
}
impl <'source, T> FromPyObject<'source> for Option<T> where T: FromPyObject<'source> {
fn extract(py: Python, obj: &'source PyObject) -> PyResult<Self> {
if obj.as_ptr() == unsafe { ffi::Py_None() } {
Ok(None)
} else {
match T::extract(py, obj) {
Ok(v) => Ok(Some(v)),
Err(e) => Err(e)
}
}
}
}
/*
impl <'prepared, T> ExtractPyObject<'prepared> for Option<T>
where T: ExtractPyObject<'prepared>
{
@ -183,5 +222,5 @@ where T: ExtractPyObject<'prepared>
}
}
}
*/

View file

@ -97,7 +97,7 @@ pub use err::{PyErr, PyResult};
pub use objects::*;
pub use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectDowncastError, PythonObjectWithTypeObject, PyClone, PyDrop};
pub use pythonrun::{GILGuard, GILProtected, prepare_freethreaded_python};
pub use conversion::{ExtractPyObject, ToPyObject};
pub use conversion::{FromPyObject, RefFromPyObject, ToPyObject};
pub use objectprotocol::{ObjectProtocol};
#[cfg(feature="python27-sys")]
@ -131,6 +131,54 @@ macro_rules! py_replace_expr {
($_t:tt $sub:expr) => {$sub};
}
#[macro_export] #[doc(hidden)]
macro_rules! py_impl_to_py_object_for_python_object {
($T: ty) => (
/// Identity conversion: allows using existing `PyObject` instances where
/// `T: ToPyObject` is expected.
impl $crate::ToPyObject for $T {
type ObjectType = $T;
#[inline]
fn to_py_object(&self, py: $crate::Python) -> $T {
$crate::PyClone::clone_ref(self, py)
}
#[inline]
fn into_py_object(self, _py: $crate::Python) -> $T {
self
}
#[inline]
fn with_borrowed_ptr<F, R>(&self, _py: $crate::Python, f: F) -> R
where F: FnOnce(*mut $crate::_detail::ffi::PyObject) -> R
{
f($crate::PythonObject::as_object(self).as_ptr())
}
}
)
}
#[macro_export] #[doc(hidden)]
macro_rules! py_impl_from_py_object_for_python_object {
($T:ty) => {
impl <'source> $crate::FromPyObject<'source> for $T {
#[inline]
fn extract(py: $crate::Python, obj: &'source $crate::PyObject) -> $crate::PyResult<$T> {
use $crate::PyClone;
Ok(try!(obj.clone_ref(py).cast_into::<$T>(py)))
}
}
impl <'source> $crate::FromPyObject<'source> for &'source $T {
#[inline]
fn extract(py: $crate::Python, obj: &'source $crate::PyObject) -> $crate::PyResult<&'source $T> {
Ok(try!(obj.cast_as::<$T>(py)))
}
}
}
}
mod python;
mod err;
mod conversion;

View file

@ -2,7 +2,7 @@ use ffi;
use python::Python;
use err::PyResult;
use super::PyObject;
use conversion::{ExtractPyObject, ToPyObject};
use conversion::{ToPyObject};
/// Represents a Python `bool`.
pub struct PyBool(PyObject);

View file

@ -16,11 +16,11 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
use python::{Python, PythonObject, ToPythonPointer, PyClone};
use python::{Python, PythonObject, ToPythonPointer, PyClone, PyDrop};
use err::{self, PyErr, PyResult};
use super::object::PyObject;
use ffi::{self, Py_ssize_t};
use conversion::{ToPyObject, ExtractPyObject};
use conversion::{ToPyObject, FromPyObject};
/// Represents a Python `list`.
pub struct PyList(PyObject);
@ -123,28 +123,40 @@ impl <T> ToPyObject for [T] where T: ToPyObject {
}
}
impl <'prepared, T> ExtractPyObject<'prepared> for Vec<T>
where T: ExtractPyObject<'prepared>
{
type Prepared = Vec<T::Prepared>;
impl <T> ToPyObject for Vec<T> where T: ToPyObject {
type ObjectType = PyList;
fn prepare_extract(py: Python, obj: &PyObject) -> PyResult<Self::Prepared> {
fn to_py_object(&self, py: Python) -> PyList {
self.as_slice().to_py_object(py)
}
fn into_py_object(self, py: Python) -> PyList {
unsafe {
let ptr = ffi::PyList_New(self.len() as Py_ssize_t);
let t = err::cast_from_owned_ptr_or_panic(py, ptr);
for (i, e) in self.into_iter().enumerate() {
let obj = e.into_py_object(py).into_object();
ffi::PyList_SetItem(ptr, i as Py_ssize_t, obj.steal_ptr());
}
t
}
}
}
impl <'source, T> FromPyObject<'source> for Vec<T>
where for<'a> T: FromPyObject<'a>
{
fn extract(py: Python, obj: &'source PyObject) -> PyResult<Self> {
let list = try!(obj.cast_as::<PyList>(py));
let len = list.len(py);
let mut v = Vec::with_capacity(len);
for i in 0 .. len {
v.push(try!(T::prepare_extract(py, &list.get_item(py, i))));
let item = list.get_item(py, i);
v.push(try!(T::extract(py, &item)));
item.release_ref(py);
}
Ok(v)
}
fn extract(py: Python, prepared: &'prepared Self::Prepared) -> PyResult<Vec<T>> {
let mut v = Vec::with_capacity(prepared.len());
for prepared_elem in prepared {
v.push(try!(T::extract(py, prepared_elem)));
}
Ok(v)
}
}
#[cfg(test)]

View file

@ -38,38 +38,10 @@ pub use self::num::PyLong as PyInt;
pub use self::num::{PyLong, PyFloat};
pub use self::sequence::PySequence;
#[doc(hidden)]
#[macro_export]
macro_rules! pyobject_to_pyobject(
($name: ident) => (
/// Identity conversion: allows using existing `PyObject` instances where
/// `T: ToPyObject` is expected.
impl $crate::ToPyObject for $name {
type ObjectType = $name;
#[inline]
fn to_py_object(&self, py: $crate::Python) -> $name {
$crate::PyClone::clone_ref(self, py)
}
#[inline]
fn into_py_object(self, _py: $crate::Python) -> $name {
self
}
#[inline]
fn with_borrowed_ptr<F, R>(&self, _py: $crate::Python, f: F) -> R
where F: FnOnce(*mut $crate::_detail::ffi::PyObject) -> R
{
f($crate::PythonObject::as_object(self).as_ptr())
}
}
)
);
macro_rules! pyobject_newtype(
($name: ident) => (
pyobject_to_pyobject!($name);
py_impl_to_py_object_for_python_object!($name);
py_impl_from_py_object_for_python_object!($name);
impl $crate::PythonObject for $name {
#[inline]
@ -138,17 +110,10 @@ macro_rules! pyobject_newtype(
macro_rules! extract(
($obj:ident to $t:ty; $py:ident => $body: block) => {
impl <'prepared> ::conversion::ExtractPyObject<'prepared>
impl <'source> ::conversion::FromPyObject<'source>
for $t
{
type Prepared = PyObject;
#[inline]
fn prepare_extract(py: Python, obj: &PyObject) -> PyResult<Self::Prepared> {
Ok(::python::PyClone::clone_ref(obj, py))
}
fn extract($py: Python, $obj: &'prepared PyObject) -> PyResult<Self> {
fn extract($py: Python, $obj: &'source PyObject) -> PyResult<Self> {
$body
}
}

View file

@ -25,7 +25,7 @@ use err::{self, PyResult, PyErr};
use super::object::PyObject;
use super::exc;
use ffi;
use conversion::{ToPyObject, ExtractPyObject};
use conversion::{ToPyObject, FromPyObject};
/// Represents a Python `int` object.
///
@ -193,16 +193,9 @@ macro_rules! int_convert_u64_or_i64 (
}
}
impl <'prepared> ExtractPyObject<'prepared> for $rust_type {
type Prepared = PyObject;
#[inline]
fn prepare_extract(py: Python, obj: &PyObject) -> PyResult<Self::Prepared> {
Ok(obj.clone_ref(py))
}
impl <'source> FromPyObject<'source> for $rust_type {
#[cfg(feature="python27-sys")]
fn extract(py: Python, obj: &'prepared PyObject) -> PyResult<$rust_type> {
fn extract(py: Python, obj: &'source PyObject) -> PyResult<$rust_type> {
let ptr = obj.as_ptr();
unsafe {
@ -221,7 +214,7 @@ macro_rules! int_convert_u64_or_i64 (
}
#[cfg(feature="python3-sys")]
fn extract(py: Python, obj: &'prepared PyObject) -> PyResult<$rust_type> {
fn extract(py: Python, obj: &'source PyObject) -> PyResult<$rust_type> {
let ptr = obj.as_ptr();
unsafe {
if ffi::PyLong_Check(ptr) != 0 {

View file

@ -102,8 +102,6 @@ fn unpack_shared(ptr: *mut ffi::PyObject) -> *mut ffi::PyObject {
ptr
}
pyobject_to_pyobject!(PyObject);
impl PythonObject for PyObject {
#[inline]
fn as_object(&self) -> &PyObject {
@ -277,11 +275,10 @@ impl PyObject {
/// Extracts some type from the Python object.
/// This is a wrapper function around `FromPyObject::from_py_object()`.
#[inline]
pub fn extract<T>(&self, py: Python) -> PyResult<T>
where T: for<'prep> ::conversion::ExtractPyObject<'prep>
pub fn extract<'a, T>(&'a self, py: Python) -> PyResult<T>
where T: ::conversion::FromPyObject<'a>
{
let prepared = try!(<T as ::conversion::ExtractPyObject>::prepare_extract(py, self));
<T as ::conversion::ExtractPyObject>::extract(py, &prepared)
::conversion::FromPyObject::extract(py, self)
}
}

View file

@ -25,7 +25,7 @@ use ffi;
use python::{Python, PythonObject, PyClone, ToPythonPointer, PythonObjectDowncastError};
use super::{exc, PyObject};
use err::{self, PyResult, PyErr};
use conversion::{ExtractPyObject, ToPyObject};
use conversion::{FromPyObject, RefFromPyObject, ToPyObject};
/// Represents a Python string.
/// Corresponds to `basestring` in Python 2, and `str` in Python 3.
@ -288,10 +288,6 @@ impl PyString {
pub fn to_string_lossy(&self, py: Python) -> Cow<str> {
self.data(py).to_string_lossy()
}
fn extract<'a>(py: Python, o: &'a PyObject) -> PyResult<Cow<'a, str>> {
try!(o.cast_as::<PyString>(py)).to_string(py)
}
}
impl PyBytes {
@ -425,50 +421,34 @@ impl ToPyObject for String {
/// Allows extracting strings from Python objects.
/// Accepts Python `str` and `unicode` objects.
/// In Python 2.7, `str` is expected to be UTF-8 encoded.
extract!(obj to String; py => {
PyString::extract(py, obj).map(|s| s.into_owned())
});
impl <'source> FromPyObject<'source> for Cow<'source, str> {
fn extract(py: Python, obj: &'source PyObject) -> PyResult<Self> {
try!(obj.cast_as::<PyString>(py)).to_string(py)
}
}
/// Allows extracting strings from Python objects.
/// Accepts Python `str` and `unicode` objects.
/// In Python 2.7, `str` is expected to be UTF-8 encoded.
extract!(obj to Cow<'prepared, str>; py => {
PyString::extract(py, obj)
});
/// Used in `impl ExtractPyObject for &str`.
pub enum PreparedString {
Extracted(String),
BorrowFrom(PyObject)
impl <'source> FromPyObject<'source> for String {
fn extract(py: Python, obj: &'source PyObject) -> PyResult<Self> {
obj.extract::<Cow<str>>(py).map(Cow::into_owned)
}
}
impl <'prepared> ExtractPyObject<'prepared> for &'prepared str {
type Prepared = PreparedString;
fn prepare_extract(py: Python, obj: &PyObject) -> PyResult<Self::Prepared> {
match try!(PyString::extract(py, obj)) {
Cow::Owned(s) => Ok(PreparedString::Extracted(s)),
Cow::Borrowed(_) => Ok(PreparedString::BorrowFrom(obj.clone_ref(py)))
}
}
fn extract(py: Python, prepared: &'prepared PreparedString) -> PyResult<Self> {
match *prepared {
PreparedString::Extracted(ref s) => Ok(s),
PreparedString::BorrowFrom(ref obj) => {
match try!(PyString::extract(py, obj)) {
Cow::Owned(_) => panic!("Failed to borrow from python object"),
Cow::Borrowed(s) => Ok(s)
}
}
}
impl RefFromPyObject for str {
fn with_extracted<F, R>(py: Python, obj: &PyObject, f: F) -> PyResult<R>
where F: FnOnce(&str) -> R
{
let s = try!(obj.extract::<Cow<str>>(py));
Ok(f(&s))
}
}
#[cfg(test)]
mod test {
use python::{Python, PythonObject};
use conversion::{ToPyObject, ExtractPyObject};
use conversion::{ToPyObject, RefFromPyObject};
#[test]
fn test_non_bmp() {
@ -485,8 +465,13 @@ mod test {
let py = gil.python();
let s = "Hello Python";
let py_string = s.to_py_object(py).into_object();
let prepared = <&str>::prepare_extract(py, &py_string).unwrap();
assert_eq!(s, <&str>::extract(py, &prepared).unwrap());
let mut called = false;
RefFromPyObject::with_extracted(py, &py_string,
|s2: &str| {
assert_eq!(s, s2);
called = true;
}).unwrap();
assert!(called);
}
}

View file

@ -21,7 +21,7 @@ use err::{self, PyErr, PyResult};
use super::object::PyObject;
use super::exc;
use ffi::{self, Py_ssize_t};
use conversion::{ToPyObject, ExtractPyObject};
use conversion::ToPyObject;
use std::slice;
/// Represents a Python tuple object.
@ -159,21 +159,19 @@ fn wrong_tuple_length(py: Python, t: &PyTuple, expected_length: usize) -> PyErr
PyErr::new_lazy_init(py.get_type::<exc::ValueError>(), Some(msg.to_py_object(py).into_object()))
}
macro_rules! id (($a:expr) => ($a));
macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+} => (
impl <'p, $($T: ToPyObject),+> ToPyObject for ($($T,)+) {
type ObjectType = PyTuple;
fn to_py_object(&self, py: Python) -> PyTuple {
PyTuple::new(py, &[
$(id!(self.$n.to_py_object(py)).into_object(),)+
$(py_coerce_expr!(self.$n.to_py_object(py)).into_object(),)+
])
}
fn into_py_object(self, py: Python) -> PyTuple {
PyTuple::new(py, &[
$(id!(self.$n.into_py_object(py)).into_object(),)+
$(py_coerce_expr!(self.$n.into_py_object(py)).into_object(),)+
])
}
}

View file

@ -50,7 +50,8 @@ base_case = '''
} => {
struct $class { _unsafe_inner: $crate::PyObject }
pyobject_to_pyobject!($class);
py_impl_to_py_object_for_python_object!($class);
py_impl_from_py_object_for_python_object!($class);
impl $crate::PythonObject for $class {
#[inline]

View file

@ -42,7 +42,8 @@ macro_rules! py_class_impl {
} => {
struct $class { _unsafe_inner: $crate::PyObject }
pyobject_to_pyobject!($class);
py_impl_to_py_object_for_python_object!($class);
py_impl_from_py_object_for_python_object!($class);
impl $crate::PythonObject for $class {
#[inline]

View file

@ -42,7 +42,8 @@ macro_rules! py_class_impl {
} => {
struct $class { _unsafe_inner: $crate::PyObject }
pyobject_to_pyobject!($class);
py_impl_to_py_object_for_python_object!($class);
py_impl_from_py_object_for_python_object!($class);
impl $crate::PythonObject for $class {
#[inline]

View file

@ -273,13 +273,8 @@ macro_rules! py_class_binary_slot {
|py| {
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>();
let arg = $crate::PyObject::from_borrowed_ptr(py, arg);
let ret = match <$arg_type as $crate::ExtractPyObject>::prepare_extract(py, &arg) {
Ok(prepared) => {
match <$arg_type as $crate::ExtractPyObject>::extract(py, &prepared) {
Ok(arg) => slf.$f(py, arg),
Err(e) => Err(e)
}
},
let ret = match <$arg_type as $crate::FromPyObject>::extract(py, &arg) {
Ok(arg) => slf.$f(py, arg),
Err(e) => Err(e)
};
$crate::PyDrop::release_ref(arg, py);
@ -308,22 +303,10 @@ macro_rules! py_class_ternary_slot {
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>();
let arg1 = $crate::PyObject::from_borrowed_ptr(py, arg1);
let arg2 = $crate::PyObject::from_borrowed_ptr(py, arg2);
let ret = match <$arg1_type as $crate::ExtractPyObject>::prepare_extract(py, &arg1) {
Ok(prepared1) => {
match <$arg1_type as $crate::ExtractPyObject>::extract(py, &prepared1) {
Ok(arg1) => {
match <$arg2_type as $crate::ExtractPyObject>::prepare_extract(py, &arg2) {
Ok(prepared2) => {
match <$arg2_type as $crate::ExtractPyObject>::extract(py, &prepared2) {
Ok(arg2) => slf.$f(py, arg1, arg2),
Err(e) => Err(e)
}
},
Err(e) => Err(e)
}
},
Err(e) => Err(e)
}
let ret = match <$arg1_type as $crate::FromPyObject>::extract(py, &arg1) {
Ok(arg1) => match <$arg2_type as $crate::FromPyObject>::extract(py, &arg2) {
Ok(arg2) => slf.$f(py, arg1, arg2),
Err(e) => Err(e)
},
Err(e) => Err(e)
};
@ -353,13 +336,8 @@ macro_rules! py_class_contains_slot {
|py| {
let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>();
let arg = $crate::PyObject::from_borrowed_ptr(py, arg);
let ret = match <$arg_type as $crate::ExtractPyObject>::prepare_extract(py, &arg) {
Ok(prepared) => {
match <$arg_type as $crate::ExtractPyObject>::extract(py, &prepared) {
Ok(arg) => slf.$f(py, arg),
Err(e) => $crate::py_class::slots::type_error_to_false(py, e)
}
},
let ret = match <$arg_type as $crate::FromPyObject>::extract(py, &arg) {
Ok(arg) => slf.$f(py, arg),
Err(e) => $crate::py_class::slots::type_error_to_false(py, e)
};
$crate::PyDrop::release_ref(arg, py);
@ -404,14 +382,6 @@ macro_rules! py_class_binary_numeric_slot {
}}
}
pub fn type_error_to_not_implemented(py: Python, e: PyErr) -> PyResult<PyObject> {
if e.matches(py, py.get_type::<exc::TypeError>()) {
Ok(py.NotImplemented())
} else {
Err(e)
}
}
pub struct UnitCallbackConverter;
impl CallbackConverter<()> for UnitCallbackConverter {