Extend py_run! macro to take dict as *d syntax
This commit is contained in:
parent
d3dc031b22
commit
7ec1fed798
55
src/lib.rs
55
src/lib.rs
|
@ -289,10 +289,10 @@ macro_rules! wrap_pymodule {
|
|||
/// # Example
|
||||
/// ```
|
||||
/// use pyo3::{prelude::*, py_run, types::PyList};
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// let py = gil.python();
|
||||
/// let list = PyList::new(py, &[1, 2, 3]);
|
||||
/// py_run!(py, list, "assert list == [1, 2, 3]");
|
||||
/// Python::with_gil(|py| {
|
||||
/// let list = PyList::new(py, &[1, 2, 3]);
|
||||
/// py_run!(py, list, "assert list == [1, 2, 3]");
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// You can use this macro to test pyfunctions or pyclasses quickly.
|
||||
|
@ -320,15 +320,32 @@ macro_rules! wrap_pymodule {
|
|||
/// (self.hour, self.minute, self.second)
|
||||
/// }
|
||||
/// }
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// let py = gil.python();
|
||||
/// let time = PyCell::new(py, Time {hour: 8, minute: 43, second: 16}).unwrap();
|
||||
/// let time_as_tuple = (8, 43, 16);
|
||||
/// py_run!(py, time time_as_tuple, r#"
|
||||
/// assert time.hour == 8
|
||||
/// assert time.repl_japanese() == "8時43分16秒"
|
||||
/// assert time.as_tuple() == time_as_tuple
|
||||
/// "#);
|
||||
/// Python::with_gil(|py| {
|
||||
/// let time = PyCell::new(py, Time {hour: 8, minute: 43, second: 16}).unwrap();
|
||||
/// let time_as_tuple = (8, 43, 16);
|
||||
/// py_run!(py, time time_as_tuple, r#"
|
||||
/// assert time.hour == 8
|
||||
/// assert time.repl_japanese() == "8時43分16秒"
|
||||
/// assert time.as_tuple() == time_as_tuple
|
||||
/// "#);
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// If you need to prepare the `locals` dict by yourself, you can pass it by `*locals`.
|
||||
///
|
||||
/// ```
|
||||
/// # use pyo3::prelude::*;
|
||||
/// #[pyclass]
|
||||
/// struct MyClass {}
|
||||
/// #[pymethods]
|
||||
/// impl MyClass {
|
||||
/// #[new]
|
||||
/// fn new() -> Self { MyClass {} }
|
||||
/// }
|
||||
/// Python::with_gil(|py| {
|
||||
/// let locals = [("C", py.get_type::<MyClass>())];
|
||||
/// pyo3::py_run!(py, *locals, "c = C()");
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// **Note**
|
||||
|
@ -345,6 +362,12 @@ macro_rules! py_run {
|
|||
($py:expr, $($val:ident)+, $code:expr) => {{
|
||||
$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]
|
||||
|
@ -355,8 +378,10 @@ macro_rules! py_run_impl {
|
|||
use $crate::types::IntoPyDict;
|
||||
use $crate::ToPyObject;
|
||||
let d = [$((stringify!($val), $val.to_object($py)),)+].into_py_dict($py);
|
||||
|
||||
if let Err(e) = $py.run($code, None, Some(d)) {
|
||||
$crate::py_run_impl!($py, *d, $code)
|
||||
}};
|
||||
($py:expr, *$dict:expr, $code:expr) => {{
|
||||
if let Err(e) = $py.run($code, None, Some($dict)) {
|
||||
e.print($py);
|
||||
// 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
|
||||
|
|
|
@ -1,37 +1,43 @@
|
|||
//! Useful tips for writing 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
|
||||
//! Some common macros for tests
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! py_assert {
|
||||
($py:expr, $val:ident, $assertion:expr) => {
|
||||
pyo3::py_run!($py, $val, concat!("assert ", $assertion))
|
||||
($py:expr, $($val:ident)+, $assertion:literal) => {
|
||||
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_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;
|
||||
let d = [(stringify!($val), &$val)].into_py_dict($py);
|
||||
|
||||
let res = $py.run($code, None, Some(d));
|
||||
let d = [$((stringify!($val), $val.to_object($py)),)+].into_py_dict($py);
|
||||
py_expect_exception!($py, *d, $code, $err)
|
||||
}};
|
||||
// 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)));
|
||||
if !err.matches($py, $py.get_type::<pyo3::exceptions::$err>()) {
|
||||
panic!("Expected {} but got {:?}", stringify!($err), err)
|
||||
}
|
||||
err
|
||||
}};
|
||||
($py:expr, $val:ident, $code:expr, $err:ident, $err_msg:expr) => {{
|
||||
let err = py_expect_exception!($py, $val, $code, $err);
|
||||
assert_eq!(
|
||||
err.instance($py)
|
||||
.str()
|
||||
.expect("error str() failed")
|
||||
.to_str()
|
||||
.expect("message was not valid utf8"),
|
||||
$err_msg
|
||||
);
|
||||
// Case3: idents & err_msg
|
||||
($py:expr, $($val:ident)+, $code:expr, $err:ident, $err_msg:literal) => {{
|
||||
let err = py_expect_exception!($py, $($val)+, $code, $err);
|
||||
// Suppose that the error message looks like 'TypeError: ~'
|
||||
assert_eq!(format!("Py{}", err), concat!(stringify!($err), ": ", $err_msg));
|
||||
err
|
||||
}};
|
||||
// Case4: dict & 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
|
||||
}};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue