add IntoPyDictPointer impl for tuple

This commit is contained in:
Nikolay Kim 2017-08-03 15:42:32 -07:00
parent cd6558a19b
commit 06a0b0514b
4 changed files with 158 additions and 8 deletions

View File

@ -26,6 +26,76 @@ fn main() {
## `FromPyObject` and `RefFromPyObject` trait
## `*args` and `**kwargs` for python object call
There are several way how to pass positional and keyword arguments to python object call.
[`ObjectProtocol`][ObjectProtocol] trait
provides two methods:
* `call` - call callable python object.
* `call_method` - call specific method on the object.
Both methods accept `args` and `kwargs` arguments. `args` argument is generate over
[`IntoPyTuple`][IntoPyTuple] trait. So args could be `PyTuple` instance or
rust tuple with up to 10 elements. Or `NoArgs` object which represents empty tuple object.
```rust,ignore
extern crate pyo3;
use pyo3::prelude::*;
fn main() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = SomeObject::new();
// call object without empty arguments
obj.call(NoArgs, NoArg);
// call object with PyTuple
let args = PyTuple::new(py, &[arg1, arg2, arg3]);
obj.call(args, NoArg);
// pass arguments as rust tuple
let args = (arg1, arg2, arg3);
obj.call(args, NoArg);
}
```
`kwargs` argument is generate over
[`IntoPyDictPointer`][IntoPyDictPointer] trait. `HashMap` or `BTreeMap` could be used as
keyword arguments. rust tuple with up to 10 elements where each element is tuple with size 2
could be used as kwargs as well. Or `NoArgs` object can be used to indicate that
no keywords areguments are provided.
```rust,ignore
extern crate pyo3;
use pyo3::prelude::*;
fn main() {
let gil = Python::acquire_gil();
let py = gil.python();
let obj = SomeObject::new();
// call object with PyDict
let kwargs = PyDict::new(py);
kwargs.set_item(key, value);
obj.call(NoArg, kwargs);
// pass arguments as rust tuple
let kwargs = ((key1, val1), (key2, val2), (key3, val3));
obj.call(args, kwargs);
// pass arguments as HashMap
let mut kwargs = HashMap::<i32, i32>::new();
kwargs.insert(1, 1);
obj.call(args, kwargs);
}
```
TODO
[ToPyObject]: https://pyo3.github.io/PyO3/pyo3/trait.ToPyObject.html
@ -33,3 +103,5 @@ TODO
[PyObject]: https://pyo3.github.io/PyO3/pyo3/struct.PyObject.html
[IntoPyTuple]: https://pyo3.github.io/PyO3/pyo3/trait.IntoPyTuple.html
[PyTuple]: https://pyo3.github.io/PyO3/pyo3/struct.PyTuple.html
[ObjectProtocol]: https://pyo3.github.io/PyO3/pyo3/trait.ObjectProtocol.html
[IntoPyDictPointer]: https://pyo3.github.io/PyO3/pyo3/trait.IntoPyDictPointer.html

View File

@ -623,7 +623,7 @@ mod test {
let gil = Python::acquire_gil();
let py = gil.python();
let array = py.import("array").unwrap().call_method(
"array", ("f", (1.0, 1.5, 2.0, 2.5)), NoArgs).unwrap();
"array", ("f", (1.0, 1.5, 2.0, 2.5)), ::NoArgs).unwrap();
let buffer = PyBuffer::get(py, array.into()).unwrap();
assert_eq!(buffer.dimensions(), 1);
assert_eq!(buffer.item_count(), 4);

View File

@ -83,8 +83,15 @@ pub trait ObjectProtocol {
/// Calls a method on the object.
/// This is equivalent to the Python expression: 'self.name(*args, **kwargs)'
fn call_method<A, K>(&self, name: &str, args: A, kwargs: K)
-> PyResult<&PyObjectRef>
///
/// # Example
/// ```rust,ignore
/// let obj = SomePyObject::new();
/// let args = (arg1, arg2, arg3);
/// let kwargs = ((key1, value1), (key2, value2));
/// let pid = obj.call_mwthod("do_something", args, kwargs);
/// ```
fn call_method<A, K>(&self, name: &str, args: A, kwargs: K) -> PyResult<&PyObjectRef>
where A: IntoPyTuple,
K: IntoPyDictPointer;

View File

@ -252,6 +252,43 @@ impl <K, V> IntoPyDictPointer for collections::BTreeMap<K, V>
}
}
impl<K: ToPyObject, V: ToPyObject> IntoPyDictPointer for (K, V) {
default fn into_dict_ptr(self, py: Python) -> *mut ffi::PyObject {
let dict = PyDict::new(py);
dict.set_item(self.0, self.1).expect("Failed to set_item on dict");
dict.into_ptr()
}
}
macro_rules! dict_conversion ({$length:expr,$(($refN:ident, $n:tt, $T1:ident, $T2:ident)),+} => {
impl<$($T1: ToPyObject, $T2: ToPyObject),+> IntoPyDictPointer for ($(($T1,$T2),)+) {
fn into_dict_ptr(self, py: Python) -> *mut ffi::PyObject {
let dict = PyDict::new(py);
$(dict.set_item(self.$n.0, self.$n.1).expect("Failed to set_item on dict");)+;
dict.into_ptr()
}
}
});
dict_conversion!(1, (ref0, 0, A1, A2));
dict_conversion!(2, (ref0, 0, A1, A2), (ref1, 1, B1, B2));
dict_conversion!(3, (ref0, 0, A1, A2), (ref1, 1, B1, B2), (ref2, 2, C1, C2));
dict_conversion!(4, (ref0, 0, A1, A2), (ref1, 1, B1, B2), (ref2, 2, C1, C2),
(ref3, 3, D1, D2));
dict_conversion!(5, (ref0, 0, A1, A2), (ref1, 1, B1, B2), (ref2, 2, C1, C2),
(ref3, 3, D1, D2), (ref4, 4, E1, E2));
dict_conversion!(6, (ref0, 0, A1, A2), (ref1, 1, B1, B2), (ref2, 2, C1, C2),
(ref3, 3, D1, D2), (ref4, 4, E1, E2), (ref5, 5, F1, F2));
dict_conversion!(7, (ref0, 0, A1, A2), (ref1, 1, B1, B2), (ref2, 2, C1, C2),
(ref3, 3, D1, D2), (ref4, 4, E1, E2), (ref5, 5, F1, F2), (ref6, 6, G1, G2));
dict_conversion!(8, (ref0, 0, A1, A2), (ref1, 1, B1, B2), (ref2, 2, C1, C2),
(ref3, 3, D1, D2), (ref4, 4, E1, E2), (ref5, 5, F1, F2), (ref6, 6, G1, G2),
(ref7, 7, H1, H2));
dict_conversion!(9, (ref0, 0, A1, A2), (ref1, 1, B1, B2), (ref2, 2, C1, C2),
(ref3, 3, D1, D2), (ref4, 4, E1, E2), (ref5, 5, F1, F2), (ref6, 6, G1, G2),
(ref7, 7, H1, H2), (ref8, 8, I1, I2));
#[cfg(test)]
mod test {
use std::collections::{BTreeMap, HashMap};
@ -502,7 +539,7 @@ mod test {
let py_map = PyDict::try_from(m.as_ref(py)).unwrap();
assert!(py_map.len() == 1);
assert!( py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
assert!(py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
}
#[test]
@ -517,7 +554,7 @@ mod test {
let py_map = PyDict::try_from(m.as_ref(py)).unwrap();
assert!(py_map.len() == 1);
assert!( py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
assert!(py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
}
#[test]
@ -532,7 +569,7 @@ mod test {
let py_map = PyDict::try_from(m.as_ref(py)).unwrap();
assert!(py_map.len() == 1);
assert!( py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
assert!(py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
}
#[test]
@ -548,7 +585,7 @@ mod test {
let py_map = PyDict::try_from(ob.as_ref(py)).unwrap();
assert!(py_map.len() == 1);
assert!( py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
assert!(py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
}
#[test]
@ -563,7 +600,7 @@ mod test {
let py_map = PyDict::try_from(m.as_ref(py)).unwrap();
assert!(py_map.len() == 1);
assert!( py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
assert!(py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
}
#[test]
@ -578,7 +615,41 @@ mod test {
let ob = unsafe{PyObject::from_owned_ptr(py, m)};
let py_map = PyDict::try_from(ob.as_ref(py)).unwrap();
assert!(py_map.len() == 1);
assert!(py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
}
#[test]
fn test_tuple_into_dict() {
let gil = Python::acquire_gil();
let py = gil.python();
let m = ((1, 1),).into_dict_ptr(py);
let ob = unsafe{PyObject::from_owned_ptr(py, m)};
let py_map = PyDict::try_from(ob.as_ref(py)).unwrap();
assert!(py_map.len() == 1);
assert!( py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
let m = ((1, 1), (2, 3)).into_dict_ptr(py);
let ob = unsafe{PyObject::from_owned_ptr(py, m)};
let py_map = PyDict::try_from(ob.as_ref(py)).unwrap();
assert!(py_map.len() == 2);
assert!(py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
assert!(py_map.get_item(2).unwrap().extract::<i32>().unwrap() == 3);
}
#[test]
fn test_simple_tuple_into_dict() {
let gil = Python::acquire_gil();
let py = gil.python();
let m = (1, 1).into_dict_ptr(py);
let ob = unsafe{PyObject::from_owned_ptr(py, m)};
let py_map = PyDict::try_from(ob.as_ref(py)).unwrap();
assert!(py_map.len() == 1);
assert!(py_map.get_item(1).unwrap().extract::<i32>().unwrap() == 1);
}
}