Expose py_run macro
This commit is contained in:
parent
f2a7c64365
commit
0f9a3b1194
|
@ -25,11 +25,11 @@ num-traits = "0.2.6"
|
|||
pyo3cls = { path = "pyo3cls", version = "=0.7.0" }
|
||||
mashup = "0.1.9"
|
||||
num-complex = { version = "0.2.1", optional = true }
|
||||
indoc = "0.3.3"
|
||||
inventory = "0.1.3"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_approx_eq = "1.1.0"
|
||||
indoc = "0.3.3"
|
||||
trybuild = "1.0"
|
||||
|
||||
[build-dependencies]
|
||||
|
|
93
src/lib.rs
93
src/lib.rs
|
@ -133,6 +133,9 @@ pub use crate::type_object::{PyObjectAlloc, PyRawObject, PyTypeInfo};
|
|||
// Re-exported for wrap_function
|
||||
#[doc(hidden)]
|
||||
pub use mashup;
|
||||
// Re-exported for py_run
|
||||
#[doc(hidden)]
|
||||
pub use indoc;
|
||||
// Re-exported for pymethods
|
||||
#[doc(hidden)]
|
||||
pub use inventory;
|
||||
|
@ -210,6 +213,96 @@ macro_rules! wrap_pymodule {
|
|||
}};
|
||||
}
|
||||
|
||||
/// A convinient macro to execute a Python code snippet, with some local variables set.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use pyo3::{prelude::*, py_run, types::PyList};
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// let py = gil.python();
|
||||
/// let obj = pyo3::types::PyDict::new(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.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use pyo3::{prelude::*, PyRawObject, py_run};
|
||||
/// #[pyclass]
|
||||
/// struct Time {
|
||||
/// hour: u32,
|
||||
/// minute: u32,
|
||||
/// second: u32,
|
||||
/// }
|
||||
/// #[pymethods]
|
||||
/// impl Time {
|
||||
/// fn repl_japanese(&self) -> String {
|
||||
/// format!("{}時{}分{}秒", self.hour, self.minute, self.second)
|
||||
/// }
|
||||
/// #[getter]
|
||||
/// fn hour(&self) -> PyResult<u32> {
|
||||
/// Ok(self.hour)
|
||||
/// }
|
||||
/// }
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// let py = gil.python();
|
||||
/// let time = PyRef::new(py, Time {hour: 8, minute: 43, second: 16}).unwrap();
|
||||
/// py_run!(py, time, r#"
|
||||
/// assert time.hour == 8
|
||||
/// assert time.repl_japanese() == "8時43分16秒"
|
||||
/// "#);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! py_run {
|
||||
($py:expr, $($val:ident )+, $code:literal) => {{
|
||||
pyo3::py_run_impl!($py, $($val)+, pyo3::indoc::indoc!($code))
|
||||
}};
|
||||
($py:expr, $($val:ident )+, $code:expr) => {{
|
||||
pyo3::py_run_impl!($py, $($val)+, &pyo3::_indoc_runtime($code))
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! py_run_impl {
|
||||
($py:expr, $($val:ident )+, $code:expr) => {{
|
||||
use pyo3::types::IntoPyDict;
|
||||
let d = [$((stringify!($val), &$val))*].into_py_dict($py);
|
||||
|
||||
$py.run($code, None, Some(d))
|
||||
.map_err(|e| {
|
||||
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
|
||||
// panic before flushing. This is where this hack comes into place
|
||||
$py.run("import sys; sys.stderr.flush()", None, None)
|
||||
.unwrap();
|
||||
})
|
||||
.expect($code)
|
||||
}};
|
||||
}
|
||||
|
||||
/// Removes indentation from multiline strings in pyrun commands
|
||||
#[doc(hidden)]
|
||||
pub fn _indoc_runtime(commands: &str) -> String {
|
||||
let indent;
|
||||
if let Some(second) = commands.lines().nth(1) {
|
||||
indent = second
|
||||
.chars()
|
||||
.take_while(char::is_ascii_whitespace)
|
||||
.collect::<String>();
|
||||
} else {
|
||||
indent = "".to_string();
|
||||
}
|
||||
|
||||
commands
|
||||
.trim_end()
|
||||
.replace(&("\n".to_string() + &indent), "\n")
|
||||
+ "\n"
|
||||
}
|
||||
|
||||
/// Test readme and user guide
|
||||
#[doc(hidden)]
|
||||
pub mod doc_test {
|
||||
|
|
|
@ -2,48 +2,10 @@
|
|||
//! - 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
|
||||
|
||||
/// Removes indentation from multiline strings in pyrun commands
|
||||
#[allow(unused)] // macro scoping is fooling the compiler
|
||||
pub fn indoc(commands: &str) -> String {
|
||||
let indent;
|
||||
if let Some(second) = commands.lines().nth(1) {
|
||||
indent = second
|
||||
.chars()
|
||||
.take_while(char::is_ascii_whitespace)
|
||||
.collect::<String>();
|
||||
} else {
|
||||
indent = "".to_string();
|
||||
}
|
||||
|
||||
commands
|
||||
.trim_end()
|
||||
.replace(&("\n".to_string() + &indent), "\n")
|
||||
+ "\n"
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! py_run {
|
||||
($py:expr, $val:expr, $code:expr) => {{
|
||||
use pyo3::types::IntoPyDict;
|
||||
let d = [(stringify!($val), &$val)].into_py_dict($py);
|
||||
|
||||
$py.run(&common::indoc($code), None, Some(d))
|
||||
.map_err(|e| {
|
||||
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
|
||||
// panic before flushing. This is where this hack comes into place
|
||||
$py.run("import sys; sys.stderr.flush()", None, None)
|
||||
.unwrap();
|
||||
})
|
||||
.expect(&common::indoc($code))
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! py_assert {
|
||||
($py:expr, $val:ident, $assertion:expr) => {
|
||||
py_run!($py, $val, concat!("assert ", $assertion))
|
||||
pyo3::py_run!($py, $val, concat!("assert ", $assertion))
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use pyo3::class::basic::CompareOp;
|
||||
use pyo3::class::*;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::py_run;
|
||||
use pyo3::types::PyAny;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
#[pyclass]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::py_run;
|
||||
use pyo3::type_object::initialize_type;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
#[pyclass]
|
||||
|
|
|
@ -6,11 +6,11 @@ use pyo3::class::{
|
|||
use pyo3::exceptions::{IndexError, ValueError};
|
||||
use pyo3::ffi;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::py_run;
|
||||
use pyo3::types::{IntoPyDict, PyAny, PyBytes, PySlice, PyType};
|
||||
use pyo3::AsPyPointer;
|
||||
use std::{isize, iter};
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
#[pyclass]
|
||||
|
|
|
@ -3,6 +3,7 @@ use pyo3::class::PyTraverseError;
|
|||
use pyo3::class::PyVisit;
|
||||
use pyo3::ffi;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::py_run;
|
||||
use pyo3::types::PyAny;
|
||||
use pyo3::types::PyTuple;
|
||||
use pyo3::AsPyPointer;
|
||||
|
@ -11,7 +12,6 @@ use std::cell::RefCell;
|
|||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
#[pyclass(freelist = 2)]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::py_run;
|
||||
use pyo3::types::IntoPyDict;
|
||||
use std::isize;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
#[pyclass]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::py_run;
|
||||
use pyo3::types::IntoPyDict;
|
||||
use std::isize;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
#[pyclass]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::py_run;
|
||||
use pyo3::types::{IntoPyDict, PyDict, PyList, PySet, PyString, PyTuple, PyType};
|
||||
use pyo3::PyRawObject;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
#[pyclass]
|
||||
|
|
|
@ -2,7 +2,6 @@ use pyo3::prelude::*;
|
|||
|
||||
use pyo3::types::IntoPyDict;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
#[pyclass]
|
||||
|
|
|
@ -5,7 +5,6 @@ use pyo3::types::{PyBytes, PyString};
|
|||
use pyo3::PyIterProtocol;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
/// Assumes it's a file reader or so.
|
||||
|
|
|
@ -6,9 +6,6 @@ use pyo3::types::IntoPyDict;
|
|||
use pyo3::types::PyAny;
|
||||
use pyo3::types::PyList;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
#[pyclass]
|
||||
struct ByteSequence {
|
||||
elements: Vec<u8>,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyDict, PyTuple};
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
#[pyclass]
|
||||
|
|
|
@ -2,10 +2,9 @@ use pyo3::prelude::*;
|
|||
use pyo3::type_object::initialize_type;
|
||||
use pyo3::types::IntoPyDict;
|
||||
use pyo3::types::{PyDict, PyTuple};
|
||||
use pyo3::wrap_pyfunction;
|
||||
use pyo3::{py_run, wrap_pyfunction};
|
||||
use std::isize;
|
||||
|
||||
#[macro_use]
|
||||
mod common;
|
||||
|
||||
#[pyclass]
|
||||
|
|
Loading…
Reference in New Issue