Merge pull request #1494 from PyO3/enhance-py-run
Extend py_run! to take locals dict and refactor tests using it
This commit is contained in:
commit
acf7271879
56
src/lib.rs
56
src/lib.rs
|
@ -289,10 +289,10 @@ macro_rules! wrap_pymodule {
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// use pyo3::{prelude::*, py_run, types::PyList};
|
/// use pyo3::{prelude::*, py_run, types::PyList};
|
||||||
/// let gil = Python::acquire_gil();
|
/// Python::with_gil(|py| {
|
||||||
/// let py = gil.python();
|
/// let list = PyList::new(py, &[1, 2, 3]);
|
||||||
/// let list = PyList::new(py, &[1, 2, 3]);
|
/// py_run!(py, list, "assert list == [1, 2, 3]");
|
||||||
/// py_run!(py, list, "assert list == [1, 2, 3]");
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// You can use this macro to test pyfunctions or pyclasses quickly.
|
/// You can use this macro to test pyfunctions or pyclasses quickly.
|
||||||
|
@ -320,15 +320,33 @@ macro_rules! wrap_pymodule {
|
||||||
/// (self.hour, self.minute, self.second)
|
/// (self.hour, self.minute, self.second)
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// let gil = Python::acquire_gil();
|
/// Python::with_gil(|py| {
|
||||||
/// let py = gil.python();
|
/// let time = PyCell::new(py, Time {hour: 8, minute: 43, second: 16}).unwrap();
|
||||||
/// let time = PyCell::new(py, Time {hour: 8, minute: 43, second: 16}).unwrap();
|
/// let time_as_tuple = (8, 43, 16);
|
||||||
/// let time_as_tuple = (8, 43, 16);
|
/// py_run!(py, time time_as_tuple, r#"
|
||||||
/// py_run!(py, time time_as_tuple, r#"
|
/// assert time.hour == 8
|
||||||
/// assert time.hour == 8
|
/// assert time.repl_japanese() == "8時43分16秒"
|
||||||
/// assert time.repl_japanese() == "8時43分16秒"
|
/// assert time.as_tuple() == time_as_tuple
|
||||||
/// assert time.as_tuple() == time_as_tuple
|
/// "#);
|
||||||
/// "#);
|
/// });
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// If you need to prepare the `locals` dict by yourself, you can pass it as `*locals`.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use pyo3::prelude::*;
|
||||||
|
/// use pyo3::types::IntoPyDict;
|
||||||
|
/// #[pyclass]
|
||||||
|
/// struct MyClass {}
|
||||||
|
/// #[pymethods]
|
||||||
|
/// impl MyClass {
|
||||||
|
/// #[new]
|
||||||
|
/// fn new() -> Self { MyClass {} }
|
||||||
|
/// }
|
||||||
|
/// Python::with_gil(|py| {
|
||||||
|
/// let locals = [("C", py.get_type::<MyClass>())].into_py_dict(py);
|
||||||
|
/// pyo3::py_run!(py, *locals, "c = C()");
|
||||||
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// **Note**
|
/// **Note**
|
||||||
|
@ -345,6 +363,12 @@ macro_rules! py_run {
|
||||||
($py:expr, $($val:ident)+, $code:expr) => {{
|
($py:expr, $($val:ident)+, $code:expr) => {{
|
||||||
$crate::py_run_impl!($py, $($val)+, &$crate::unindent::unindent($code))
|
$crate::py_run_impl!($py, $($val)+, &$crate::unindent::unindent($code))
|
||||||
}};
|
}};
|
||||||
|
($py:expr, *$dict:expr, $code:literal) => {{
|
||||||
|
$crate::py_run_impl!($py, *$dict, $crate::indoc::indoc!($code))
|
||||||
|
}};
|
||||||
|
($py:expr, *$dict:expr, $code:expr) => {{
|
||||||
|
$crate::py_run_impl!($py, *$dict, &$crate::unindent::unindent($code))
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
@ -355,8 +379,10 @@ macro_rules! py_run_impl {
|
||||||
use $crate::types::IntoPyDict;
|
use $crate::types::IntoPyDict;
|
||||||
use $crate::ToPyObject;
|
use $crate::ToPyObject;
|
||||||
let d = [$((stringify!($val), $val.to_object($py)),)+].into_py_dict($py);
|
let d = [$((stringify!($val), $val.to_object($py)),)+].into_py_dict($py);
|
||||||
|
$crate::py_run_impl!($py, *d, $code)
|
||||||
if let Err(e) = $py.run($code, None, Some(d)) {
|
}};
|
||||||
|
($py:expr, *$dict:expr, $code:expr) => {{
|
||||||
|
if let Err(e) = $py.run($code, None, Some($dict)) {
|
||||||
e.print($py);
|
e.print($py);
|
||||||
// So when this c api function the last line called printed the error to stderr,
|
// So when this c api function the last line called printed the error to stderr,
|
||||||
// the output is only written into a buffer which is never flushed because we
|
// the output is only written into a buffer which is never flushed because we
|
||||||
|
|
|
@ -1,37 +1,43 @@
|
||||||
//! Useful tips for writing tests:
|
//! Some common macros for tests
|
||||||
//! - Tests are run in parallel; There's still a race condition in test_owned with some other test
|
|
||||||
//! - You need to use flush=True to get any output from print
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! py_assert {
|
macro_rules! py_assert {
|
||||||
($py:expr, $val:ident, $assertion:expr) => {
|
($py:expr, $($val:ident)+, $assertion:literal) => {
|
||||||
pyo3::py_run!($py, $val, concat!("assert ", $assertion))
|
pyo3::py_run!($py, $($val)+, concat!("assert ", $assertion))
|
||||||
|
};
|
||||||
|
($py:expr, *$dict:expr, $assertion:literal) => {
|
||||||
|
pyo3::py_run!($py, *$dict, concat!("assert ", $assertion))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! py_expect_exception {
|
macro_rules! py_expect_exception {
|
||||||
($py:expr, $val:ident, $code:expr, $err:ident) => {{
|
// Case1: idents & no err_msg
|
||||||
|
($py:expr, $($val:ident)+, $code:expr, $err:ident) => {{
|
||||||
use pyo3::types::IntoPyDict;
|
use pyo3::types::IntoPyDict;
|
||||||
let d = [(stringify!($val), &$val)].into_py_dict($py);
|
let d = [$((stringify!($val), $val.to_object($py)),)+].into_py_dict($py);
|
||||||
|
py_expect_exception!($py, *d, $code, $err)
|
||||||
let res = $py.run($code, None, Some(d));
|
}};
|
||||||
|
// Case2: dict & no err_msg
|
||||||
|
($py:expr, *$dict:expr, $code:expr, $err:ident) => {{
|
||||||
|
let res = $py.run($code, None, Some($dict));
|
||||||
let err = res.expect_err(&format!("Did not raise {}", stringify!($err)));
|
let err = res.expect_err(&format!("Did not raise {}", stringify!($err)));
|
||||||
if !err.matches($py, $py.get_type::<pyo3::exceptions::$err>()) {
|
if !err.matches($py, $py.get_type::<pyo3::exceptions::$err>()) {
|
||||||
panic!("Expected {} but got {:?}", stringify!($err), err)
|
panic!("Expected {} but got {:?}", stringify!($err), err)
|
||||||
}
|
}
|
||||||
err
|
err
|
||||||
}};
|
}};
|
||||||
($py:expr, $val:ident, $code:expr, $err:ident, $err_msg:expr) => {{
|
// Case3: idents & err_msg
|
||||||
let err = py_expect_exception!($py, $val, $code, $err);
|
($py:expr, $($val:ident)+, $code:expr, $err:ident, $err_msg:literal) => {{
|
||||||
assert_eq!(
|
let err = py_expect_exception!($py, $($val)+, $code, $err);
|
||||||
err.instance($py)
|
// Suppose that the error message looks like 'TypeError: ~'
|
||||||
.str()
|
assert_eq!(format!("Py{}", err), concat!(stringify!($err), ": ", $err_msg));
|
||||||
.expect("error str() failed")
|
err
|
||||||
.to_str()
|
}};
|
||||||
.expect("message was not valid utf8"),
|
// Case4: dict & err_msg
|
||||||
$err_msg
|
($py:expr, *$dict:expr, $code:expr, $err:ident, $err_msg:literal) => {{
|
||||||
);
|
let err = py_expect_exception!($py, *$dict, $code, $err);
|
||||||
|
assert_eq!(format!("Py{}", err), concat!(stringify!($err), ": ", $err_msg));
|
||||||
err
|
err
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ use std::ptr;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
struct TestBufferClass {
|
struct TestBufferClass {
|
||||||
vec: Vec<u8>,
|
vec: Vec<u8>,
|
||||||
|
@ -93,8 +95,7 @@ fn test_buffer() {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let env = [("ob", instance)].into_py_dict(py);
|
let env = [("ob", instance)].into_py_dict(py);
|
||||||
py.run("assert bytes(ob) == b' 23'", None, Some(env))
|
py_assert!(py, *env, "bytes(ob) == b' 23'");
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(drop_called.load(Ordering::Relaxed));
|
assert!(drop_called.load(Ordering::Relaxed));
|
||||||
|
|
|
@ -4,7 +4,7 @@ use pyo3::class::{
|
||||||
};
|
};
|
||||||
use pyo3::exceptions::{PyIndexError, PyValueError};
|
use pyo3::exceptions::{PyIndexError, PyValueError};
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use pyo3::types::{IntoPyDict, PySlice, PyType};
|
use pyo3::types::{PySlice, PyType};
|
||||||
use pyo3::{ffi, py_run, AsPyPointer, PyCell};
|
use pyo3::{ffi, py_run, AsPyPointer, PyCell};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::{isize, iter};
|
use std::{isize, iter};
|
||||||
|
@ -450,11 +450,9 @@ fn test_cls_impl() {
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
|
|
||||||
let ob = Py::new(py, Test {}).unwrap();
|
let ob = Py::new(py, Test {}).unwrap();
|
||||||
let d = [("ob", ob)].into_py_dict(py);
|
|
||||||
|
|
||||||
py.run("assert ob[1] == 'int'", None, Some(d)).unwrap();
|
py_assert!(py, ob, "ob[1] == 'int'");
|
||||||
py.run("assert ob[100:200:1] == 'slice'", None, Some(d))
|
py_assert!(py, ob, "ob[100:200:1] == 'slice'");
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass(dict, subclass)]
|
#[pyclass(dict, subclass)]
|
||||||
|
|
|
@ -60,12 +60,7 @@ fn class_with_properties() {
|
||||||
py_run!(py, inst, "assert inst.data_list == [42]");
|
py_run!(py, inst, "assert inst.data_list == [42]");
|
||||||
|
|
||||||
let d = [("C", py.get_type::<ClassWithProperties>())].into_py_dict(py);
|
let d = [("C", py.get_type::<ClassWithProperties>())].into_py_dict(py);
|
||||||
py.run(
|
py_assert!(py, *d, "C.DATA.__doc__ == 'a getter for data'");
|
||||||
"assert C.DATA.__doc__ == 'a getter for data'",
|
|
||||||
None,
|
|
||||||
Some(d),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
|
|
@ -2,10 +2,13 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use pyo3::exceptions::PyKeyError;
|
use pyo3::exceptions::PyKeyError;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
use pyo3::py_run;
|
||||||
use pyo3::types::IntoPyDict;
|
use pyo3::types::IntoPyDict;
|
||||||
use pyo3::types::PyList;
|
use pyo3::types::PyList;
|
||||||
use pyo3::PyMappingProtocol;
|
use pyo3::PyMappingProtocol;
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
struct Mapping {
|
struct Mapping {
|
||||||
index: HashMap<String, usize>,
|
index: HashMap<String, usize>,
|
||||||
|
@ -66,35 +69,36 @@ impl PyMappingProtocol for Mapping {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a dict with `m = Mapping(['1', '2', '3'])`.
|
||||||
|
fn map_dict(py: Python) -> &pyo3::types::PyDict {
|
||||||
|
let d = [("Mapping", py.get_type::<Mapping>())].into_py_dict(py);
|
||||||
|
py_run!(py, *d, "m = Mapping(['1', '2', '3'])");
|
||||||
|
d
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_getitem() {
|
fn test_getitem() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
let d = [("Mapping", py.get_type::<Mapping>())].into_py_dict(py);
|
let d = map_dict(py);
|
||||||
|
|
||||||
let run = |code| py.run(code, None, Some(d)).unwrap();
|
py_assert!(py, *d, "m['1'] == 0");
|
||||||
let err = |code| py.run(code, None, Some(d)).unwrap_err();
|
py_assert!(py, *d, "m['2'] == 1");
|
||||||
|
py_assert!(py, *d, "m['3'] == 2");
|
||||||
run("m = Mapping(['1', '2', '3']); assert m['1'] == 0");
|
py_expect_exception!(py, *d, "print(m['4'])", PyKeyError);
|
||||||
run("m = Mapping(['1', '2', '3']); assert m['2'] == 1");
|
|
||||||
run("m = Mapping(['1', '2', '3']); assert m['3'] == 2");
|
|
||||||
err("m = Mapping(['1', '2', '3']); print(m['4'])");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_setitem() {
|
fn test_setitem() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
let d = [("Mapping", py.get_type::<Mapping>())].into_py_dict(py);
|
let d = map_dict(py);
|
||||||
|
|
||||||
let run = |code| py.run(code, None, Some(d)).unwrap();
|
py_run!(py, *d, "m['1'] = 4; assert m['1'] == 4");
|
||||||
let err = |code| py.run(code, None, Some(d)).unwrap_err();
|
py_run!(py, *d, "m['0'] = 0; assert m['0'] == 0");
|
||||||
|
py_assert!(py, *d, "len(m) == 4");
|
||||||
run("m = Mapping(['1', '2', '3']); m['1'] = 4; assert m['1'] == 4");
|
py_expect_exception!(py, *d, "m[0] = 'hello'", PyTypeError);
|
||||||
run("m = Mapping(['1', '2', '3']); m['0'] = 0; assert m['0'] == 0");
|
py_expect_exception!(py, *d, "m[0] = -1", PyTypeError);
|
||||||
run("m = Mapping(['1', '2', '3']); len(m) == 4");
|
|
||||||
err("m = Mapping(['1', '2', '3']); m[0] = 'hello'");
|
|
||||||
err("m = Mapping(['1', '2', '3']); m[0] = -1");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -102,16 +106,14 @@ fn test_delitem() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
|
|
||||||
let d = [("Mapping", py.get_type::<Mapping>())].into_py_dict(py);
|
let d = map_dict(py);
|
||||||
let run = |code| py.run(code, None, Some(d)).unwrap();
|
py_run!(
|
||||||
let err = |code| py.run(code, None, Some(d)).unwrap_err();
|
py,
|
||||||
|
*d,
|
||||||
run(
|
"del m['1']; assert len(m) == 2 and m['2'] == 1 and m['3'] == 2"
|
||||||
"m = Mapping(['1', '2', '3']); del m['1']; assert len(m) == 2; \
|
|
||||||
assert m['2'] == 1; assert m['3'] == 2",
|
|
||||||
);
|
);
|
||||||
err("m = Mapping(['1', '2', '3']); del m[-1]");
|
py_expect_exception!(py, *d, "del m[-1]", PyTypeError);
|
||||||
err("m = Mapping(['1', '2', '3']); del m['4']");
|
py_expect_exception!(py, *d, "del m['4']", PyKeyError);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -119,8 +121,6 @@ fn test_reversed() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
|
|
||||||
let d = [("Mapping", py.get_type::<Mapping>())].into_py_dict(py);
|
let d = map_dict(py);
|
||||||
let run = |code| py.run(code, None, Some(d)).unwrap();
|
py_assert!(py, *d, "set(reversed(m)) == {'1', '2', '3'}");
|
||||||
|
|
||||||
run("m = Mapping(['1', '2']); assert set(reversed(m)) == {'1', '2'}");
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,10 +56,8 @@ fn instance_method_with_args() {
|
||||||
let obj = PyCell::new(py, InstanceMethodWithArgs { member: 7 }).unwrap();
|
let obj = PyCell::new(py, InstanceMethodWithArgs { member: 7 }).unwrap();
|
||||||
let obj_ref = obj.borrow();
|
let obj_ref = obj.borrow();
|
||||||
assert_eq!(obj_ref.method(6), 42);
|
assert_eq!(obj_ref.method(6), 42);
|
||||||
let d = [("obj", obj)].into_py_dict(py);
|
py_assert!(py, obj, "obj.method(3) == 21");
|
||||||
py.run("assert obj.method(3) == 21", None, Some(d)).unwrap();
|
py_assert!(py, obj, "obj.method(multiplier=6) == 42");
|
||||||
py.run("assert obj.method(multiplier=6) == 42", None, Some(d))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
@ -85,15 +83,10 @@ fn class_method() {
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
|
|
||||||
let d = [("C", py.get_type::<ClassMethod>())].into_py_dict(py);
|
let d = [("C", py.get_type::<ClassMethod>())].into_py_dict(py);
|
||||||
let run = |code| {
|
py_assert!(py, *d, "C.method() == 'ClassMethod.method()!'");
|
||||||
py.run(code, None, Some(d))
|
py_assert!(py, *d, "C().method() == 'ClassMethod.method()!'");
|
||||||
.map_err(|e| e.print(py))
|
py_assert!(py, *d, "C.method.__doc__ == 'Test class method.'");
|
||||||
.unwrap()
|
py_assert!(py, *d, "C().method.__doc__ == 'Test class method.'");
|
||||||
};
|
|
||||||
run("assert C.method() == 'ClassMethod.method()!'");
|
|
||||||
run("assert C().method() == 'ClassMethod.method()!'");
|
|
||||||
run("assert C.method.__doc__ == 'Test class method.'");
|
|
||||||
run("assert C().method.__doc__ == 'Test class method.'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
@ -113,12 +106,11 @@ fn class_method_with_args() {
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
|
|
||||||
let d = [("C", py.get_type::<ClassMethodWithArgs>())].into_py_dict(py);
|
let d = [("C", py.get_type::<ClassMethodWithArgs>())].into_py_dict(py);
|
||||||
py.run(
|
py_assert!(
|
||||||
"assert C.method('abc') == 'ClassMethodWithArgs.method(abc)'",
|
py,
|
||||||
None,
|
*d,
|
||||||
Some(d),
|
"C.method('abc') == 'ClassMethodWithArgs.method(abc)'"
|
||||||
)
|
);
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
@ -146,15 +138,10 @@ fn static_method() {
|
||||||
assert_eq!(StaticMethod::method(py), "StaticMethod.method()!");
|
assert_eq!(StaticMethod::method(py), "StaticMethod.method()!");
|
||||||
|
|
||||||
let d = [("C", py.get_type::<StaticMethod>())].into_py_dict(py);
|
let d = [("C", py.get_type::<StaticMethod>())].into_py_dict(py);
|
||||||
let run = |code| {
|
py_assert!(py, *d, "C.method() == 'StaticMethod.method()!'");
|
||||||
py.run(code, None, Some(d))
|
py_assert!(py, *d, "C().method() == 'StaticMethod.method()!'");
|
||||||
.map_err(|e| e.print(py))
|
py_assert!(py, *d, "C.method.__doc__ == 'Test static method.'");
|
||||||
.unwrap()
|
py_assert!(py, *d, "C().method.__doc__ == 'Test static method.'");
|
||||||
};
|
|
||||||
run("assert C.method() == 'StaticMethod.method()!'");
|
|
||||||
run("assert C().method() == 'StaticMethod.method()!'");
|
|
||||||
run("assert C.method.__doc__ == 'Test static method.'");
|
|
||||||
run("assert C().method.__doc__ == 'Test static method.'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
@ -176,8 +163,7 @@ fn static_method_with_args() {
|
||||||
assert_eq!(StaticMethodWithArgs::method(py, 1234), "0x4d2");
|
assert_eq!(StaticMethodWithArgs::method(py, 1234), "0x4d2");
|
||||||
|
|
||||||
let d = [("C", py.get_type::<StaticMethodWithArgs>())].into_py_dict(py);
|
let d = [("C", py.get_type::<StaticMethodWithArgs>())].into_py_dict(py);
|
||||||
py.run("assert C.method(1337) == '0x539'", None, Some(d))
|
py_assert!(py, *d, "C.method(1337) == '0x539'");
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
@ -449,15 +435,17 @@ fn meth_doc() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
let d = [("C", py.get_type::<MethDocs>())].into_py_dict(py);
|
let d = [("C", py.get_type::<MethDocs>())].into_py_dict(py);
|
||||||
let run = |code| {
|
py_assert!(py, *d, "C.__doc__ == 'A class with \"documentation\".'");
|
||||||
py.run(code, None, Some(d))
|
py_assert!(
|
||||||
.map_err(|e| e.print(py))
|
py,
|
||||||
.unwrap()
|
*d,
|
||||||
};
|
"C.method.__doc__ == 'A method with \"documentation\" as well.'"
|
||||||
|
);
|
||||||
run("assert C.__doc__ == 'A class with \"documentation\".'");
|
py_assert!(
|
||||||
run("assert C.method.__doc__ == 'A method with \"documentation\" as well.'");
|
py,
|
||||||
run("assert C.x.__doc__ == '`int`: a very \"important\" member of \\'this\\' instance.'");
|
*d,
|
||||||
|
"C.x.__doc__ == '`int`: a very \"important\" member of \\'this\\' instance.'"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
@ -530,20 +518,31 @@ fn method_with_pyclassarg() {
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
let obj1 = PyCell::new(py, MethodWithPyClassArg { value: 10 }).unwrap();
|
let obj1 = PyCell::new(py, MethodWithPyClassArg { value: 10 }).unwrap();
|
||||||
let obj2 = PyCell::new(py, MethodWithPyClassArg { value: 10 }).unwrap();
|
let obj2 = PyCell::new(py, MethodWithPyClassArg { value: 10 }).unwrap();
|
||||||
let objs = [("obj1", obj1), ("obj2", obj2)].into_py_dict(py);
|
let d = [("obj1", obj1), ("obj2", obj2)].into_py_dict(py);
|
||||||
let run = |code| {
|
py_run!(py, *d, "obj = obj1.add(obj2); assert obj.value == 20");
|
||||||
py.run(code, None, Some(objs))
|
py_run!(py, *d, "obj = obj1.add_pyref(obj2); assert obj.value == 20");
|
||||||
.map_err(|e| e.print(py))
|
py_run!(py, *d, "obj = obj1.optional_add(); assert obj.value == 20");
|
||||||
.unwrap()
|
py_run!(
|
||||||
};
|
py,
|
||||||
run("obj = obj1.add(obj2); assert obj.value == 20");
|
*d,
|
||||||
run("obj = obj1.add_pyref(obj2); assert obj.value == 20");
|
"obj = obj1.optional_add(obj2); assert obj.value == 20"
|
||||||
run("obj = obj1.optional_add(); assert obj.value == 20");
|
);
|
||||||
run("obj = obj1.optional_add(obj2); assert obj.value == 20");
|
py_run!(py, *d, "obj1.inplace_add(obj2); assert obj.value == 20");
|
||||||
run("obj1.inplace_add(obj2); assert obj.value == 20");
|
py_run!(
|
||||||
run("obj1.inplace_add_pyref(obj2); assert obj2.value == 30");
|
py,
|
||||||
run("obj1.optional_inplace_add(); assert obj2.value == 30");
|
*d,
|
||||||
run("obj1.optional_inplace_add(obj2); assert obj2.value == 40");
|
"obj1.inplace_add_pyref(obj2); assert obj2.value == 30"
|
||||||
|
);
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
*d,
|
||||||
|
"obj1.optional_inplace_add(); assert obj2.value == 30"
|
||||||
|
);
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
*d,
|
||||||
|
"obj1.optional_inplace_add(obj2); assert obj2.value == 40"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
use pyo3::py_run;
|
||||||
use pyo3::types::{IntoPyDict, PyDict, PyTuple};
|
use pyo3::types::{IntoPyDict, PyDict, PyTuple};
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
@ -83,25 +83,43 @@ fn test_module_with_functions() {
|
||||||
)]
|
)]
|
||||||
.into_py_dict(py);
|
.into_py_dict(py);
|
||||||
|
|
||||||
let run = |code| {
|
py_assert!(
|
||||||
py.run(code, None, Some(d))
|
py,
|
||||||
.map_err(|e| e.print(py))
|
*d,
|
||||||
.unwrap()
|
"module_with_functions.__doc__ == 'This module is implemented in Rust.'"
|
||||||
};
|
);
|
||||||
|
py_assert!(py, *d, "module_with_functions.sum_as_string(1, 2) == '3'");
|
||||||
run("assert module_with_functions.__doc__ == 'This module is implemented in Rust.'");
|
py_assert!(py, *d, "module_with_functions.no_parameters() == 42");
|
||||||
run("assert module_with_functions.sum_as_string(1, 2) == '3'");
|
py_assert!(py, *d, "module_with_functions.foo == 'bar'");
|
||||||
run("assert module_with_functions.no_parameters() == 42");
|
py_assert!(py, *d, "module_with_functions.AnonClass != None");
|
||||||
run("assert module_with_functions.foo == 'bar'");
|
py_assert!(py, *d, "module_with_functions.LocatedClass != None");
|
||||||
run("assert module_with_functions.AnonClass != None");
|
py_assert!(
|
||||||
run("assert module_with_functions.LocatedClass != None");
|
py,
|
||||||
run("assert module_with_functions.LocatedClass.__module__ == 'module'");
|
*d,
|
||||||
run("assert module_with_functions.double(3) == 6");
|
"module_with_functions.LocatedClass.__module__ == 'module'"
|
||||||
run("assert module_with_functions.double.__doc__ == 'Doubles the given value'");
|
);
|
||||||
run("assert module_with_functions.also_double(3) == 6");
|
py_assert!(py, *d, "module_with_functions.double(3) == 6");
|
||||||
run("assert module_with_functions.also_double.__doc__ == 'Doubles the given value'");
|
py_assert!(
|
||||||
run("assert module_with_functions.double_value(module_with_functions.ValueClass(1)) == 2");
|
py,
|
||||||
run("assert module_with_functions.with_module() == 'module_with_functions'");
|
*d,
|
||||||
|
"module_with_functions.double.__doc__ == 'Doubles the given value'"
|
||||||
|
);
|
||||||
|
py_assert!(py, *d, "module_with_functions.also_double(3) == 6");
|
||||||
|
py_assert!(
|
||||||
|
py,
|
||||||
|
*d,
|
||||||
|
"module_with_functions.also_double.__doc__ == 'Doubles the given value'"
|
||||||
|
);
|
||||||
|
py_assert!(
|
||||||
|
py,
|
||||||
|
*d,
|
||||||
|
"module_with_functions.double_value(module_with_functions.ValueClass(1)) == 2"
|
||||||
|
);
|
||||||
|
py_assert!(
|
||||||
|
py,
|
||||||
|
*d,
|
||||||
|
"module_with_functions.with_module() == 'module_with_functions'"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pymodule(other_name)]
|
#[pymodule(other_name)]
|
||||||
|
@ -119,12 +137,7 @@ fn test_module_renaming() {
|
||||||
|
|
||||||
let d = [("different_name", wrap_pymodule!(other_name)(py))].into_py_dict(py);
|
let d = [("different_name", wrap_pymodule!(other_name)(py))].into_py_dict(py);
|
||||||
|
|
||||||
py.run(
|
py_run!(py, *d, "assert different_name.__name__ == 'other_name'");
|
||||||
"assert different_name.__name__ == 'other_name'",
|
|
||||||
None,
|
|
||||||
Some(d),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -83,33 +83,35 @@ impl PySequenceProtocol for ByteSequence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a dict with `s = ByteSequence([1, 2, 3])`.
|
||||||
|
fn seq_dict(py: Python) -> &pyo3::types::PyDict {
|
||||||
|
let d = [("ByteSequence", py.get_type::<ByteSequence>())].into_py_dict(py);
|
||||||
|
// Though we can construct `s` in Rust, let's test `__new__` works.
|
||||||
|
py_run!(py, *d, "s = ByteSequence([1, 2, 3])");
|
||||||
|
d
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_getitem() {
|
fn test_getitem() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
let d = [("ByteSequence", py.get_type::<ByteSequence>())].into_py_dict(py);
|
let d = seq_dict(py);
|
||||||
|
|
||||||
let run = |code| py.run(code, None, Some(d)).unwrap();
|
py_assert!(py, *d, "s[0] == 1");
|
||||||
let err = |code| py.run(code, None, Some(d)).unwrap_err();
|
py_assert!(py, *d, "s[1] == 2");
|
||||||
|
py_assert!(py, *d, "s[2] == 3");
|
||||||
run("s = ByteSequence([1, 2, 3]); assert s[0] == 1");
|
py_expect_exception!(py, *d, "print(s[-4])", PyIndexError);
|
||||||
run("s = ByteSequence([1, 2, 3]); assert s[1] == 2");
|
py_expect_exception!(py, *d, "print(s[4])", PyIndexError);
|
||||||
run("s = ByteSequence([1, 2, 3]); assert s[2] == 3");
|
|
||||||
err("s = ByteSequence([1, 2, 3]); print(s[-4])");
|
|
||||||
err("s = ByteSequence([1, 2, 3]); print(s[4])");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_setitem() {
|
fn test_setitem() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
let d = [("ByteSequence", py.get_type::<ByteSequence>())].into_py_dict(py);
|
let d = seq_dict(py);
|
||||||
|
|
||||||
let run = |code| py.run(code, None, Some(d)).unwrap();
|
py_run!(py, *d, "s[0] = 4; assert list(s) == [4, 2, 3]");
|
||||||
let err = |code| py.run(code, None, Some(d)).unwrap_err();
|
py_expect_exception!(py, *d, "s[0] = 'hello'", PyTypeError);
|
||||||
|
|
||||||
run("s = ByteSequence([1, 2, 3]); s[0] = 4; assert list(s) == [4, 2, 3]");
|
|
||||||
err("s = ByteSequence([1, 2, 3]); s[0] = 'hello'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -118,15 +120,39 @@ fn test_delitem() {
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
|
|
||||||
let d = [("ByteSequence", py.get_type::<ByteSequence>())].into_py_dict(py);
|
let d = [("ByteSequence", py.get_type::<ByteSequence>())].into_py_dict(py);
|
||||||
let run = |code| py.run(code, None, Some(d)).unwrap();
|
|
||||||
let err = |code| py.run(code, None, Some(d)).unwrap_err();
|
|
||||||
|
|
||||||
run("s = ByteSequence([1, 2, 3]); del s[0]; assert list(s) == [2, 3]");
|
py_run!(
|
||||||
run("s = ByteSequence([1, 2, 3]); del s[1]; assert list(s) == [1, 3]");
|
py,
|
||||||
run("s = ByteSequence([1, 2, 3]); del s[-1]; assert list(s) == [1, 2]");
|
*d,
|
||||||
run("s = ByteSequence([1, 2, 3]); del s[-2]; assert list(s) == [1, 3]");
|
"s = ByteSequence([1, 2, 3]); del s[0]; assert list(s) == [2, 3]"
|
||||||
err("s = ByteSequence([1, 2, 3]); del s[-4]; print(list(s))");
|
);
|
||||||
err("s = ByteSequence([1, 2, 3]); del s[4]");
|
py_run!(
|
||||||
|
py,
|
||||||
|
*d,
|
||||||
|
"s = ByteSequence([1, 2, 3]); del s[1]; assert list(s) == [1, 3]"
|
||||||
|
);
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
*d,
|
||||||
|
"s = ByteSequence([1, 2, 3]); del s[-1]; assert list(s) == [1, 2]"
|
||||||
|
);
|
||||||
|
py_run!(
|
||||||
|
py,
|
||||||
|
*d,
|
||||||
|
"s = ByteSequence([1, 2, 3]); del s[-2]; assert list(s) == [1, 3]"
|
||||||
|
);
|
||||||
|
py_expect_exception!(
|
||||||
|
py,
|
||||||
|
*d,
|
||||||
|
"s = ByteSequence([1, 2, 3]); del s[-4]; print(list(s))",
|
||||||
|
PyIndexError
|
||||||
|
);
|
||||||
|
py_expect_exception!(
|
||||||
|
py,
|
||||||
|
*d,
|
||||||
|
"s = ByteSequence([1, 2, 3]); del s[4]",
|
||||||
|
PyIndexError
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -134,14 +160,13 @@ fn test_contains() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
|
|
||||||
let d = [("ByteSequence", py.get_type::<ByteSequence>())].into_py_dict(py);
|
let d = seq_dict(py);
|
||||||
let run = |code| py.run(code, None, Some(d)).unwrap();
|
|
||||||
|
|
||||||
run("s = ByteSequence([1, 2, 3]); assert 1 in s");
|
py_assert!(py, *d, "1 in s");
|
||||||
run("s = ByteSequence([1, 2, 3]); assert 2 in s");
|
py_assert!(py, *d, "2 in s");
|
||||||
run("s = ByteSequence([1, 2, 3]); assert 3 in s");
|
py_assert!(py, *d, "3 in s");
|
||||||
run("s = ByteSequence([1, 2, 3]); assert 4 not in s");
|
py_assert!(py, *d, "4 not in s");
|
||||||
run("s = ByteSequence([1, 2, 3]); assert 'hello' not in s");
|
py_assert!(py, *d, "'hello' not in s");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -149,12 +174,19 @@ fn test_concat() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
|
|
||||||
let d = [("ByteSequence", py.get_type::<ByteSequence>())].into_py_dict(py);
|
let d = seq_dict(py);
|
||||||
let run = |code| py.run(code, None, Some(d)).unwrap();
|
|
||||||
let err = |code| py.run(code, None, Some(d)).unwrap_err();
|
|
||||||
|
|
||||||
run("s1 = ByteSequence([1, 2]); s2 = ByteSequence([3, 4]); assert list(s1+s2) == [1, 2, 3, 4]");
|
py_run!(
|
||||||
err("s1 = ByteSequence([1, 2]); s2 = 'hello'; s1 + s2");
|
py,
|
||||||
|
*d,
|
||||||
|
"s1 = ByteSequence([1, 2]); s2 = ByteSequence([3, 4]); assert list(s1 + s2) == [1, 2, 3, 4]"
|
||||||
|
);
|
||||||
|
py_expect_exception!(
|
||||||
|
py,
|
||||||
|
*d,
|
||||||
|
"s1 = ByteSequence([1, 2]); s2 = 'hello'; s1 + s2",
|
||||||
|
PyTypeError
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -162,12 +194,14 @@ fn test_inplace_concat() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
|
|
||||||
let d = [("ByteSequence", py.get_type::<ByteSequence>())].into_py_dict(py);
|
let d = seq_dict(py);
|
||||||
let run = |code| py.run(code, None, Some(d)).unwrap();
|
|
||||||
let err = |code| py.run(code, None, Some(d)).unwrap_err();
|
|
||||||
|
|
||||||
run("s = ByteSequence([1, 2]); s += ByteSequence([3, 4]); assert list(s) == [1, 2, 3, 4]");
|
py_run!(
|
||||||
err("s = ByteSequence([1, 2]); s += 'hello'");
|
py,
|
||||||
|
*d,
|
||||||
|
"s += ByteSequence([4, 5]); assert list(s) == [1, 2, 3, 4, 5]"
|
||||||
|
);
|
||||||
|
py_expect_exception!(py, *d, "s += 'hello'", PyTypeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -175,12 +209,10 @@ fn test_repeat() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
|
|
||||||
let d = [("ByteSequence", py.get_type::<ByteSequence>())].into_py_dict(py);
|
let d = seq_dict(py);
|
||||||
let run = |code| py.run(code, None, Some(d)).unwrap();
|
|
||||||
let err = |code| py.run(code, None, Some(d)).unwrap_err();
|
|
||||||
|
|
||||||
run("s1 = ByteSequence([1, 2, 3]); s2 = s1*2; assert list(s2) == [1, 2, 3, 1, 2, 3]");
|
py_run!(py, *d, "s2 = s * 2; assert list(s2) == [1, 2, 3, 1, 2, 3]");
|
||||||
err("s1 = ByteSequence([1, 2, 3]); s2 = s1*-1; assert list(s2) == [1, 2, 3, 1, 2, 3]");
|
py_expect_exception!(py, *d, "s2 = s * -1", PyValueError);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -189,11 +221,13 @@ fn test_inplace_repeat() {
|
||||||
let py = gil.python();
|
let py = gil.python();
|
||||||
|
|
||||||
let d = [("ByteSequence", py.get_type::<ByteSequence>())].into_py_dict(py);
|
let d = [("ByteSequence", py.get_type::<ByteSequence>())].into_py_dict(py);
|
||||||
let run = |code| py.run(code, None, Some(d)).unwrap();
|
|
||||||
let err = |code| py.run(code, None, Some(d)).unwrap_err();
|
|
||||||
|
|
||||||
run("s = ByteSequence([1, 2]); s *= 3; assert list(s) == [1, 2, 1, 2, 1, 2]");
|
py_run!(
|
||||||
err("s = ByteSequence([1, 2); s *= -1");
|
py,
|
||||||
|
*d,
|
||||||
|
"s = ByteSequence([1, 2]); s *= 3; assert list(s) == [1, 2, 1, 2, 1, 2]"
|
||||||
|
);
|
||||||
|
py_expect_exception!(py, *d, "s = ByteSequence([1, 2]); s *= -1", PyValueError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that #[pyo3(get, set)] works correctly for Vec<PyObject>
|
// Check that #[pyo3(get, set)] works correctly for Vec<PyObject>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use pyo3::types::IntoPyDict;
|
|
||||||
use pyo3::types::{PyDict, PyTuple};
|
use pyo3::types::{PyDict, PyTuple};
|
||||||
use pyo3::{py_run, wrap_pyfunction, PyCell};
|
use pyo3::{py_run, wrap_pyfunction, PyCell};
|
||||||
|
|
||||||
|
@ -29,9 +28,7 @@ fn mut_ref_arg() {
|
||||||
let inst1 = Py::new(py, MutRefArg { n: 0 }).unwrap();
|
let inst1 = Py::new(py, MutRefArg { n: 0 }).unwrap();
|
||||||
let inst2 = Py::new(py, MutRefArg { n: 0 }).unwrap();
|
let inst2 = Py::new(py, MutRefArg { n: 0 }).unwrap();
|
||||||
|
|
||||||
let d = [("inst1", &inst1), ("inst2", &inst2)].into_py_dict(py);
|
py_run!(py, inst1 inst2, "inst1.set_other(inst2)");
|
||||||
|
|
||||||
py.run("inst1.set_other(inst2)", None, Some(d)).unwrap();
|
|
||||||
let inst2 = inst2.as_ref(py).borrow();
|
let inst2 = inst2.as_ref(py).borrow();
|
||||||
assert_eq!(inst2.n, 100);
|
assert_eq!(inst2.n, 100);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue