Add some tests for py_fn!()
This commit is contained in:
parent
32086a0ef5
commit
c1682b68ac
2
Makefile
2
Makefile
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -142,4 +142,5 @@ impl ToPyObject for PyFn {
|
|||
}
|
||||
}
|
||||
|
||||
// Tests for this file are in tests/test_function.rs
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
78
tests/test_function.rs
Normal 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);
|
||||
}
|
||||
|
Loading…
Reference in a new issue