introduce ToPyTuple trait, that simplifies ObjectProtoco.call method

This commit is contained in:
Nikolay Kim 2017-04-28 16:02:52 -07:00
parent f651b442ba
commit 8d728663d7
5 changed files with 56 additions and 10 deletions

View File

@ -21,7 +21,7 @@ use std::cmp::Ordering;
use ffi;
use libc;
use python::{Python, PythonObject, ToPythonPointer};
use objects::{PyObject, PyTuple, PyDict, PyString};
use objects::{PyObject, PyTuple, PyDict, PyString, ToPyTuple};
use conversion::ToPyObject;
use err::{PyErr, PyResult, self};
@ -182,7 +182,7 @@ pub trait ObjectProtocol : PythonObject {
/// This is equivalent to the Python expression: 'self(*args, **kwargs)'
#[inline]
fn call<A>(&self, py: Python, args: A, kwargs: Option<&PyDict>) -> PyResult<PyObject>
where A: ToPyObject<ObjectType=PyTuple>
where A: ToPyTuple
{
args.with_borrowed_ptr(py, |args| unsafe {
err::result_from_owned_ptr(py, ffi::PyObject_Call(self.as_ptr(), args, kwargs.as_ptr()))
@ -193,7 +193,7 @@ pub trait ObjectProtocol : PythonObject {
/// This is equivalent to the Python expression: 'self.name(*args, **kwargs)'
#[inline]
fn call_method<A>(&self, py: Python, name: &str, args: A, kwargs: Option<&PyDict>) -> PyResult<PyObject>
where A: ToPyObject<ObjectType=PyTuple>
where A: ToPyTuple
{
try!(self.getattr(py, name)).call(py, args, kwargs)
}

View File

@ -29,7 +29,7 @@ pub use self::string::PyString as PyUnicode;
pub use self::iterator::PyIterator;
pub use self::boolobject::PyBool;
pub use self::bytearray::PyByteArray;
pub use self::tuple::{PyTuple, NoArgs};
pub use self::tuple::{PyTuple, NoArgs, ToPyTuple};
pub use self::dict::PyDict;
pub use self::list::PyList;
#[cfg(feature="python27-sys")]

View File

@ -22,7 +22,7 @@ use libc::c_char;
use python::{Python, PythonObject, PyDrop};
use objectprotocol::ObjectProtocol;
use conversion::ToPyObject;
use objects::{PyObject, PyTuple, PyDict, exc};
use objects::{PyObject, PyTuple, PyDict, ToPyTuple, exc};
use py_class::PythonObjectFromPyClassMacro;
use err::{self, PyResult, PyErr};
use std::ffi::{CStr, CString};
@ -93,7 +93,7 @@ impl PyModule {
/// Calls a function in the module.
/// This is equivalent to the Python expression: `getattr(module, name)(*args, **kwargs)`
pub fn call<A>(&self, py: Python, name: &str, args: A, kwargs: Option<&PyDict>) -> PyResult<PyObject>
where A: ToPyObject<ObjectType=PyTuple>
where A: ToPyTuple
{
try!(self.as_object().getattr(py, name)).call(py, args, kwargs)
}

View File

@ -16,7 +16,7 @@
// 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, PyDrop};
use python::{Python, PythonObject, ToPythonPointer, PyClone, PyDrop};
use err::{self, PyErr, PyResult};
use super::object::PyObject;
use super::exc;
@ -24,6 +24,23 @@ use ffi::{self, Py_ssize_t};
use conversion::{FromPyObject, ToPyObject};
use std::slice;
/// Conversion trait that allows various objects to be converted into PyTuple object.
pub trait ToPyTuple {
/// Converts self into a PyTuple object.
fn to_py_tuple(&self, py: Python) -> PyTuple;
/// Converts self into a PyTuple object and calls the specified closure
/// on the native FFI pointer underlying the Python object.
#[inline]
fn with_borrowed_ptr<F, R>(&self, py: Python, f: F) -> R
where F: FnOnce(*mut ffi::PyObject) -> R
{
let obj = self.to_py_tuple(py).into_object();
f(obj.as_ptr())
}
}
/// Represents a Python tuple object.
pub struct PyTuple(PyObject);
@ -92,6 +109,18 @@ impl PyTuple {
}
}
impl ToPyTuple for PyTuple {
fn to_py_tuple(&self, py: Python) -> PyTuple {
self.clone_ref(py)
}
}
impl<'a> ToPyTuple for &'a str {
fn to_py_tuple(&self, py: Python) -> PyTuple {
PyTuple::new(py, &[py_coerce_expr!(self.to_py_object(py)).into_object()])
}
}
fn wrong_tuple_length(py: Python, t: &PyTuple, expected_length: usize) -> PyErr {
let msg = format!("Expected tuple of length {}, but got tuple of length {}.", expected_length, t.len(py));
PyErr::new_lazy_init(py.get_type::<exc::ValueError>(), Some(msg.to_py_object(py).into_object()))
@ -114,6 +143,15 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
}
}
impl <$($T: ToPyObject),+> ToPyTuple for ($($T,)+) {
fn to_py_tuple(&self, py: Python) -> PyTuple {
PyTuple::new(py, &[
$(py_coerce_expr!(self.$n.to_py_object(py)).into_object(),)+
])
}
}
impl <'s, $($T: FromPyObject<'s>),+> FromPyObject<'s> for ($($T,)+) {
fn extract(py: Python, obj: &'s PyObject) -> PyResult<Self> {
let t = try!(obj.cast_as::<PyTuple>(py));
@ -168,6 +206,15 @@ impl ToPyObject for NoArgs {
}
}
/// Converts `NoArgs` to an empty Python tuple.
impl ToPyTuple for NoArgs {
fn to_py_tuple(&self, py: Python) -> PyTuple {
PyTuple::empty(py)
}
}
/// Returns `Ok(NoArgs)` if the input is an empty Python tuple.
/// Otherwise, returns an error.
extract!(obj to NoArgs; py => {
@ -180,7 +227,6 @@ extract!(obj to NoArgs; py => {
});
#[cfg(test)]
mod test {
use python::{Python, PythonObject};

View File

@ -18,7 +18,7 @@
use python::{Python, PythonObject, ToPythonPointer};
use conversion::ToPyObject;
use objects::{PyObject, PyTuple, PyDict};
use objects::{PyObject, PyTuple, PyDict, ToPyTuple};
use err::{PyResult, result_from_owned_ptr};
use ffi;
use std::ffi::CStr;
@ -67,7 +67,7 @@ impl PyType {
/// 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: ToPyObject<ObjectType=PyTuple>
where A: ToPyTuple
{
args.with_borrowed_ptr(py, |args| unsafe {
result_from_owned_ptr(py, ffi::PyObject_Call(self.0.as_ptr(), args, kwargs.as_ptr()))