diff --git a/tests/common.rs b/tests/common.rs new file mode 100644 index 00000000..74787a46 --- /dev/null +++ b/tests/common.rs @@ -0,0 +1,26 @@ +#[macro_export] +macro_rules! py_run { + ($py:expr, $val:ident, $code:expr) => {{ + let d = PyDict::new($py); + d.set_item(stringify!($val), &$val).unwrap(); + $py.run($code, None, Some(d)).map_err(|e| e.print($py)).expect($code); + }} +} + +#[macro_export] +macro_rules! py_assert { + ($py:expr, $val:ident, $assertion:expr) => { py_run!($py, $val, concat!("assert ", $assertion)) }; +} + +#[macro_export] +macro_rules! py_expect_exception { + ($py:expr, $val:ident, $code:expr, $err:ident) => {{ + let d = PyDict::new($py); + d.set_item(stringify!($val), &$val).unwrap(); + let res = $py.run($code, None, Some(d)); + let err = res.unwrap_err(); + if !err.matches($py, $py.get_type::()) { + panic!(format!("Expected {} but got {:?}", stringify!($err), err)) + } + }} +} diff --git a/tests/test_arithmetics.rs b/tests/test_arithmetics.rs new file mode 100644 index 00000000..05ae3fce --- /dev/null +++ b/tests/test_arithmetics.rs @@ -0,0 +1,305 @@ +#![feature(proc_macro, specialization)] + +extern crate pyo3; + +use pyo3::prelude::*; + +use pyo3::py::class as pyclass; +use pyo3::py::proto as pyproto; + + +#[macro_use] +mod common; + +#[pyclass] +struct UnaryArithmetic {token: PyToken} + +#[pyproto] +impl PyNumberProtocol for UnaryArithmetic { + + fn __neg__(&self) -> PyResult<&'static str> { + Ok("neg") + } + + fn __pos__(&self) -> PyResult<&'static str> { + Ok("pos") + } + + fn __abs__(&self) -> PyResult<&'static str> { + Ok("abs") + } + + fn __invert__(&self) -> PyResult<&'static str> { + Ok("invert") + } +} + +#[test] +fn unary_arithmetic() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let c = py.init(|t| UnaryArithmetic{token: t}).unwrap(); + py_run!(py, c, "assert -c == 'neg'"); + py_run!(py, c, "assert +c == 'pos'"); + py_run!(py, c, "assert abs(c) == 'abs'"); + py_run!(py, c, "assert ~c == 'invert'"); +} + + +#[pyclass] +struct BinaryArithmetic { + token: PyToken +} + +#[pyproto] +impl PyObjectProtocol for BinaryArithmetic { + fn __repr__(&self) -> PyResult<&'static str> { + Ok("BA") + } +} + + +#[pyclass] +struct InPlaceOperations { + value: u32, + token: PyToken, +} + +#[pyproto] +impl PyObjectProtocol for InPlaceOperations { + fn __repr__(&self) -> PyResult { + Ok(format!("IPO({:?})", self.value)) + } +} + +#[pyproto] +impl PyNumberProtocol for InPlaceOperations { + fn __iadd__(&mut self, other: u32) -> PyResult<()> { + self.value += other; + Ok(()) + } + + fn __isub__(&mut self, other: u32) -> PyResult<()> { + self.value -= other; + Ok(()) + } + + fn __imul__(&mut self, other: u32) -> PyResult<()> { + self.value *= other; + Ok(()) + } + + fn __ilshift__(&mut self, other: u32) -> PyResult<()> { + self.value <<= other; + Ok(()) + } + + fn __irshift__(&mut self, other: u32) -> PyResult<()> { + self.value >>= other; + Ok(()) + } + + fn __iand__(&mut self, other: u32) -> PyResult<()> { + self.value &= other; + Ok(()) + } + + fn __ixor__(&mut self, other: u32) -> PyResult<()> { + self.value ^= other; + Ok(()) + } + + fn __ior__(&mut self, other: u32) -> PyResult<()> { + self.value |= other; + Ok(()) + } +} + +#[test] +fn inplace_operations() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let c = py.init(|t| InPlaceOperations{value: 0, token: t}).unwrap(); + py_run!(py, c, "d = c; c += 1; assert repr(c) == repr(d) == 'IPO(1)'"); + + let c = py.init(|t| InPlaceOperations{value:10, token: t}).unwrap(); + py_run!(py, c, "d = c; c -= 1; assert repr(c) == repr(d) == 'IPO(9)'"); + + let c = py.init(|t| InPlaceOperations{value: 3, token: t}).unwrap(); + py_run!(py, c, "d = c; c *= 3; assert repr(c) == repr(d) == 'IPO(9)'"); + + let c = py.init(|t| InPlaceOperations{value: 3, token: t}).unwrap(); + py_run!(py, c, "d = c; c <<= 2; assert repr(c) == repr(d) == 'IPO(12)'"); + + let c = py.init(|t| InPlaceOperations{value: 12, token: t}).unwrap(); + py_run!(py, c, "d = c; c >>= 2; assert repr(c) == repr(d) == 'IPO(3)'"); + + let c = py.init(|t| InPlaceOperations{value: 12, token: t}).unwrap(); + py_run!(py, c, "d = c; c &= 10; assert repr(c) == repr(d) == 'IPO(8)'"); + + let c = py.init(|t| InPlaceOperations{value: 12, token: t}).unwrap(); + py_run!(py, c, "d = c; c |= 3; assert repr(c) == repr(d) == 'IPO(15)'"); + + let c = py.init(|t| InPlaceOperations{value: 12, token: t}).unwrap(); + py_run!(py, c, "d = c; c ^= 5; assert repr(c) == repr(d) == 'IPO(9)'"); +} + +#[pyproto] +impl PyNumberProtocol for BinaryArithmetic { + fn __add__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { + Ok(format!("{:?} + {:?}", lhs, rhs)) + } + + fn __sub__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { + Ok(format!("{:?} - {:?}", lhs, rhs)) + } + + fn __mul__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { + Ok(format!("{:?} * {:?}", lhs, rhs)) + } + + fn __lshift__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { + Ok(format!("{:?} << {:?}", lhs, rhs)) + } + + fn __rshift__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { + Ok(format!("{:?} >> {:?}", lhs, rhs)) + } + + fn __and__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { + Ok(format!("{:?} & {:?}", lhs, rhs)) + } + + fn __xor__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { + Ok(format!("{:?} ^ {:?}", lhs, rhs)) + } + + fn __or__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { + Ok(format!("{:?} | {:?}", lhs, rhs)) + } +} + +#[test] +fn binary_arithmetic() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let c = py.init(|t| BinaryArithmetic{token: t}).unwrap(); + py_run!(py, c, "assert c + c == 'BA + BA'"); + py_run!(py, c, "assert c + 1 == 'BA + 1'"); + py_run!(py, c, "assert 1 + c == '1 + BA'"); + py_run!(py, c, "assert c - 1 == 'BA - 1'"); + py_run!(py, c, "assert 1 - c == '1 - BA'"); + py_run!(py, c, "assert c * 1 == 'BA * 1'"); + py_run!(py, c, "assert 1 * c == '1 * BA'"); + + py_run!(py, c, "assert c << 1 == 'BA << 1'"); + py_run!(py, c, "assert 1 << c == '1 << BA'"); + py_run!(py, c, "assert c >> 1 == 'BA >> 1'"); + py_run!(py, c, "assert 1 >> c == '1 >> BA'"); + py_run!(py, c, "assert c & 1 == 'BA & 1'"); + py_run!(py, c, "assert 1 & c == '1 & BA'"); + py_run!(py, c, "assert c ^ 1 == 'BA ^ 1'"); + py_run!(py, c, "assert 1 ^ c == '1 ^ BA'"); + py_run!(py, c, "assert c | 1 == 'BA | 1'"); + py_run!(py, c, "assert 1 | c == '1 | BA'"); +} + + +#[pyclass] +struct RichComparisons { + token: PyToken +} + +#[pyproto] +impl PyObjectProtocol for RichComparisons { + fn __repr__(&self) -> PyResult<&'static str> { + Ok("RC") + } + + fn __richcmp__(&self, other: &PyObjectRef, op: CompareOp) -> PyResult { + match op { + CompareOp::Lt => Ok(format!("{} < {:?}", self.__repr__().unwrap(), other)), + CompareOp::Le => Ok(format!("{} <= {:?}", self.__repr__().unwrap(), other)), + CompareOp::Eq => Ok(format!("{} == {:?}", self.__repr__().unwrap(), other)), + CompareOp::Ne => Ok(format!("{} != {:?}", self.__repr__().unwrap(), other)), + CompareOp::Gt => Ok(format!("{} > {:?}", self.__repr__().unwrap(), other)), + CompareOp::Ge => Ok(format!("{} >= {:?}", self.__repr__().unwrap(), other)) + } + } +} + +#[pyclass] +struct RichComparisons2 { + py: PyToken +} + +#[pyproto] +impl PyObjectProtocol for RichComparisons2 { + fn __repr__(&self) -> PyResult<&'static str> { + Ok("RC2") + } + + fn __richcmp__(&self, _other: &'p PyObjectRef, op: CompareOp) -> PyResult { + match op { + CompareOp::Eq => Ok(true.to_object(self.py())), + CompareOp::Ne => Ok(false.to_object(self.py())), + _ => Ok(self.py().NotImplemented()) + } + } +} + +#[test] +fn rich_comparisons() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let c = py.init(|t| RichComparisons{token: t}).unwrap(); + py_run!(py, c, "assert (c < c) == 'RC < RC'"); + py_run!(py, c, "assert (c < 1) == 'RC < 1'"); + py_run!(py, c, "assert (1 < c) == 'RC > 1'"); + py_run!(py, c, "assert (c <= c) == 'RC <= RC'"); + py_run!(py, c, "assert (c <= 1) == 'RC <= 1'"); + py_run!(py, c, "assert (1 <= c) == 'RC >= 1'"); + py_run!(py, c, "assert (c == c) == 'RC == RC'"); + py_run!(py, c, "assert (c == 1) == 'RC == 1'"); + py_run!(py, c, "assert (1 == c) == 'RC == 1'"); + py_run!(py, c, "assert (c != c) == 'RC != RC'"); + py_run!(py, c, "assert (c != 1) == 'RC != 1'"); + py_run!(py, c, "assert (1 != c) == 'RC != 1'"); + py_run!(py, c, "assert (c > c) == 'RC > RC'"); + py_run!(py, c, "assert (c > 1) == 'RC > 1'"); + py_run!(py, c, "assert (1 > c) == 'RC < 1'"); + py_run!(py, c, "assert (c >= c) == 'RC >= RC'"); + py_run!(py, c, "assert (c >= 1) == 'RC >= 1'"); + py_run!(py, c, "assert (1 >= c) == 'RC <= 1'"); +} + +#[test] +#[cfg(Py_3)] +fn rich_comparisons_python_3_type_error() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let c2 = py.init(|t| RichComparisons2{py: t}).unwrap(); + py_expect_exception!(py, c2, "c2 < c2", TypeError); + py_expect_exception!(py, c2, "c2 < 1", TypeError); + py_expect_exception!(py, c2, "1 < c2", TypeError); + py_expect_exception!(py, c2, "c2 <= c2", TypeError); + py_expect_exception!(py, c2, "c2 <= 1", TypeError); + py_expect_exception!(py, c2, "1 <= c2", TypeError); + py_run!(py, c2, "assert (c2 == c2) == True"); + py_run!(py, c2, "assert (c2 == 1) == True"); + py_run!(py, c2, "assert (1 == c2) == True"); + py_run!(py, c2, "assert (c2 != c2) == False"); + py_run!(py, c2, "assert (c2 != 1) == False"); + py_run!(py, c2, "assert (1 != c2) == False"); + py_expect_exception!(py, c2, "c2 > c2", TypeError); + py_expect_exception!(py, c2, "c2 > 1", TypeError); + py_expect_exception!(py, c2, "1 > c2", TypeError); + py_expect_exception!(py, c2, "c2 >= c2", TypeError); + py_expect_exception!(py, c2, "c2 >= 1", TypeError); + py_expect_exception!(py, c2, "1 >= c2", TypeError); +} \ No newline at end of file diff --git a/tests/test_buffer_protocol.rs b/tests/test_buffer_protocol.rs index 557a1688..24e9e64c 100644 --- a/tests/test_buffer_protocol.rs +++ b/tests/test_buffer_protocol.rs @@ -1,12 +1,12 @@ -#![allow(dead_code, unused_variables)] -#![feature(proc_macro, specialization, const_fn, const_align_of, const_size_of)] +#![feature(proc_macro, specialization)] extern crate pyo3; use std::ptr; use std::os::raw::{c_int, c_void}; -use pyo3::*; +use pyo3::prelude::*; +use pyo3::ffi; use pyo3::py::class as pyclass; use pyo3::py::proto as pyproto; @@ -19,7 +19,7 @@ struct TestClass { } #[pyproto] -impl class::PyBufferProtocol for TestClass { +impl PyBufferProtocol for TestClass { fn bf_getbuffer(&self, view: *mut ffi::Py_buffer, flags: c_int) -> PyResult<()> { if view.is_null() { diff --git a/tests/test_class.rs b/tests/test_class.rs deleted file mode 100644 index 5eb3a004..00000000 --- a/tests/test_class.rs +++ /dev/null @@ -1,1450 +0,0 @@ -#![feature(proc_macro, specialization, const_fn, const_align_of, const_size_of)] -#![allow(dead_code, unused_variables)] - -extern crate pyo3; - -use pyo3::*; -use std::{isize, iter}; -use std::cell::RefCell; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; -use pyo3::ffi; - -use pyo3::py::class as pyclass; -use pyo3::py::methods as pymethods; -use pyo3::py::proto as pyproto; - -macro_rules! py_run { - ($py:expr, $val:ident, $code:expr) => {{ - let d = PyDict::new($py); - d.set_item(stringify!($val), &$val).unwrap(); - $py.run($code, None, Some(d)).map_err(|e| e.print($py)).expect($code); - }} -} - -macro_rules! py_assert { - ($py:expr, $val:ident, $assertion:expr) => { py_run!($py, $val, concat!("assert ", $assertion)) }; -} - -macro_rules! py_expect_exception { - ($py:expr, $val:ident, $code:expr, $err:ident) => {{ - let d = PyDict::new($py); - d.set_item(stringify!($val), &$val).unwrap(); - let res = $py.run($code, None, Some(d)); - let err = res.unwrap_err(); - if !err.matches($py, $py.get_type::()) { - panic!(format!("Expected {} but got {:?}", stringify!($err), err)) - } - }} -} - -#[pyclass] -struct EmptyClass { } - -#[test] -fn empty_class() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let typeobj = py.get_type::(); - // By default, don't allow creating instances from python. - assert!(typeobj.call(NoArgs, NoArgs).is_err()); - - py_assert!(py, typeobj, "typeobj.__name__ == 'EmptyClass'"); -} - -/// Line1 -///Line2 -/// Line3 -// this is not doc string -#[pyclass] -struct ClassWithDocs { } - -#[test] -fn class_with_docstr() { - { - let gil = Python::acquire_gil(); - let py = gil.python(); - let typeobj = py.get_type::(); - py_run!(py, typeobj, "assert typeobj.__doc__ == 'Line1\\nLine2\\n Line3'"); - } -} - -#[pyclass(name=CustomName)] -struct EmptyClass2 { } - -#[test] -fn custom_class_name() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let typeobj = py.get_type::(); - py_assert!(py, typeobj, "typeobj.__name__ == 'CustomName'"); -} - -#[pyclass] -struct EmptyClassInModule { } - -#[test] -fn empty_class_in_module() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let module = PyModule::new(py, "test_module.nested").unwrap(); - module.add_class::().unwrap(); - - let ty = module.getattr("EmptyClassInModule").unwrap(); - assert_eq!(ty.getattr("__name__").unwrap().extract::().unwrap(), "EmptyClassInModule"); - assert_eq!(ty.getattr("__module__").unwrap().extract::().unwrap(), "test_module.nested"); -} - -#[pyclass] -struct EmptyClassWithNew { - token: PyToken -} - -#[pymethods] -impl EmptyClassWithNew { - #[__new__] - fn __new__(obj: &PyRawObject) -> PyResult<()> { - obj.init(|t| EmptyClassWithNew{token: t}) - } -} - -#[test] -fn empty_class_with_new() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let typeobj = py.get_type::(); - assert!(typeobj.call(NoArgs, NoArgs).unwrap().cast_as::().is_ok()); -} - -#[pyclass] -struct NewWithOneArg { - _data: i32, - token: PyToken -} - -#[pymethods] -impl NewWithOneArg { - #[new] - fn __new__(obj: &PyRawObject, arg: i32) -> PyResult<()> { - obj.init(|t| NewWithOneArg{_data: arg, token: t}) - } -} - -#[test] -fn new_with_one_arg() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let typeobj = py.get_type::(); - let wrp = typeobj.call((42,), NoArgs).unwrap(); - let obj = wrp.cast_as::().unwrap(); - assert_eq!(obj._data, 42); -} - -#[pyclass] -struct NewWithTwoArgs { - _data1: i32, - _data2: i32, - - token: PyToken -} - -#[pymethods] -impl NewWithTwoArgs { - #[new] - fn __new__(obj: &PyRawObject, arg1: i32, arg2: i32) -> PyResult<()> - { - obj.init(|t| NewWithTwoArgs{_data1: arg1, _data2: arg2, token: t}) - } -} - -#[test] -fn new_with_two_args() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let typeobj = py.get_type::(); - let wrp = typeobj.call((10, 20), NoArgs).map_err(|e| e.print(py)).unwrap(); - let obj = wrp.cast_as::().unwrap(); - assert_eq!(obj._data1, 10); - assert_eq!(obj._data2, 20); -} - -#[pyclass(freelist=2)] -struct ClassWithFreelist{token: PyToken} - -#[test] -fn class_with_freelist() { - let ptr; - { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let inst = Py::new(py, |t| ClassWithFreelist{token: t}).unwrap(); - let inst2 = Py::new(py, |t| ClassWithFreelist{token: t}).unwrap(); - ptr = inst.as_ptr(); - drop(inst); - } - - { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let inst3 = Py::new(py, |t| ClassWithFreelist{token: t}).unwrap(); - assert_eq!(ptr, inst3.as_ptr()); - - let inst4 = Py::new(py, |t| ClassWithFreelist{token: t}).unwrap(); - assert_ne!(ptr, inst4.as_ptr()) - } -} - -struct TestDropCall { - drop_called: Arc -} -impl Drop for TestDropCall { - fn drop(&mut self) { - self.drop_called.store(true, Ordering::Relaxed); - } -} - -#[pyclass] -struct DataIsDropped { - member1: TestDropCall, - member2: TestDropCall, - token: PyToken, -} - -#[test] -fn data_is_dropped() { - let drop_called1 = Arc::new(AtomicBool::new(false)); - let drop_called2 = Arc::new(AtomicBool::new(false)); - - { - let gil = Python::acquire_gil(); - let py = gil.python(); - let inst = py.init(|t| DataIsDropped{ - member1: TestDropCall { drop_called: Arc::clone(&drop_called1) }, - member2: TestDropCall { drop_called: Arc::clone(&drop_called2) }, - token: t - }).unwrap(); - assert!(!drop_called1.load(Ordering::Relaxed)); - assert!(!drop_called2.load(Ordering::Relaxed)); - drop(inst); - } - - assert!(drop_called1.load(Ordering::Relaxed)); - assert!(drop_called2.load(Ordering::Relaxed)); -} - -#[pyclass] -struct ClassWithDrop { - token: PyToken, -} -impl Drop for ClassWithDrop { - fn drop(&mut self) { - unsafe { - let py = Python::assume_gil_acquired(); - - let _empty1 = PyTuple::empty(py); - let _empty2: PyObject = PyTuple::empty(py).into(); - let _empty3: &PyObjectRef = py.from_owned_ptr(ffi::PyTuple_New(0)); - } - } -} - -// Test behavior of pythonrun::register_pointers + typeob::dealloc -#[test] -fn create_pointers_in_drop() { - let gil = Python::acquire_gil(); - - let ptr; - let cnt; - { - let gil = Python::acquire_gil(); - let py = gil.python(); - let empty = PyTuple::empty(py); - ptr = empty.as_ptr(); - cnt = empty.get_refcnt() - 1; - let inst = py.init(|t| ClassWithDrop{token: t}).unwrap(); - drop(inst); - } - - // empty1 and empty2 are still alive (stored in pointers list) - { - let _gil = Python::acquire_gil(); - assert_eq!(cnt + 2, unsafe {ffi::Py_REFCNT(ptr)}); - } - - // empty1 and empty2 should be released - { - let _gil = Python::acquire_gil(); - assert_eq!(cnt, unsafe {ffi::Py_REFCNT(ptr)}); - } -} - -#[pyclass] -struct InstanceMethod { - member: i32, - token: PyToken -} - -#[pymethods] -impl InstanceMethod { - /// Test method - fn method(&self) -> PyResult { - Ok(self.member) - } -} - -#[test] -fn instance_method() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let obj = py.init_ref(|t| InstanceMethod{member: 42, token: t}).unwrap(); - assert!(obj.method().unwrap() == 42); - let d = PyDict::new(py); - d.set_item("obj", obj).unwrap(); - py.run("assert obj.method() == 42", None, Some(d)).unwrap(); - py.run("assert obj.method.__doc__ == 'Test method'", None, Some(d)).unwrap(); -} - -#[pyclass] -struct InstanceMethodWithArgs { - member: i32, - token: PyToken -} - -#[pymethods] -impl InstanceMethodWithArgs { - fn method(&self, multiplier: i32) -> PyResult { - Ok(self.member * multiplier) - } -} - -//#[test] -fn instance_method_with_args() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let obj = py.init_ref(|t| InstanceMethodWithArgs{member: 7, token: t}).unwrap(); - assert!(obj.method(6).unwrap() == 42); - let d = PyDict::new(py); - d.set_item("obj", obj).unwrap(); - py.run("assert obj.method(3) == 21", None, Some(d)).unwrap(); - py.run("assert obj.method(multiplier=6) == 42", None, Some(d)).unwrap(); -} - - -#[pyclass] -struct ClassMethod {token: PyToken} - -#[pymethods] -impl ClassMethod { - #[new] - fn __new__(obj: &PyRawObject) -> PyResult<()> { - obj.init(|t| ClassMethod{token: t}) - } - - #[classmethod] - fn method(cls: &PyType) -> PyResult { - Ok(format!("{}.method()!", cls.name())) - } -} - -#[test] -fn class_method() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let d = PyDict::new(py); - d.set_item("C", py.get_type::()).unwrap(); - py.run("assert C.method() == 'ClassMethod.method()!'", None, Some(d)).unwrap(); - py.run("assert C().method() == 'ClassMethod.method()!'", None, Some(d)).unwrap(); -} - - -#[pyclass] -struct ClassMethodWithArgs{token: PyToken} - -#[pymethods] -impl ClassMethodWithArgs { - #[classmethod] - fn method(cls: &PyType, input: &PyString) -> PyResult { - Ok(format!("{}.method({})", cls.name(), input)) - } -} - -#[test] -fn class_method_with_args() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let d = PyDict::new(py); - d.set_item("C", py.get_type::()).unwrap(); - py.run("assert C.method('abc') == 'ClassMethodWithArgs.method(abc)'", None, Some(d)).unwrap(); -} - -#[pyclass] -struct StaticMethod { - token: PyToken -} - -#[pymethods] -impl StaticMethod { - #[new] - fn __new__(obj: &PyRawObject) -> PyResult<()> { - obj.init(|t| StaticMethod{token: t}) - } - - #[staticmethod] - fn method(py: Python) -> PyResult<&'static str> { - Ok("StaticMethod.method()!") - } -} - -#[test] -fn static_method() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - assert_eq!(StaticMethod::method(py).unwrap(), "StaticMethod.method()!"); - let d = PyDict::new(py); - d.set_item("C", py.get_type::()).unwrap(); - py.run("assert C.method() == 'StaticMethod.method()!'", None, Some(d)).unwrap(); - py.run("assert C().method() == 'StaticMethod.method()!'", None, Some(d)).unwrap(); -} - -#[pyclass] -struct StaticMethodWithArgs{token: PyToken} - -#[pymethods] -impl StaticMethodWithArgs { - - #[staticmethod] - fn method(py: Python, input: i32) -> PyResult { - Ok(format!("0x{:x}", input)) - } -} - -#[test] -fn static_method_with_args() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - assert_eq!(StaticMethodWithArgs::method(py, 1234).unwrap(), "0x4d2"); - - let d = PyDict::new(py); - d.set_item("C", py.get_type::()).unwrap(); - py.run("assert C.method(1337) == '0x539'", None, Some(d)).unwrap(); -} - -#[pyclass] -struct GCIntegration { - self_ref: RefCell, - dropped: TestDropCall, - token: PyToken, -} - -#[pyproto] -impl PyGCProtocol for GCIntegration { - fn __traverse__(&self, visit: PyVisit) -> Result<(), PyTraverseError> { - visit.call(&*self.self_ref.borrow()) - } - - fn __clear__(&mut self) { - *self.self_ref.borrow_mut() = self.py().None(); - } -} - -#[test] -fn gc_integration() { - let drop_called = Arc::new(AtomicBool::new(false)); - - { - let gil = Python::acquire_gil(); - let py = gil.python(); - let inst = Py::new_ref(py, |t| GCIntegration{ - self_ref: RefCell::new(py.None()), - dropped: TestDropCall { drop_called: Arc::clone(&drop_called) }, - token: t}).unwrap(); - - *inst.self_ref.borrow_mut() = inst.into(); - } - - let gil = Python::acquire_gil(); - let py = gil.python(); - py.run("import gc; gc.collect()", None, None).unwrap(); - assert!(drop_called.load(Ordering::Relaxed)); -} - -#[pyclass(gc)] -struct GCIntegration2 { - token: PyToken, -} -#[test] -fn gc_integration2() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let inst = Py::new_ref(py, |t| GCIntegration2{token: t}).unwrap(); - py_run!(py, inst, "import gc; assert inst in gc.get_objects()"); -} - -#[pyclass(weakref)] -struct WeakRefSupport { - token: PyToken, -} -#[test] -fn weakref_support() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let inst = Py::new_ref(py, |t| WeakRefSupport{token: t}).unwrap(); - py_run!(py, inst, "import weakref; assert weakref.ref(inst)() is inst"); -} - -#[pyclass] -pub struct Len { - l: usize, - token: PyToken, -} - -#[pyproto] -impl PyMappingProtocol for Len { - fn __len__(&self) -> PyResult { - Ok(self.l) - } -} - -#[test] -fn len() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let inst = Py::new(py, |t| Len{l: 10, token: t}).unwrap(); - py_assert!(py, inst, "len(inst) == 10"); - unsafe { - assert_eq!(ffi::PyObject_Size(inst.as_ptr()), 10); - assert_eq!(ffi::PyMapping_Size(inst.as_ptr()), 10); - } - - let inst = Py::new(py, |t| Len{l: (isize::MAX as usize) + 1, token: t}).unwrap(); - py_expect_exception!(py, inst, "len(inst)", OverflowError); -} - -#[pyclass] -struct Iterator{ - iter: Box + Send>, - token: PyToken, -} - -#[pyproto] -impl PyIterProtocol for Iterator { - fn __iter__(&mut self) -> PyResult> { - Ok(self.into()) - } - - fn __next__(&mut self) -> PyResult> { - Ok(self.iter.next()) - } -} - -#[test] -fn iterator() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let inst = Py::new(py, |t| Iterator{iter: Box::new(5..8), token: t}).unwrap(); - py_assert!(py, inst, "iter(inst) is inst"); - py_assert!(py, inst, "list(inst) == [5, 6, 7]"); -} - -#[pyclass] -struct StringMethods {token: PyToken} - -#[pyproto] -impl<'p> PyObjectProtocol<'p> for StringMethods { - fn __str__(&self) -> PyResult<&'static str> { - Ok("str") - } - - fn __repr__(&self) -> PyResult<&'static str> { - Ok("repr") - } - - fn __format__(&self, format_spec: String) -> PyResult { - Ok(format!("format({})", format_spec)) - } - - fn __unicode__(&self) -> PyResult { - Ok(PyString::new(self.py(), "unicode").into()) - } - - fn __bytes__(&self) -> PyResult { - Ok(PyBytes::new(self.py(), b"bytes").into()) - } -} - -#[cfg(Py_3)] -#[test] -fn string_methods() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let obj = Py::new(py, |t| StringMethods{token: t}).unwrap(); - py_assert!(py, obj, "str(obj) == 'str'"); - py_assert!(py, obj, "repr(obj) == 'repr'"); - py_assert!(py, obj, "'{0:x}'.format(obj) == 'format(x)'"); - py_assert!(py, obj, "bytes(obj) == b'bytes'"); -} - -#[cfg(not(Py_3))] -#[test] -fn string_methods() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let obj = Py::new(py, |t| StringMethods{token: t}).unwrap(); - py_assert!(py, obj, "str(obj) == 'str'"); - py_assert!(py, obj, "repr(obj) == 'repr'"); - py_assert!(py, obj, "unicode(obj) == 'unicode'"); - py_assert!(py, obj, "'{0:x}'.format(obj) == 'format(x)'"); -} - - -#[pyclass] -struct Comparisons { - val: i32, - token: PyToken, -} - -#[pyproto] -impl PyObjectProtocol for Comparisons { - fn __hash__(&self) -> PyResult { - Ok(self.val as isize) - } - fn __bool__(&self) -> PyResult { - Ok(self.val != 0) - } -} - - -#[test] -fn comparisons() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let zero = Py::new(py, |t| Comparisons{val: 0, token: t}).unwrap(); - let one = Py::new(py, |t| Comparisons{val: 1, token: t}).unwrap(); - let ten = Py::new(py, |t| Comparisons{val: 10, token: t}).unwrap(); - let minus_one = Py::new(py, |t| Comparisons{val: -1, token: t}).unwrap(); - py_assert!(py, one, "hash(one) == 1"); - py_assert!(py, ten, "hash(ten) == 10"); - py_assert!(py, minus_one, "hash(minus_one) == -2"); - - py_assert!(py, one, "bool(one) is True"); - py_assert!(py, zero, "not zero"); -} - - -#[pyclass] -struct Sequence { - token: PyToken -} - -#[pyproto] -impl PySequenceProtocol for Sequence { - fn __len__(&self) -> PyResult { - Ok(5) - } - - fn __getitem__(&self, key: isize) -> PyResult { - if key == 5 { - return Err(PyErr::new::(NoArgs)); - } - Ok(key) - } -} - -#[test] -fn sequence() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let c = py.init(|t| Sequence{token: t}).unwrap(); - py_assert!(py, c, "list(c) == [0, 1, 2, 3, 4]"); - py_expect_exception!(py, c, "c['abc']", TypeError); -} - - -#[pyclass] -struct Callable {token: PyToken} - -#[pymethods] -impl Callable { - - #[__call__] - fn __call__(&self, arg: i32) -> PyResult { - Ok(arg * 6) - } -} - -#[test] -fn callable() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let c = py.init(|t| Callable{token: t}).unwrap(); - py_assert!(py, c, "callable(c)"); - py_assert!(py, c, "c(7) == 42"); - - let nc = py.init(|t| Comparisons{val: 0, token: t}).unwrap(); - py_assert!(py, nc, "not callable(nc)"); -} - -#[pyclass] -struct SetItem { - key: i32, - val: i32, - token: PyToken, -} - -#[pyproto] -impl PyMappingProtocol<'a> for SetItem { - fn __setitem__(&mut self, key: i32, val: i32) -> PyResult<()> { - self.key = key; - self.val = val; - Ok(()) - } -} - -#[test] -fn setitem() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let c = py.init_ref(|t| SetItem{key: 0, val: 0, token: t}).unwrap(); - py_run!(py, c, "c[1] = 2"); - assert_eq!(c.key, 1); - assert_eq!(c.val, 2); - py_expect_exception!(py, c, "del c[1]", NotImplementedError); -} - -#[pyclass] -struct DelItem { - key: i32, - token: PyToken, -} - -#[pyproto] -impl PyMappingProtocol<'a> for DelItem { - fn __delitem__(&mut self, key: i32) -> PyResult<()> { - self.key = key; - Ok(()) - } -} - -#[test] -fn delitem() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let c = py.init_ref(|t| DelItem{key:0, token:t}).unwrap(); - py_run!(py, c, "del c[1]"); - assert_eq!(c.key, 1); - py_expect_exception!(py, c, "c[1] = 2", NotImplementedError); -} - -#[pyclass] -struct SetDelItem { - val: Option, - token: PyToken, -} - -#[pyproto] -impl PyMappingProtocol for SetDelItem { - fn __setitem__(&mut self, key: i32, val: i32) -> PyResult<()> { - self.val = Some(val); - Ok(()) - } - - fn __delitem__(&mut self, key: i32) -> PyResult<()> { - self.val = None; - Ok(()) - } -} - -#[test] -fn setdelitem() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let c = py.init_ref(|t| SetDelItem{val: None, token: t}).unwrap(); - py_run!(py, c, "c[1] = 2"); - assert_eq!(c.val, Some(2)); - py_run!(py, c, "del c[1]"); - assert_eq!(c.val, None); -} - -#[pyclass] -struct Reversed {token: PyToken} - -#[pyproto] -impl PyMappingProtocol for Reversed{ - fn __reversed__(&self) -> PyResult<&'static str> { - Ok("I am reversed") - } -} - -#[test] -fn reversed() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let c = py.init(|t| Reversed{token: t}).unwrap(); - py_run!(py, c, "assert reversed(c) == 'I am reversed'"); -} - -#[pyclass] -struct Contains {token: PyToken} - -#[pyproto] -impl PySequenceProtocol for Contains { - fn __contains__(&self, item: i32) -> PyResult { - Ok(item >= 0) - } -} - -#[test] -fn contains() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let c = py.init(|t| Contains{token: t}).unwrap(); - py_run!(py, c, "assert 1 in c"); - py_run!(py, c, "assert -1 not in c"); - py_expect_exception!(py, c, "assert 'wrong type' not in c", TypeError); -} - - - -#[pyclass] -struct UnaryArithmetic {token: PyToken} - -#[pyproto] -impl PyNumberProtocol for UnaryArithmetic { - - fn __neg__(&self) -> PyResult<&'static str> { - Ok("neg") - } - - fn __pos__(&self) -> PyResult<&'static str> { - Ok("pos") - } - - fn __abs__(&self) -> PyResult<&'static str> { - Ok("abs") - } - - fn __invert__(&self) -> PyResult<&'static str> { - Ok("invert") - } -} - -#[test] -fn unary_arithmetic() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let c = py.init(|t| UnaryArithmetic{token: t}).unwrap(); - py_run!(py, c, "assert -c == 'neg'"); - py_run!(py, c, "assert +c == 'pos'"); - py_run!(py, c, "assert abs(c) == 'abs'"); - py_run!(py, c, "assert ~c == 'invert'"); -} - - -#[pyclass] -struct BinaryArithmetic { - token: PyToken -} - -#[pyproto] -impl PyObjectProtocol for BinaryArithmetic { - fn __repr__(&self) -> PyResult<&'static str> { - Ok("BA") - } -} - -#[pyproto] -impl PyNumberProtocol for BinaryArithmetic { - fn __add__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { - Ok(format!("{:?} + {:?}", lhs, rhs)) - } - - fn __sub__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { - Ok(format!("{:?} - {:?}", lhs, rhs)) - } - - fn __mul__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { - Ok(format!("{:?} * {:?}", lhs, rhs)) - } - - fn __lshift__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { - Ok(format!("{:?} << {:?}", lhs, rhs)) - } - - fn __rshift__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { - Ok(format!("{:?} >> {:?}", lhs, rhs)) - } - - fn __and__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { - Ok(format!("{:?} & {:?}", lhs, rhs)) - } - - fn __xor__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { - Ok(format!("{:?} ^ {:?}", lhs, rhs)) - } - - fn __or__(lhs: &PyObjectRef, rhs: &PyObjectRef) -> PyResult { - Ok(format!("{:?} | {:?}", lhs, rhs)) - } -} - -#[test] -fn binary_arithmetic() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let c = py.init(|t| BinaryArithmetic{token: t}).unwrap(); - py_run!(py, c, "assert c + c == 'BA + BA'"); - py_run!(py, c, "assert c + 1 == 'BA + 1'"); - py_run!(py, c, "assert 1 + c == '1 + BA'"); - py_run!(py, c, "assert c - 1 == 'BA - 1'"); - py_run!(py, c, "assert 1 - c == '1 - BA'"); - py_run!(py, c, "assert c * 1 == 'BA * 1'"); - py_run!(py, c, "assert 1 * c == '1 * BA'"); - - py_run!(py, c, "assert c << 1 == 'BA << 1'"); - py_run!(py, c, "assert 1 << c == '1 << BA'"); - py_run!(py, c, "assert c >> 1 == 'BA >> 1'"); - py_run!(py, c, "assert 1 >> c == '1 >> BA'"); - py_run!(py, c, "assert c & 1 == 'BA & 1'"); - py_run!(py, c, "assert 1 & c == '1 & BA'"); - py_run!(py, c, "assert c ^ 1 == 'BA ^ 1'"); - py_run!(py, c, "assert 1 ^ c == '1 ^ BA'"); - py_run!(py, c, "assert c | 1 == 'BA | 1'"); - py_run!(py, c, "assert 1 | c == '1 | BA'"); -} - - -#[pyclass] -struct RichComparisons { - token: PyToken -} - -#[pyproto] -impl PyObjectProtocol for RichComparisons { - fn __repr__(&self) -> PyResult<&'static str> { - Ok("RC") - } - - fn __richcmp__(&self, other: &PyObjectRef, op: CompareOp) -> PyResult { - match op { - CompareOp::Lt => Ok(format!("{} < {:?}", self.__repr__().unwrap(), other)), - CompareOp::Le => Ok(format!("{} <= {:?}", self.__repr__().unwrap(), other)), - CompareOp::Eq => Ok(format!("{} == {:?}", self.__repr__().unwrap(), other)), - CompareOp::Ne => Ok(format!("{} != {:?}", self.__repr__().unwrap(), other)), - CompareOp::Gt => Ok(format!("{} > {:?}", self.__repr__().unwrap(), other)), - CompareOp::Ge => Ok(format!("{} >= {:?}", self.__repr__().unwrap(), other)) - } - } -} - -#[pyclass] -struct RichComparisons2 { - py: PyToken -} - -#[pyproto] -impl PyObjectProtocol for RichComparisons2 { - fn __repr__(&self) -> PyResult<&'static str> { - Ok("RC2") - } - - fn __richcmp__(&self, other: &'p PyObjectRef, op: CompareOp) -> PyResult { - match op { - CompareOp::Eq => Ok(true.to_object(self.py())), - CompareOp::Ne => Ok(false.to_object(self.py())), - _ => Ok(self.py().NotImplemented()) - } - } -} - -#[test] -fn rich_comparisons() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let c = py.init(|t| RichComparisons{token: t}).unwrap(); - py_run!(py, c, "assert (c < c) == 'RC < RC'"); - py_run!(py, c, "assert (c < 1) == 'RC < 1'"); - py_run!(py, c, "assert (1 < c) == 'RC > 1'"); - py_run!(py, c, "assert (c <= c) == 'RC <= RC'"); - py_run!(py, c, "assert (c <= 1) == 'RC <= 1'"); - py_run!(py, c, "assert (1 <= c) == 'RC >= 1'"); - py_run!(py, c, "assert (c == c) == 'RC == RC'"); - py_run!(py, c, "assert (c == 1) == 'RC == 1'"); - py_run!(py, c, "assert (1 == c) == 'RC == 1'"); - py_run!(py, c, "assert (c != c) == 'RC != RC'"); - py_run!(py, c, "assert (c != 1) == 'RC != 1'"); - py_run!(py, c, "assert (1 != c) == 'RC != 1'"); - py_run!(py, c, "assert (c > c) == 'RC > RC'"); - py_run!(py, c, "assert (c > 1) == 'RC > 1'"); - py_run!(py, c, "assert (1 > c) == 'RC < 1'"); - py_run!(py, c, "assert (c >= c) == 'RC >= RC'"); - py_run!(py, c, "assert (c >= 1) == 'RC >= 1'"); - py_run!(py, c, "assert (1 >= c) == 'RC <= 1'"); -} - -#[test] -#[cfg(Py_3)] -fn rich_comparisons_python_3_type_error() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let c2 = py.init(|t| RichComparisons2{py: t}).unwrap(); - py_expect_exception!(py, c2, "c2 < c2", TypeError); - py_expect_exception!(py, c2, "c2 < 1", TypeError); - py_expect_exception!(py, c2, "1 < c2", TypeError); - py_expect_exception!(py, c2, "c2 <= c2", TypeError); - py_expect_exception!(py, c2, "c2 <= 1", TypeError); - py_expect_exception!(py, c2, "1 <= c2", TypeError); - py_run!(py, c2, "assert (c2 == c2) == True"); - py_run!(py, c2, "assert (c2 == 1) == True"); - py_run!(py, c2, "assert (1 == c2) == True"); - py_run!(py, c2, "assert (c2 != c2) == False"); - py_run!(py, c2, "assert (c2 != 1) == False"); - py_run!(py, c2, "assert (1 != c2) == False"); - py_expect_exception!(py, c2, "c2 > c2", TypeError); - py_expect_exception!(py, c2, "c2 > 1", TypeError); - py_expect_exception!(py, c2, "1 > c2", TypeError); - py_expect_exception!(py, c2, "c2 >= c2", TypeError); - py_expect_exception!(py, c2, "c2 >= 1", TypeError); - py_expect_exception!(py, c2, "1 >= c2", TypeError); -} - -#[pyclass] -struct InPlaceOperations { - value: u32, - token: PyToken, -} - -#[pyproto] -impl PyObjectProtocol for InPlaceOperations { - fn __repr__(&self) -> PyResult { - Ok(format!("IPO({:?})", self.value)) - } -} - -#[pyproto] -impl PyNumberProtocol for InPlaceOperations { - fn __iadd__(&mut self, other: u32) -> PyResult<()> { - self.value += other; - Ok(()) - } - - fn __isub__(&mut self, other: u32) -> PyResult<()> { - self.value -= other; - Ok(()) - } - - fn __imul__(&mut self, other: u32) -> PyResult<()> { - self.value *= other; - Ok(()) - } - - fn __ilshift__(&mut self, other: u32) -> PyResult<()> { - self.value <<= other; - Ok(()) - } - - fn __irshift__(&mut self, other: u32) -> PyResult<()> { - self.value >>= other; - Ok(()) - } - - fn __iand__(&mut self, other: u32) -> PyResult<()> { - self.value &= other; - Ok(()) - } - - fn __ixor__(&mut self, other: u32) -> PyResult<()> { - self.value ^= other; - Ok(()) - } - - fn __ior__(&mut self, other: u32) -> PyResult<()> { - self.value |= other; - Ok(()) - } -} - -#[test] -fn inplace_operations() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let c = py.init(|t| InPlaceOperations{value: 0, token: t}).unwrap(); - py_run!(py, c, "d = c; c += 1; assert repr(c) == repr(d) == 'IPO(1)'"); - - let c = py.init(|t| InPlaceOperations{value:10, token: t}).unwrap(); - py_run!(py, c, "d = c; c -= 1; assert repr(c) == repr(d) == 'IPO(9)'"); - - let c = py.init(|t| InPlaceOperations{value: 3, token: t}).unwrap(); - py_run!(py, c, "d = c; c *= 3; assert repr(c) == repr(d) == 'IPO(9)'"); - - let c = py.init(|t| InPlaceOperations{value: 3, token: t}).unwrap(); - py_run!(py, c, "d = c; c <<= 2; assert repr(c) == repr(d) == 'IPO(12)'"); - - let c = py.init(|t| InPlaceOperations{value: 12, token: t}).unwrap(); - py_run!(py, c, "d = c; c >>= 2; assert repr(c) == repr(d) == 'IPO(3)'"); - - let c = py.init(|t| InPlaceOperations{value: 12, token: t}).unwrap(); - py_run!(py, c, "d = c; c &= 10; assert repr(c) == repr(d) == 'IPO(8)'"); - - let c = py.init(|t| InPlaceOperations{value: 12, token: t}).unwrap(); - py_run!(py, c, "d = c; c |= 3; assert repr(c) == repr(d) == 'IPO(15)'"); - - let c = py.init(|t| InPlaceOperations{value: 12, token: t}).unwrap(); - py_run!(py, c, "d = c; c ^= 5; assert repr(c) == repr(d) == 'IPO(9)'"); -} - -#[pyclass] -struct ContextManager { - exit_called: bool, - token: PyToken, -} - -#[pyproto] -impl<'p> PyContextProtocol<'p> for ContextManager { - - fn __enter__(&mut self) -> PyResult { - Ok(42) - } - - fn __exit__(&mut self, - ty: Option<&'p PyType>, - value: Option<&'p PyObjectRef>, - traceback: Option<&'p PyObjectRef>) -> PyResult { - self.exit_called = true; - if ty == Some(self.py().get_type::()) { - Ok(true) - } else { - Ok(false) - } - } -} - -#[test] -fn context_manager() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let c = py.init_mut(|t| ContextManager{exit_called: false, token: t}).unwrap(); - py_run!(py, c, "with c as x:\n assert x == 42"); - assert!(c.exit_called); - - c.exit_called = false; - py_run!(py, c, "with c as x:\n raise ValueError"); - assert!(c.exit_called); - - c.exit_called = false; - py_expect_exception!( - py, c, "with c as x:\n raise NotImplementedError", NotImplementedError); - assert!(c.exit_called); -} - -#[pyclass] -struct ClassWithProperties { - num: i32, - token: PyToken, -} - -#[pymethods] -impl ClassWithProperties { - - fn get_num(&self) -> PyResult { - Ok(self.num) - } - - #[getter(DATA)] - fn get_data(&self) -> PyResult { - Ok(self.num) - } - #[setter(DATA)] - fn set_data(&mut self, value: i32) -> PyResult<()> { - self.num = value; - Ok(()) - } -} - -#[test] -fn class_with_properties() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let inst = py.init(|t| ClassWithProperties{num: 10, token: t}).unwrap(); - - py_run!(py, inst, "assert inst.get_num() == 10"); - py_run!(py, inst, "assert inst.get_num() == inst.DATA"); - py_run!(py, inst, "inst.DATA = 20"); - py_run!(py, inst, "assert inst.get_num() == 20"); - py_run!(py, inst, "assert inst.get_num() == inst.DATA"); -} - -#[pyclass] -struct MethArgs { - token: PyToken -} - -#[pymethods] -impl MethArgs { - - #[args(test="10")] - fn get_default(&self, test: i32) -> PyResult { - Ok(test) - } - #[args("*", test=10)] - fn get_kwarg(&self, test: i32) -> PyResult { - Ok(test) - } - #[args(args="*", kwargs="**")] - fn get_kwargs(&self, args: &PyTuple, kwargs: Option<&PyDict>) -> PyResult { - Ok([args.into(), kwargs.to_object(self.py())].to_object(self.py())) - } -} - -#[test] -fn meth_args() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let inst = py.init(|t| MethArgs{token: t}).unwrap(); - - py_run!(py, inst, "assert inst.get_default() == 10"); - py_run!(py, inst, "assert inst.get_default(100) == 100"); - py_run!(py, inst, "assert inst.get_kwarg() == 10"); - py_run!(py, inst, "assert inst.get_kwarg(100) == 10"); - py_run!(py, inst, "assert inst.get_kwarg(test=100) == 100"); - py_run!(py, inst, "assert inst.get_kwargs() == [(), None]"); - py_run!(py, inst, "assert inst.get_kwargs(1,2,3) == [(1,2,3), None]"); - py_run!(py, inst, "assert inst.get_kwargs(t=1,n=2) == [(), {'t': 1, 'n': 2}]"); - py_run!(py, inst, "assert inst.get_kwargs(1,2,3,t=1,n=2) == [(1,2,3), {'t': 1, 'n': 2}]"); - // py_expect_exception!(py, inst, "inst.get_kwarg(100)", TypeError); -} - -#[pyclass(subclass)] -struct SubclassAble {} - -#[test] -fn subclass() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let d = PyDict::new(py); - d.set_item("SubclassAble", py.get_type::()).unwrap(); - py.run("class A(SubclassAble): pass\nassert issubclass(A, SubclassAble)", None, Some(d)) - .map_err(|e| e.print(py)) - .unwrap(); -} - -#[pyclass(dict)] -struct DunderDictSupport { - token: PyToken, -} - -#[test] -fn dunder_dict_support() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let inst = Py::new_ref(py, |t| DunderDictSupport{token: t}).unwrap(); - py_run!(py, inst, "inst.a = 1; assert inst.a == 1"); -} - -#[pyclass(weakref, dict)] -struct WeakRefDunderDictSupport { - token: PyToken, -} - -#[test] -fn weakref_dunder_dict_support() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let inst = Py::new_ref(py, |t| WeakRefDunderDictSupport{token: t}).unwrap(); - py_run!(py, inst, "import weakref; assert weakref.ref(inst)() is inst; inst.a = 1; assert inst.a == 1"); -} - -#[pyclass] -struct GetterSetter { - #[prop(get, set)] - num: i32, - token: PyToken -} - -#[pymethods] -impl GetterSetter { - - fn get_num2(&self) -> PyResult { - Ok(self.num) - } -} - -#[test] -fn getter_setter_autogen() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let inst = py.init(|t| GetterSetter{num: 10, token: t}).unwrap(); - - py_run!(py, inst, "assert inst.num == 10"); - py_run!(py, inst, "inst.num = 20; assert inst.num == 20"); -} - -#[pyclass] -struct BaseClass { - #[prop(get)] - val1: usize, -} - -#[pymethods] -impl BaseClass { - #[new] - fn __new__(obj: &PyRawObject) -> PyResult<()> { - obj.init(|t| BaseClass{val1: 10}) - } -} - -#[pyclass(base=BaseClass)] -struct SubClass { - #[prop(get)] - val2: usize, -} - -#[pymethods] -impl SubClass { - #[new] - fn __new__(obj: &PyRawObject) -> PyResult<()> { - obj.init(|t| SubClass{val2: 5})?; - BaseClass::__new__(obj) - } -} - -#[test] -fn inheritance_with_new_methods() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let typebase = py.get_type::(); - let typeobj = py.get_type::(); - let inst = typeobj.call(NoArgs, NoArgs).unwrap(); - py_run!(py, inst, "assert inst.val1 == 10; assert inst.val2 == 5"); -} - - -#[pyclass] -struct BaseClassWithDrop { - token: PyToken, - data: Option>, -} - -#[pymethods] -impl BaseClassWithDrop { - #[new] - fn __new__(obj: &PyRawObject) -> PyResult<()> { - obj.init(|t| BaseClassWithDrop{token: t, data: None}) - } -} - -impl Drop for BaseClassWithDrop { - fn drop(&mut self) { - if let Some(ref mut data) = self.data { - data.store(true, Ordering::Relaxed); - } - } -} - -#[pyclass(base=BaseClassWithDrop)] -struct SubClassWithDrop { - token: PyToken, - data: Option>, -} - -#[pymethods] -impl SubClassWithDrop { - #[new] - fn __new__(obj: &PyRawObject) -> PyResult<()> { - obj.init(|t| SubClassWithDrop{token: t, data: None})?; - BaseClassWithDrop::__new__(obj) - } -} - -impl Drop for SubClassWithDrop { - fn drop(&mut self) { - if let Some(ref mut data) = self.data { - data.store(true, Ordering::Relaxed); - } - } -} - -#[test] -fn inheritance_with_new_methods_with_drop() { - let drop_called1 = Arc::new(AtomicBool::new(false)); - let drop_called2 = Arc::new(AtomicBool::new(false)); - - { - let gil = Python::acquire_gil(); - let py = gil.python(); - let typebase = py.get_type::(); - let typeobj = py.get_type::(); - let inst = typeobj.call(NoArgs, NoArgs).unwrap(); - - let obj = SubClassWithDrop::try_from_mut(inst).unwrap(); - obj.data = Some(Arc::clone(&drop_called1)); - - let base = obj.get_mut_base(); - base.data = Some(Arc::clone(&drop_called2)); - } - - assert!(drop_called1.load(Ordering::Relaxed)); - assert!(drop_called2.load(Ordering::Relaxed)); -} - - -#[pyclass] -struct MutRefArg { - n: i32, - token: PyToken, -} - -#[pymethods] -impl MutRefArg { - - fn get(&self) -> PyResult { - Ok(self.n) - } - fn set_other(&self, other: &mut MutRefArg) -> PyResult<()> { - other.n = 100; - Ok(()) - } -} - -#[test] -fn mut_ref_arg() { - let gil = Python::acquire_gil(); - let py = gil.python(); - let inst1 = py.init(|t| MutRefArg{token: t, n: 0}).unwrap(); - let inst2 = py.init(|t| MutRefArg{token: t, n: 0}).unwrap(); - - let d = PyDict::new(py); - d.set_item("inst1", &inst1).unwrap(); - d.set_item("inst2", &inst2).unwrap(); - - py.run("inst1.set_other(inst2)", None, Some(d)).unwrap(); - assert_eq!(inst2.as_ref(py).n, 100); -} diff --git a/tests/test_class_basics.rs b/tests/test_class_basics.rs new file mode 100644 index 00000000..24d77310 --- /dev/null +++ b/tests/test_class_basics.rs @@ -0,0 +1,67 @@ +#![feature(proc_macro, specialization)] + +extern crate pyo3; + +use pyo3::prelude::*; + +use pyo3::py::class as pyclass; + +#[macro_use] +mod common; + +#[pyclass] +struct EmptyClass { } + +#[test] +fn empty_class() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let typeobj = py.get_type::(); + // By default, don't allow creating instances from python. + assert!(typeobj.call(NoArgs, NoArgs).is_err()); + + py_assert!(py, typeobj, "typeobj.__name__ == 'EmptyClass'"); +} + +/// Line1 +///Line2 +/// Line3 +// this is not doc string +#[pyclass] +struct ClassWithDocs { } + +#[test] +fn class_with_docstr() { + { + let gil = Python::acquire_gil(); + let py = gil.python(); + let typeobj = py.get_type::(); + py_run!(py, typeobj, "assert typeobj.__doc__ == 'Line1\\nLine2\\n Line3'"); + } +} + +#[pyclass(name=CustomName)] +struct EmptyClass2 { } + +#[test] +fn custom_class_name() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let typeobj = py.get_type::(); + py_assert!(py, typeobj, "typeobj.__name__ == 'CustomName'"); +} + +#[pyclass] +struct EmptyClassInModule { } + +#[test] +fn empty_class_in_module() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let module = PyModule::new(py, "test_module.nested").unwrap(); + module.add_class::().unwrap(); + + let ty = module.getattr("EmptyClassInModule").unwrap(); + assert_eq!(ty.getattr("__name__").unwrap().extract::().unwrap(), "EmptyClassInModule"); + assert_eq!(ty.getattr("__module__").unwrap().extract::().unwrap(), "test_module.nested"); +} diff --git a/tests/test_class_new.rs b/tests/test_class_new.rs new file mode 100644 index 00000000..8fa81f7f --- /dev/null +++ b/tests/test_class_new.rs @@ -0,0 +1,81 @@ +#![feature(proc_macro, specialization)] + +extern crate pyo3; + +use pyo3::prelude::*; + +use pyo3::py::class as pyclass; +use pyo3::py::methods as pymethods; + +#[pyclass] +struct EmptyClassWithNew { + token: PyToken +} + +#[pymethods] +impl EmptyClassWithNew { + #[__new__] + fn __new__(obj: &PyRawObject) -> PyResult<()> { + obj.init(|t| EmptyClassWithNew{token: t}) + } +} + +#[test] +fn empty_class_with_new() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let typeobj = py.get_type::(); + assert!(typeobj.call(NoArgs, NoArgs).unwrap().cast_as::().is_ok()); +} + +#[pyclass] +struct NewWithOneArg { + _data: i32, + token: PyToken +} + +#[pymethods] +impl NewWithOneArg { + #[new] + fn __new__(obj: &PyRawObject, arg: i32) -> PyResult<()> { + obj.init(|t| NewWithOneArg{_data: arg, token: t}) + } +} + +#[test] +fn new_with_one_arg() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let typeobj = py.get_type::(); + let wrp = typeobj.call((42,), NoArgs).unwrap(); + let obj = wrp.cast_as::().unwrap(); + assert_eq!(obj._data, 42); +} + +#[pyclass] +struct NewWithTwoArgs { + _data1: i32, + _data2: i32, + + token: PyToken +} + +#[pymethods] +impl NewWithTwoArgs { + #[new] + fn __new__(obj: &PyRawObject, arg1: i32, arg2: i32) -> PyResult<()> + { + obj.init(|t| NewWithTwoArgs{_data1: arg1, _data2: arg2, token: t}) + } +} + +#[test] +fn new_with_two_args() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let typeobj = py.get_type::(); + let wrp = typeobj.call((10, 20), NoArgs).map_err(|e| e.print(py)).unwrap(); + let obj = wrp.cast_as::().unwrap(); + assert_eq!(obj._data1, 10); + assert_eq!(obj._data2, 20); +} \ No newline at end of file diff --git a/tests/test_gc.rs b/tests/test_gc.rs new file mode 100644 index 00000000..e0ca2553 --- /dev/null +++ b/tests/test_gc.rs @@ -0,0 +1,262 @@ +#![feature(proc_macro, specialization)] + +extern crate pyo3; + +use pyo3::prelude::*; +use std::sync::Arc; +use std::cell::RefCell; +use std::sync::atomic::{AtomicBool, Ordering}; +use pyo3::ffi; + +use pyo3::py::class as pyclass; +use pyo3::py::methods as pymethods; +use pyo3::py::proto as pyproto; + +#[macro_use] +mod common; + + +#[pyclass(freelist=2)] +struct ClassWithFreelist{token: PyToken} + +#[test] +fn class_with_freelist() { + let ptr; + { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let inst = Py::new(py, |t| ClassWithFreelist{token: t}).unwrap(); + let _inst2 = Py::new(py, |t| ClassWithFreelist{token: t}).unwrap(); + ptr = inst.as_ptr(); + drop(inst); + } + + { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let inst3 = Py::new(py, |t| ClassWithFreelist{token: t}).unwrap(); + assert_eq!(ptr, inst3.as_ptr()); + + let inst4 = Py::new(py, |t| ClassWithFreelist{token: t}).unwrap(); + assert_ne!(ptr, inst4.as_ptr()) + } +} + +struct TestDropCall { + drop_called: Arc +} +impl Drop for TestDropCall { + fn drop(&mut self) { + self.drop_called.store(true, Ordering::Relaxed); + } +} + +#[allow(dead_code)] +#[pyclass] +struct DataIsDropped { + member1: TestDropCall, + member2: TestDropCall, + token: PyToken, +} + +#[test] +fn data_is_dropped() { + let drop_called1 = Arc::new(AtomicBool::new(false)); + let drop_called2 = Arc::new(AtomicBool::new(false)); + + { + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst = py.init(|t| DataIsDropped{ + member1: TestDropCall { drop_called: Arc::clone(&drop_called1) }, + member2: TestDropCall { drop_called: Arc::clone(&drop_called2) }, + token: t + }).unwrap(); + assert!(!drop_called1.load(Ordering::Relaxed)); + assert!(!drop_called2.load(Ordering::Relaxed)); + drop(inst); + } + + assert!(drop_called1.load(Ordering::Relaxed)); + assert!(drop_called2.load(Ordering::Relaxed)); +} + +#[pyclass] +struct ClassWithDrop { + token: PyToken, +} +impl Drop for ClassWithDrop { + fn drop(&mut self) { + unsafe { + let py = Python::assume_gil_acquired(); + + let _empty1 = PyTuple::empty(py); + let _empty2: PyObject = PyTuple::empty(py).into(); + let _empty3: &PyObjectRef = py.from_owned_ptr(ffi::PyTuple_New(0)); + } + } +} + +// Test behavior of pythonrun::register_pointers + typeob::dealloc +#[test] +fn create_pointers_in_drop() { + let _gil = Python::acquire_gil(); + + let ptr; + let cnt; + { + let gil = Python::acquire_gil(); + let py = gil.python(); + let empty = PyTuple::empty(py); + ptr = empty.as_ptr(); + cnt = empty.get_refcnt() - 1; + let inst = py.init(|t| ClassWithDrop{token: t}).unwrap(); + drop(inst); + } + + // empty1 and empty2 are still alive (stored in pointers list) + { + let _gil = Python::acquire_gil(); + assert_eq!(cnt + 2, unsafe {ffi::Py_REFCNT(ptr)}); + } + + // empty1 and empty2 should be released + { + let _gil = Python::acquire_gil(); + assert_eq!(cnt, unsafe {ffi::Py_REFCNT(ptr)}); + } +} + +#[allow(dead_code)] +#[pyclass] +struct GCIntegration { + self_ref: RefCell, + dropped: TestDropCall, + token: PyToken, +} + +#[pyproto] +impl PyGCProtocol for GCIntegration { + fn __traverse__(&self, visit: PyVisit) -> Result<(), PyTraverseError> { + visit.call(&*self.self_ref.borrow()) + } + + fn __clear__(&mut self) { + *self.self_ref.borrow_mut() = self.py().None(); + } +} + +#[test] +fn gc_integration() { + let drop_called = Arc::new(AtomicBool::new(false)); + + { + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst = Py::new_ref(py, |t| GCIntegration{ + self_ref: RefCell::new(py.None()), + dropped: TestDropCall { drop_called: Arc::clone(&drop_called) }, + token: t}).unwrap(); + + *inst.self_ref.borrow_mut() = inst.into(); + } + + let gil = Python::acquire_gil(); + let py = gil.python(); + py.run("import gc; gc.collect()", None, None).unwrap(); + assert!(drop_called.load(Ordering::Relaxed)); +} + +#[pyclass(gc)] +struct GCIntegration2 { + token: PyToken, +} +#[test] +fn gc_integration2() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst = Py::new_ref(py, |t| GCIntegration2{token: t}).unwrap(); + py_run!(py, inst, "import gc; assert inst in gc.get_objects()"); +} + +#[pyclass(weakref)] +struct WeakRefSupport { + token: PyToken, +} +#[test] +fn weakref_support() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst = Py::new_ref(py, |t| WeakRefSupport{token: t}).unwrap(); + py_run!(py, inst, "import weakref; assert weakref.ref(inst)() is inst"); +} + +#[pyclass] +struct BaseClassWithDrop { + token: PyToken, + data: Option>, +} + +#[pymethods] +impl BaseClassWithDrop { + #[new] + fn __new__(obj: &PyRawObject) -> PyResult<()> { + obj.init(|t| BaseClassWithDrop{token: t, data: None}) + } +} + +impl Drop for BaseClassWithDrop { + fn drop(&mut self) { + if let Some(ref mut data) = self.data { + data.store(true, Ordering::Relaxed); + } + } +} + +#[pyclass(base=BaseClassWithDrop)] +struct SubClassWithDrop { + token: PyToken, + data: Option>, +} + +#[pymethods] +impl SubClassWithDrop { + #[new] + fn __new__(obj: &PyRawObject) -> PyResult<()> { + obj.init(|t| SubClassWithDrop{token: t, data: None})?; + BaseClassWithDrop::__new__(obj) + } +} + +impl Drop for SubClassWithDrop { + fn drop(&mut self) { + if let Some(ref mut data) = self.data { + data.store(true, Ordering::Relaxed); + } + } +} + +#[test] +fn inheritance_with_new_methods_with_drop() { + let drop_called1 = Arc::new(AtomicBool::new(false)); + let drop_called2 = Arc::new(AtomicBool::new(false)); + + { + let gil = Python::acquire_gil(); + let py = gil.python(); + let _typebase = py.get_type::(); + let typeobj = py.get_type::(); + let inst = typeobj.call(NoArgs, NoArgs).unwrap(); + + let obj = SubClassWithDrop::try_from_mut(inst).unwrap(); + obj.data = Some(Arc::clone(&drop_called1)); + + let base = obj.get_mut_base(); + base.data = Some(Arc::clone(&drop_called2)); + } + + assert!(drop_called1.load(Ordering::Relaxed)); + assert!(drop_called2.load(Ordering::Relaxed)); +} diff --git a/tests/test_getter_setter.rs b/tests/test_getter_setter.rs new file mode 100644 index 00000000..b7dfa874 --- /dev/null +++ b/tests/test_getter_setter.rs @@ -0,0 +1,77 @@ +#![feature(proc_macro, specialization)] + +extern crate pyo3; + +use pyo3::prelude::*; +use std::isize; + +use pyo3::py::class as pyclass; +use pyo3::py::methods as pymethods; + +#[macro_use] +mod common; + + +#[pyclass] +struct ClassWithProperties { + num: i32, + token: PyToken, +} + +#[pymethods] +impl ClassWithProperties { + + fn get_num(&self) -> PyResult { + Ok(self.num) + } + + #[getter(DATA)] + fn get_data(&self) -> PyResult { + Ok(self.num) + } + #[setter(DATA)] + fn set_data(&mut self, value: i32) -> PyResult<()> { + self.num = value; + Ok(()) + } +} + +#[test] +fn class_with_properties() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let inst = py.init(|t| ClassWithProperties{num: 10, token: t}).unwrap(); + + py_run!(py, inst, "assert inst.get_num() == 10"); + py_run!(py, inst, "assert inst.get_num() == inst.DATA"); + py_run!(py, inst, "inst.DATA = 20"); + py_run!(py, inst, "assert inst.get_num() == 20"); + py_run!(py, inst, "assert inst.get_num() == inst.DATA"); +} + +#[pyclass] +struct GetterSetter { + #[prop(get, set)] + num: i32, + token: PyToken +} + +#[pymethods] +impl GetterSetter { + + fn get_num2(&self) -> PyResult { + Ok(self.num) + } +} + +#[test] +fn getter_setter_autogen() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let inst = py.init(|t| GetterSetter{num: 10, token: t}).unwrap(); + + py_run!(py, inst, "assert inst.num == 10"); + py_run!(py, inst, "inst.num = 20; assert inst.num == 20"); +} diff --git a/tests/test_inheritance.rs b/tests/test_inheritance.rs new file mode 100644 index 00000000..9b27c77c --- /dev/null +++ b/tests/test_inheritance.rs @@ -0,0 +1,68 @@ +#![feature(proc_macro, specialization)] + +extern crate pyo3; + +use pyo3::prelude::*; +use std::isize; + +use pyo3::py::class as pyclass; +use pyo3::py::methods as pymethods; + +#[macro_use] +mod common; + + +#[pyclass] +struct BaseClass { + #[prop(get)] + val1: usize, +} + + +#[pyclass(subclass)] +struct SubclassAble {} + +#[test] +fn subclass() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let d = PyDict::new(py); + d.set_item("SubclassAble", py.get_type::()).unwrap(); + py.run("class A(SubclassAble): pass\nassert issubclass(A, SubclassAble)", None, Some(d)) + .map_err(|e| e.print(py)) + .unwrap(); +} + +#[pymethods] +impl BaseClass { + #[new] + fn __new__(obj: &PyRawObject) -> PyResult<()> { + obj.init(|_| BaseClass{val1: 10}) + } +} + +#[pyclass(base=BaseClass)] +struct SubClass { + #[prop(get)] + val2: usize, +} + +#[pymethods] +impl SubClass { + #[new] + fn __new__(obj: &PyRawObject) -> PyResult<()> { + obj.init(|_| SubClass{val2: 5})?; + BaseClass::__new__(obj) + } +} + +#[test] +fn inheritance_with_new_methods() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let _typebase = py.get_type::(); + let typeobj = py.get_type::(); + let inst = typeobj.call(NoArgs, NoArgs).unwrap(); + py_run!(py, inst, "assert inst.val1 == 10; assert inst.val2 == 5"); +} \ No newline at end of file diff --git a/tests/test_methods.rs b/tests/test_methods.rs new file mode 100644 index 00000000..e65ba47c --- /dev/null +++ b/tests/test_methods.rs @@ -0,0 +1,208 @@ +#![feature(proc_macro, specialization)] + +extern crate pyo3; + +use pyo3::prelude::*; + +use pyo3::py::class as pyclass; +use pyo3::py::methods as pymethods; + +#[macro_use] +mod common; + +#[pyclass] +struct InstanceMethod { + member: i32, + token: PyToken +} + +#[pymethods] +impl InstanceMethod { + /// Test method + fn method(&self) -> PyResult { + Ok(self.member) + } +} + +#[test] +fn instance_method() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let obj = py.init_ref(|t| InstanceMethod{member: 42, token: t}).unwrap(); + assert!(obj.method().unwrap() == 42); + let d = PyDict::new(py); + d.set_item("obj", obj).unwrap(); + py.run("assert obj.method() == 42", None, Some(d)).unwrap(); + py.run("assert obj.method.__doc__ == 'Test method'", None, Some(d)).unwrap(); +} + +#[pyclass] +struct InstanceMethodWithArgs { + member: i32, + token: PyToken +} + +#[pymethods] +impl InstanceMethodWithArgs { + fn method(&self, multiplier: i32) -> PyResult { + Ok(self.member * multiplier) + } +} + +//#[test] +fn instance_method_with_args() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let obj = py.init_ref(|t| InstanceMethodWithArgs{member: 7, token: t}).unwrap(); + assert!(obj.method(6).unwrap() == 42); + let d = PyDict::new(py); + d.set_item("obj", obj).unwrap(); + py.run("assert obj.method(3) == 21", None, Some(d)).unwrap(); + py.run("assert obj.method(multiplier=6) == 42", None, Some(d)).unwrap(); +} + + +#[pyclass] +struct ClassMethod {token: PyToken} + +#[pymethods] +impl ClassMethod { + #[new] + fn __new__(obj: &PyRawObject) -> PyResult<()> { + obj.init(|t| ClassMethod{token: t}) + } + + #[classmethod] + fn method(cls: &PyType) -> PyResult { + Ok(format!("{}.method()!", cls.name())) + } +} + +#[test] +fn class_method() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let d = PyDict::new(py); + d.set_item("C", py.get_type::()).unwrap(); + py.run("assert C.method() == 'ClassMethod.method()!'", None, Some(d)).unwrap(); + py.run("assert C().method() == 'ClassMethod.method()!'", None, Some(d)).unwrap(); +} + + +#[pyclass] +struct ClassMethodWithArgs{token: PyToken} + +#[pymethods] +impl ClassMethodWithArgs { + #[classmethod] + fn method(cls: &PyType, input: &PyString) -> PyResult { + Ok(format!("{}.method({})", cls.name(), input)) + } +} + +#[test] +fn class_method_with_args() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let d = PyDict::new(py); + d.set_item("C", py.get_type::()).unwrap(); + py.run("assert C.method('abc') == 'ClassMethodWithArgs.method(abc)'", None, Some(d)).unwrap(); +} + +#[pyclass] +struct StaticMethod { + token: PyToken +} + +#[pymethods] +impl StaticMethod { + #[new] + fn __new__(obj: &PyRawObject) -> PyResult<()> { + obj.init(|t| StaticMethod{token: t}) + } + + #[staticmethod] + fn method(_py: Python) -> PyResult<&'static str> { + Ok("StaticMethod.method()!") + } +} + +#[test] +fn static_method() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + assert_eq!(StaticMethod::method(py).unwrap(), "StaticMethod.method()!"); + let d = PyDict::new(py); + d.set_item("C", py.get_type::()).unwrap(); + py.run("assert C.method() == 'StaticMethod.method()!'", None, Some(d)).unwrap(); + py.run("assert C().method() == 'StaticMethod.method()!'", None, Some(d)).unwrap(); +} + +#[pyclass] +struct StaticMethodWithArgs{token: PyToken} + +#[pymethods] +impl StaticMethodWithArgs { + + #[staticmethod] + fn method(_py: Python, input: i32) -> PyResult { + Ok(format!("0x{:x}", input)) + } +} + +#[test] +fn static_method_with_args() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + assert_eq!(StaticMethodWithArgs::method(py, 1234).unwrap(), "0x4d2"); + + let d = PyDict::new(py); + d.set_item("C", py.get_type::()).unwrap(); + py.run("assert C.method(1337) == '0x539'", None, Some(d)).unwrap(); +} + +#[pyclass] +struct MethArgs { + token: PyToken +} + +#[pymethods] +impl MethArgs { + + #[args(test="10")] + fn get_default(&self, test: i32) -> PyResult { + Ok(test) + } + #[args("*", test=10)] + fn get_kwarg(&self, test: i32) -> PyResult { + Ok(test) + } + #[args(args="*", kwargs="**")] + fn get_kwargs(&self, args: &PyTuple, kwargs: Option<&PyDict>) -> PyResult { + Ok([args.into(), kwargs.to_object(self.py())].to_object(self.py())) + } +} + +#[test] +fn meth_args() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst = py.init(|t| MethArgs{token: t}).unwrap(); + + py_run!(py, inst, "assert inst.get_default() == 10"); + py_run!(py, inst, "assert inst.get_default(100) == 100"); + py_run!(py, inst, "assert inst.get_kwarg() == 10"); + py_run!(py, inst, "assert inst.get_kwarg(100) == 10"); + py_run!(py, inst, "assert inst.get_kwarg(test=100) == 100"); + py_run!(py, inst, "assert inst.get_kwargs() == [(), None]"); + py_run!(py, inst, "assert inst.get_kwargs(1,2,3) == [(1,2,3), None]"); + py_run!(py, inst, "assert inst.get_kwargs(t=1,n=2) == [(), {'t': 1, 'n': 2}]"); + py_run!(py, inst, "assert inst.get_kwargs(1,2,3,t=1,n=2) == [(1,2,3), {'t': 1, 'n': 2}]"); + // py_expect_exception!(py, inst, "inst.get_kwarg(100)", TypeError); +} \ No newline at end of file diff --git a/tests/test_module.rs b/tests/test_module.rs index 95562864..15420654 100644 --- a/tests/test_module.rs +++ b/tests/test_module.rs @@ -1,9 +1,8 @@ #![feature(proc_macro, specialization)] -#[macro_use] extern crate pyo3; -use pyo3::{PyDict, PyModule, PyObject, PyResult, Python}; +use pyo3::prelude::*; use pyo3::py::{class, modinit}; diff --git a/tests/test_slice.rs b/tests/test_slice.rs deleted file mode 100644 index aaaf64f7..00000000 --- a/tests/test_slice.rs +++ /dev/null @@ -1,61 +0,0 @@ -#![feature(proc_macro, specialization, const_fn, const_align_of, const_size_of)] -#![allow(dead_code, unused_variables)] - -extern crate pyo3; - -use pyo3::*; - -use pyo3::py::class as pyclass; -use pyo3::py::proto as pyproto; - - -#[test] -fn test_basics() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let v = PySlice::new(py, 1, 10, 2); - let indices = v.indices(100).unwrap(); - assert_eq!(1, indices.start); - assert_eq!(10, indices.stop); - assert_eq!(2, indices.step); - assert_eq!(5, indices.slicelength); -} - - -#[pyclass] -struct Test { - token: PyToken -} - -#[pyproto] -impl<'p> PyMappingProtocol<'p> for Test -{ - fn __getitem__(&self, idx: &PyObjectRef) -> PyResult { - if let Ok(slice) = idx.cast_as::() { - let indices = slice.indices(1000)?; - if indices.start == 100 && indices.stop == 200 && indices.step == 1 { - return Ok("slice".into_object(self.py())) - } - } - else if let Ok(idx) = idx.extract::() { - if idx == 1 { - return Ok("int".into_object(self.py())) - } - } - Err(PyErr::new::("error")) - } -} - -#[test] -fn test_cls_impl() { - let gil = Python::acquire_gil(); - let py = gil.python(); - - let ob = py.init(|t| Test{token: t}).unwrap(); - let d = PyDict::new(py); - d.set_item("ob", ob).unwrap(); - - py.run("assert ob[1] == 'int'", None, Some(d)).unwrap(); - py.run("assert ob[100:200:1] == 'slice'", None, Some(d)).unwrap(); -} diff --git a/tests/test_underscore_methods.rs b/tests/test_underscore_methods.rs new file mode 100644 index 00000000..e93d7a95 --- /dev/null +++ b/tests/test_underscore_methods.rs @@ -0,0 +1,436 @@ +#![feature(proc_macro, specialization)] + +extern crate pyo3; + +use pyo3::prelude::*; +use std::{isize, iter}; +use pyo3::ffi; + +use pyo3::py::class as pyclass; +use pyo3::py::methods as pymethods; +use pyo3::py::proto as pyproto; + + +#[macro_use] +mod common; + + +#[pyclass] +pub struct Len { + l: usize, + token: PyToken, +} + +#[pyproto] +impl PyMappingProtocol for Len { + fn __len__(&self) -> PyResult { + Ok(self.l) + } +} + +#[test] +fn len() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let inst = Py::new(py, |t| Len{l: 10, token: t}).unwrap(); + py_assert!(py, inst, "len(inst) == 10"); + unsafe { + assert_eq!(ffi::PyObject_Size(inst.as_ptr()), 10); + assert_eq!(ffi::PyMapping_Size(inst.as_ptr()), 10); + } + + let inst = Py::new(py, |t| Len{l: (isize::MAX as usize) + 1, token: t}).unwrap(); + py_expect_exception!(py, inst, "len(inst)", OverflowError); +} + +#[pyclass] +struct Iterator{ + iter: Box + Send>, + token: PyToken, +} + +#[pyproto] +impl PyIterProtocol for Iterator { + fn __iter__(&mut self) -> PyResult> { + Ok(self.into()) + } + + fn __next__(&mut self) -> PyResult> { + Ok(self.iter.next()) + } +} + +#[test] +fn iterator() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let inst = Py::new(py, |t| Iterator{iter: Box::new(5..8), token: t}).unwrap(); + py_assert!(py, inst, "iter(inst) is inst"); + py_assert!(py, inst, "list(inst) == [5, 6, 7]"); +} + +#[pyclass] +struct StringMethods {token: PyToken} + +#[pyproto] +impl<'p> PyObjectProtocol<'p> for StringMethods { + fn __str__(&self) -> PyResult<&'static str> { + Ok("str") + } + + fn __repr__(&self) -> PyResult<&'static str> { + Ok("repr") + } + + fn __format__(&self, format_spec: String) -> PyResult { + Ok(format!("format({})", format_spec)) + } + + fn __unicode__(&self) -> PyResult { + Ok(PyString::new(self.py(), "unicode").into()) + } + + fn __bytes__(&self) -> PyResult { + Ok(PyBytes::new(self.py(), b"bytes").into()) + } +} + +#[cfg(Py_3)] +#[test] +fn string_methods() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let obj = Py::new(py, |t| StringMethods{token: t}).unwrap(); + py_assert!(py, obj, "str(obj) == 'str'"); + py_assert!(py, obj, "repr(obj) == 'repr'"); + py_assert!(py, obj, "'{0:x}'.format(obj) == 'format(x)'"); + py_assert!(py, obj, "bytes(obj) == b'bytes'"); +} + +#[cfg(not(Py_3))] +#[test] +fn string_methods() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let obj = Py::new(py, |t| StringMethods{token: t}).unwrap(); + py_assert!(py, obj, "str(obj) == 'str'"); + py_assert!(py, obj, "repr(obj) == 'repr'"); + py_assert!(py, obj, "unicode(obj) == 'unicode'"); + py_assert!(py, obj, "'{0:x}'.format(obj) == 'format(x)'"); +} + + +#[pyclass] +struct Comparisons { + val: i32, + token: PyToken, +} + +#[pyproto] +impl PyObjectProtocol for Comparisons { + fn __hash__(&self) -> PyResult { + Ok(self.val as isize) + } + fn __bool__(&self) -> PyResult { + Ok(self.val != 0) + } +} + + +#[test] +fn comparisons() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let zero = Py::new(py, |t| Comparisons{val: 0, token: t}).unwrap(); + let one = Py::new(py, |t| Comparisons{val: 1, token: t}).unwrap(); + let ten = Py::new(py, |t| Comparisons{val: 10, token: t}).unwrap(); + let minus_one = Py::new(py, |t| Comparisons{val: -1, token: t}).unwrap(); + py_assert!(py, one, "hash(one) == 1"); + py_assert!(py, ten, "hash(ten) == 10"); + py_assert!(py, minus_one, "hash(minus_one) == -2"); + + py_assert!(py, one, "bool(one) is True"); + py_assert!(py, zero, "not zero"); +} + + +#[pyclass] +struct Sequence { + token: PyToken +} + +#[pyproto] +impl PySequenceProtocol for Sequence { + fn __len__(&self) -> PyResult { + Ok(5) + } + + fn __getitem__(&self, key: isize) -> PyResult { + if key == 5 { + return Err(PyErr::new::(NoArgs)); + } + Ok(key) + } +} + +#[test] +fn sequence() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let c = py.init(|t| Sequence{token: t}).unwrap(); + py_assert!(py, c, "list(c) == [0, 1, 2, 3, 4]"); + py_expect_exception!(py, c, "c['abc']", TypeError); +} + + +#[pyclass] +struct Callable {token: PyToken} + +#[pymethods] +impl Callable { + + #[__call__] + fn __call__(&self, arg: i32) -> PyResult { + Ok(arg * 6) + } +} + +#[test] +fn callable() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let c = py.init(|t| Callable{token: t}).unwrap(); + py_assert!(py, c, "callable(c)"); + py_assert!(py, c, "c(7) == 42"); + + let nc = py.init(|t| Comparisons{val: 0, token: t}).unwrap(); + py_assert!(py, nc, "not callable(nc)"); +} + +#[pyclass] +struct SetItem { + key: i32, + val: i32, + token: PyToken, +} + +#[pyproto] +impl PyMappingProtocol<'a> for SetItem { + fn __setitem__(&mut self, key: i32, val: i32) -> PyResult<()> { + self.key = key; + self.val = val; + Ok(()) + } +} + +#[test] +fn setitem() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let c = py.init_ref(|t| SetItem{key: 0, val: 0, token: t}).unwrap(); + py_run!(py, c, "c[1] = 2"); + assert_eq!(c.key, 1); + assert_eq!(c.val, 2); + py_expect_exception!(py, c, "del c[1]", NotImplementedError); +} + +#[pyclass] +struct DelItem { + key: i32, + token: PyToken, +} + +#[pyproto] +impl PyMappingProtocol<'a> for DelItem { + fn __delitem__(&mut self, key: i32) -> PyResult<()> { + self.key = key; + Ok(()) + } +} + +#[test] +fn delitem() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let c = py.init_ref(|t| DelItem{key:0, token:t}).unwrap(); + py_run!(py, c, "del c[1]"); + assert_eq!(c.key, 1); + py_expect_exception!(py, c, "c[1] = 2", NotImplementedError); +} + +#[pyclass] +struct SetDelItem { + val: Option, + token: PyToken, +} + +#[pyproto] +impl PyMappingProtocol for SetDelItem { + fn __setitem__(&mut self, _key: i32, val: i32) -> PyResult<()> { + self.val = Some(val); + Ok(()) + } + + fn __delitem__(&mut self, _key: i32) -> PyResult<()> { + self.val = None; + Ok(()) + } +} + +#[test] +fn setdelitem() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let c = py.init_ref(|t| SetDelItem{val: None, token: t}).unwrap(); + py_run!(py, c, "c[1] = 2"); + assert_eq!(c.val, Some(2)); + py_run!(py, c, "del c[1]"); + assert_eq!(c.val, None); +} + +#[pyclass] +struct Reversed {token: PyToken} + +#[pyproto] +impl PyMappingProtocol for Reversed{ + fn __reversed__(&self) -> PyResult<&'static str> { + Ok("I am reversed") + } +} + +#[test] +fn reversed() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let c = py.init(|t| Reversed{token: t}).unwrap(); + py_run!(py, c, "assert reversed(c) == 'I am reversed'"); +} + +#[pyclass] +struct Contains {token: PyToken} + +#[pyproto] +impl PySequenceProtocol for Contains { + fn __contains__(&self, item: i32) -> PyResult { + Ok(item >= 0) + } +} + +#[test] +fn contains() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let c = py.init(|t| Contains{token: t}).unwrap(); + py_run!(py, c, "assert 1 in c"); + py_run!(py, c, "assert -1 not in c"); + py_expect_exception!(py, c, "assert 'wrong type' not in c", TypeError); +} + + +#[pyclass] +struct ContextManager { + exit_called: bool, + token: PyToken, +} + +#[pyproto] +impl<'p> PyContextProtocol<'p> for ContextManager { + + fn __enter__(&mut self) -> PyResult { + Ok(42) + } + + fn __exit__(&mut self, + ty: Option<&'p PyType>, + _value: Option<&'p PyObjectRef>, + _traceback: Option<&'p PyObjectRef>) -> PyResult { + self.exit_called = true; + if ty == Some(self.py().get_type::()) { + Ok(true) + } else { + Ok(false) + } + } +} + +#[test] +fn context_manager() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let c = py.init_mut(|t| ContextManager{exit_called: false, token: t}).unwrap(); + py_run!(py, c, "with c as x:\n assert x == 42"); + assert!(c.exit_called); + + c.exit_called = false; + py_run!(py, c, "with c as x:\n raise ValueError"); + assert!(c.exit_called); + + c.exit_called = false; + py_expect_exception!( + py, c, "with c as x:\n raise NotImplementedError", NotImplementedError); + assert!(c.exit_called); +} + + +#[test] +fn test_basics() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let v = PySlice::new(py, 1, 10, 2); + let indices = v.indices(100).unwrap(); + assert_eq!(1, indices.start); + assert_eq!(10, indices.stop); + assert_eq!(2, indices.step); + assert_eq!(5, indices.slicelength); +} + +#[pyclass] +struct Test { + token: PyToken +} + +#[pyproto] +impl<'p> PyMappingProtocol<'p> for Test +{ + fn __getitem__(&self, idx: &PyObjectRef) -> PyResult { + if let Ok(slice) = idx.cast_as::() { + let indices = slice.indices(1000)?; + if indices.start == 100 && indices.stop == 200 && indices.step == 1 { + return Ok("slice".into_object(self.py())) + } + } + else if let Ok(idx) = idx.extract::() { + if idx == 1 { + return Ok("int".into_object(self.py())) + } + } + Err(PyErr::new::("error")) + } +} + +#[test] +fn test_cls_impl() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let ob = py.init(|t| Test{token: t}).unwrap(); + let d = PyDict::new(py); + d.set_item("ob", ob).unwrap(); + + py.run("assert ob[1] == 'int'", None, Some(d)).unwrap(); + py.run("assert ob[100:200:1] == 'slice'", None, Some(d)).unwrap(); +} diff --git a/tests/test_various.rs b/tests/test_various.rs new file mode 100644 index 00000000..0539d084 --- /dev/null +++ b/tests/test_various.rs @@ -0,0 +1,73 @@ +#![feature(proc_macro, specialization)] + +extern crate pyo3; + +use pyo3::prelude::*; +use std::isize; + +use pyo3::py::class as pyclass; +use pyo3::py::methods as pymethods; + +#[macro_use] +mod common; + + +#[pyclass(dict)] +struct DunderDictSupport { + token: PyToken, +} + +#[test] +fn dunder_dict_support() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst = Py::new_ref(py, |t| DunderDictSupport{token: t}).unwrap(); + py_run!(py, inst, "inst.a = 1; assert inst.a == 1"); +} + +#[pyclass(weakref, dict)] +struct WeakRefDunderDictSupport { + token: PyToken, +} + +#[test] +fn weakref_dunder_dict_support() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst = Py::new_ref(py, |t| WeakRefDunderDictSupport{token: t}).unwrap(); + py_run!(py, inst, "import weakref; assert weakref.ref(inst)() is inst; inst.a = 1; assert inst.a == 1"); +} + + +#[pyclass] +struct MutRefArg { + n: i32, + token: PyToken, +} + +#[pymethods] +impl MutRefArg { + + fn get(&self) -> PyResult { + Ok(self.n) + } + fn set_other(&self, other: &mut MutRefArg) -> PyResult<()> { + other.n = 100; + Ok(()) + } +} + +#[test] +fn mut_ref_arg() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let inst1 = py.init(|t| MutRefArg{token: t, n: 0}).unwrap(); + let inst2 = py.init(|t| MutRefArg{token: t, n: 0}).unwrap(); + + let d = PyDict::new(py); + d.set_item("inst1", &inst1).unwrap(); + d.set_item("inst2", &inst2).unwrap(); + + py.run("inst1.set_other(inst2)", None, Some(d)).unwrap(); + assert_eq!(inst2.as_ref(py).n, 100); +}