diff --git a/extensions/hello.rs b/extensions/hello.rs index 8d8a8553..9cb91d83 100644 --- a/extensions/hello.rs +++ b/extensions/hello.rs @@ -11,13 +11,15 @@ py_module_initializer!(hello, inithello, PyInit_hello, |py, m| { Ok(()) }); -fn run(py: Python, args: &PyTuple, kwargs: &PyDict) -> PyResult { +fn run(py: Python, args: &PyTuple, kwargs: Option<&PyDict>) -> PyResult { 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()) } diff --git a/src/argparse.rs b/src/argparse.rs index d24e6c49..40032512 100644 --- a/src/argparse.rs +++ b/src/argparse.rs @@ -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 #[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); + } } diff --git a/src/conversion.rs b/src/conversion.rs index 59490874..a03a9b2c 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -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 = ::prepare_extract(&obj); -/// let extracted = try!(extract(&prepared));``` -/// +/// let value = try!(obj.extract::(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; - - fn extract<'p>(py: Python<'p>, prepared: &'prepared Self::Prepared) -> PyResult; +pub trait FromPyObject<'source> : Sized { + /// Extracts `Self` from the source `PyObject`. + fn extract(py: Python, obj: &'source PyObject) -> PyResult; } + +py_impl_from_py_object_for_python_object!(PyObject); + + + +pub trait RefFromPyObject { + fn with_extracted(py: Python, obj: &PyObject, f: F) -> PyResult + where F: FnOnce(&Self) -> R; +} + +impl RefFromPyObject for T + where for<'a> &'a T: FromPyObject<'a> +{ + #[inline] + fn with_extracted(py: Python, obj: &PyObject, f: F) -> PyResult + 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 ToPyObject for Option where T: ToPyObject { } } +impl <'source, T> FromPyObject<'source> for Option where T: FromPyObject<'source> { + fn extract(py: Python, obj: &'source PyObject) -> PyResult { + 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 where T: ExtractPyObject<'prepared> { @@ -183,5 +222,5 @@ where T: ExtractPyObject<'prepared> } } } - +*/ diff --git a/src/lib.rs b/src/lib.rs index 7f9c38c4..3e383353 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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(&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; diff --git a/src/objects/boolobject.rs b/src/objects/boolobject.rs index 88dc5623..8e4bc1e4 100644 --- a/src/objects/boolobject.rs +++ b/src/objects/boolobject.rs @@ -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); diff --git a/src/objects/list.rs b/src/objects/list.rs index 28f50a36..33019e0d 100644 --- a/src/objects/list.rs +++ b/src/objects/list.rs @@ -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 ToPyObject for [T] where T: ToPyObject { } } -impl <'prepared, T> ExtractPyObject<'prepared> for Vec - where T: ExtractPyObject<'prepared> -{ - type Prepared = Vec; +impl ToPyObject for Vec where T: ToPyObject { + type ObjectType = PyList; - fn prepare_extract(py: Python, obj: &PyObject) -> PyResult { + 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 + where for<'a> T: FromPyObject<'a> +{ + fn extract(py: Python, obj: &'source PyObject) -> PyResult { let list = try!(obj.cast_as::(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> { - 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)] diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 13bc3eef..080cde65 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -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(&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 { - Ok(::python::PyClone::clone_ref(obj, py)) - } - - fn extract($py: Python, $obj: &'prepared PyObject) -> PyResult { + fn extract($py: Python, $obj: &'source PyObject) -> PyResult { $body } } diff --git a/src/objects/num.rs b/src/objects/num.rs index 433789c5..39633a5e 100644 --- a/src/objects/num.rs +++ b/src/objects/num.rs @@ -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 { - 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 { diff --git a/src/objects/object.rs b/src/objects/object.rs index 59cc0866..09fad0e7 100644 --- a/src/objects/object.rs +++ b/src/objects/object.rs @@ -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(&self, py: Python) -> PyResult - where T: for<'prep> ::conversion::ExtractPyObject<'prep> + pub fn extract<'a, T>(&'a self, py: Python) -> PyResult + where T: ::conversion::FromPyObject<'a> { - let prepared = try!(::prepare_extract(py, self)); - ::extract(py, &prepared) + ::conversion::FromPyObject::extract(py, self) } } diff --git a/src/objects/string.rs b/src/objects/string.rs index c3004150..e51ef7d7 100644 --- a/src/objects/string.rs +++ b/src/objects/string.rs @@ -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 { self.data(py).to_string_lossy() } - - fn extract<'a>(py: Python, o: &'a PyObject) -> PyResult> { - try!(o.cast_as::(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 { + try!(obj.cast_as::(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 { + obj.extract::>(py).map(Cow::into_owned) + } } -impl <'prepared> ExtractPyObject<'prepared> for &'prepared str { - type Prepared = PreparedString; - - fn prepare_extract(py: Python, obj: &PyObject) -> PyResult { - 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 { - 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(py: Python, obj: &PyObject, f: F) -> PyResult + where F: FnOnce(&str) -> R + { + let s = try!(obj.extract::>(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); } } diff --git a/src/objects/tuple.rs b/src/objects/tuple.rs index efbcd3d8..fd3b0a33 100644 --- a/src/objects/tuple.rs +++ b/src/objects/tuple.rs @@ -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::(), 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(),)+ ]) } } diff --git a/src/py_class/py_class_impl.py b/src/py_class/py_class_impl.py index 85f02b09..91de39e9 100644 --- a/src/py_class/py_class_impl.py +++ b/src/py_class/py_class_impl.py @@ -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] diff --git a/src/py_class/py_class_impl2.rs b/src/py_class/py_class_impl2.rs index fdb761a4..52204af8 100644 --- a/src/py_class/py_class_impl2.rs +++ b/src/py_class/py_class_impl2.rs @@ -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] diff --git a/src/py_class/py_class_impl3.rs b/src/py_class/py_class_impl3.rs index ae0af322..d456aac8 100644 --- a/src/py_class/py_class_impl3.rs +++ b/src/py_class/py_class_impl3.rs @@ -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] diff --git a/src/py_class/slots.rs b/src/py_class/slots.rs index be4dd452..0da42927 100644 --- a/src/py_class/slots.rs +++ b/src/py_class/slots.rs @@ -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 { - if e.matches(py, py.get_type::()) { - Ok(py.NotImplemented()) - } else { - Err(e) - } -} - pub struct UnitCallbackConverter; impl CallbackConverter<()> for UnitCallbackConverter {