Add some tests for py_fn!()

This commit is contained in:
Daniel Grunwald 2015-11-07 16:52:20 +01:00
parent 32086a0ef5
commit c1682b68ac
6 changed files with 91 additions and 11 deletions

View File

@ -11,7 +11,7 @@ ifeq ($(PY),3)
FEATURES := python3-sys
ifdef PEP384
export PEP384=1
FEATURES := $(FEATURES),pep-384
FEATURES := $(FEATURES) pep-384
endif
endif

View File

@ -50,7 +50,7 @@ pub fn parse_args(
) -> PyResult<()>
{
assert!(params.len() == output.len());
let nargs = args.len();
let nargs = args.len(py);
let nkeywords = kwargs.map_or(0, |d| d.len(py));
if nargs + nkeywords > params.len() {
return Err(err::PyErr::new::<exc::TypeError, _>(py,

View File

@ -142,4 +142,5 @@ impl ToPyObject for PyFn {
}
}
// Tests for this file are in tests/test_function.rs

View File

@ -52,8 +52,7 @@ impl PyTuple {
/// Gets the length of the tuple.
#[inline]
pub fn len(&self) -> usize {
// Safe despite not taking a `Python` token, because tuples are immutable.
pub fn len(&self, _py: Python) -> usize {
unsafe {
// non-negative Py_ssize_t should always fit into Rust uint
ffi::PyTuple_GET_SIZE(self.0.as_ptr()) as usize
@ -66,7 +65,7 @@ impl PyTuple {
pub fn get_item(&self, py: Python, index: usize) -> PyObject {
// TODO: reconsider whether we should panic
// It's quite inconsistent that this method takes `Python` when `len()` does not.
assert!(index < self.len());
assert!(index < self.len(py));
unsafe {
PyObject::from_borrowed_ptr(py, ffi::PyTuple_GET_ITEM(self.0.as_ptr(), index as Py_ssize_t))
}
@ -76,12 +75,14 @@ impl PyTuple {
pub fn as_slice<'a>(&'a self) -> &'a [PyObject] {
// This is safe because PyObject has the same memory layout as *mut ffi::PyObject,
// and because tuples are immutable.
// (We don't even need a Python token, thanks to immutability)
unsafe {
let ptr = self.0.as_ptr() as *mut ffi::PyTupleObject;
PyObject::borrow_from_owned_ptr_slice(
slice::from_raw_parts(
(*ptr).ob_item.as_ptr(),
self.len()))
ffi::PyTuple_GET_SIZE(self.0.as_ptr()) as usize
))
}
}
@ -91,7 +92,7 @@ impl PyTuple {
py: py,
tuple: self,
index: 0,
end: self.len()
end: self.len(py)
}
}
}
@ -151,7 +152,7 @@ impl <'a, 'p> ExactSizeIterator for PyTupleIterator<'a, 'p> {
}
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());
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()))
}
@ -231,7 +232,7 @@ impl ToPyObject for NoArgs {
/// Otherwise, returns an error.
extract!(obj to NoArgs; py => {
let t = try!(obj.cast_as::<PyTuple>(py));
if t.len() == 0 {
if t.len(py) == 0 {
Ok(NoArgs)
} else {
Err(wrong_tuple_length(py, t, 0))
@ -250,7 +251,7 @@ mod test {
let gil = Python::acquire_gil();
let py = gil.python();
let tuple = (1, 2, 3).to_py_object(py);
assert_eq!(3, tuple.len());
assert_eq!(3, tuple.len(py));
}
}

View File

@ -172,7 +172,7 @@ impl <'p, T> PyRustTypeBuilder<'p, T> where T: 'static + Send {
where T2: 'static + Send, B2: PythonBaseObject
{
// Ensure we can't change the base after any callbacks are registered.
assert!(self.can_change_base && self.members.len() == 0,
assert!(self.can_change_base && self.members.is_empty(),
"base() must be called before any members are added to the type");
let base_type_obj: &PyType = base_type;
PyRustTypeBuilder {

78
tests/test_function.rs Normal file
View File

@ -0,0 +1,78 @@
#![feature(plugin)]
#![plugin(interpolate_idents)]
#[macro_use] extern crate cpython;
use cpython::{PyResult, Python, NoArgs, ToPyObject, ObjectProtocol, PyDict, PyTuple};
use std::sync::atomic;
use std::sync::atomic::Ordering::Relaxed;
#[test]
fn no_args() {
static CALL_COUNT: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT;
fn f(_py: Python) -> PyResult<usize> {
Ok(CALL_COUNT.fetch_add(1, Relaxed))
}
let gil = Python::acquire_gil();
let py = gil.python();
let obj = py_fn!(f()).to_py_object(py);
assert_eq!(CALL_COUNT.load(Relaxed), 0);
assert_eq!(obj.call(py, NoArgs, None).unwrap().extract::<i32>(py).unwrap(), 0);
assert_eq!(obj.call(py, NoArgs, None).unwrap().extract::<i32>(py).unwrap(), 1);
assert_eq!(CALL_COUNT.load(Relaxed), 2);
assert!(obj.call(py, (1,), None).is_err());
assert_eq!(CALL_COUNT.load(Relaxed), 2);
assert_eq!(obj.call(py, NoArgs, Some(&PyDict::new(py))).unwrap().extract::<i32>(py).unwrap(), 2);
assert_eq!(CALL_COUNT.load(Relaxed), 3);
let dict = PyDict::new(py);
dict.set_item(py, "param", 42).unwrap();
assert!(obj.call(py, NoArgs, Some(&dict)).is_err());
assert_eq!(CALL_COUNT.load(Relaxed), 3);
}
#[test]
fn one_arg() {
fn f(_py: Python, i: usize) -> PyResult<usize> {
Ok(i * 2)
}
let gil = Python::acquire_gil();
let py = gil.python();
let obj = py_fn!(f(i: usize)).to_py_object(py);
assert!(obj.call(py, NoArgs, None).is_err());
assert_eq!(obj.call(py, (1,), None).unwrap().extract::<i32>(py).unwrap(), 2);
assert!(obj.call(py, (1, 2), None).is_err());
let dict = PyDict::new(py);
dict.set_item(py, "i", 42).unwrap();
assert_eq!(obj.call(py, NoArgs, Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 84);
assert!(obj.call(py, (1,), Some(&dict)).is_err());
dict.set_item(py, "j", 10).unwrap();
assert!(obj.call(py, NoArgs, Some(&dict)).is_err());
}
#[test]
fn flexible_sig() {
fn f(py: Python, args: &PyTuple, kwargs: Option<&PyDict>) -> PyResult<usize> {
Ok(args.len(py) + 100 * kwargs.map_or(0, |kwargs| kwargs.len(py)))
}
let gil = Python::acquire_gil();
let py = gil.python();
let obj = py_fn!(f).to_py_object(py);
assert_eq!(obj.call(py, NoArgs, None).unwrap().extract::<i32>(py).unwrap(), 0);
assert_eq!(obj.call(py, (1,), None).unwrap().extract::<i32>(py).unwrap(), 1);
assert_eq!(obj.call(py, (1,2), None).unwrap().extract::<i32>(py).unwrap(), 2);
let dict = PyDict::new(py);
dict.set_item(py, "i", 42).unwrap();
assert_eq!(obj.call(py, NoArgs, Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 100);
assert_eq!(obj.call(py, (1,2), Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 102);
dict.set_item(py, "j", 10).unwrap();
assert_eq!(obj.call(py, (1,2,3), Some(&dict)).unwrap().extract::<i32>(py).unwrap(), 203);
}