add assert_warnings test helper
This commit is contained in:
parent
aeb7a958dc
commit
c8f82be32c
|
@ -822,6 +822,7 @@ impl_signed_integer!(isize);
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::PyErrState;
|
use super::PyErrState;
|
||||||
use crate::exceptions::{self, PyTypeError, PyValueError};
|
use crate::exceptions::{self, PyTypeError, PyValueError};
|
||||||
|
use crate::tests::common::CatchWarnings;
|
||||||
use crate::{PyErr, PyTypeInfo, Python};
|
use crate::{PyErr, PyTypeInfo, Python};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1019,11 +1020,12 @@ mod tests {
|
||||||
let warnings = py.import("warnings").unwrap();
|
let warnings = py.import("warnings").unwrap();
|
||||||
warnings.call_method0("resetwarnings").unwrap();
|
warnings.call_method0("resetwarnings").unwrap();
|
||||||
|
|
||||||
// First, test with ignoring the warning
|
// First, test the warning is emitted
|
||||||
warnings
|
assert_warnings!(
|
||||||
.call_method1("simplefilter", ("ignore", cls))
|
py,
|
||||||
.unwrap();
|
{ PyErr::warn(py, cls, "I am warning you", 0).unwrap() },
|
||||||
PyErr::warn(py, cls, "I am warning you", 0).unwrap();
|
[(exceptions::PyUserWarning, "I am warning you")]
|
||||||
|
);
|
||||||
|
|
||||||
// Test with raising
|
// Test with raising
|
||||||
warnings
|
warnings
|
||||||
|
@ -1031,17 +1033,18 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
PyErr::warn(py, cls, "I am warning you", 0).unwrap_err();
|
PyErr::warn(py, cls, "I am warning you", 0).unwrap_err();
|
||||||
|
|
||||||
// Test with explicit module and specific filter
|
// Test with error for an explicit module
|
||||||
warnings.call_method0("resetwarnings").unwrap();
|
warnings.call_method0("resetwarnings").unwrap();
|
||||||
warnings
|
|
||||||
.call_method1("simplefilter", ("ignore", cls))
|
|
||||||
.unwrap();
|
|
||||||
warnings
|
warnings
|
||||||
.call_method1("filterwarnings", ("error", "", cls, "pyo3test"))
|
.call_method1("filterwarnings", ("error", "", cls, "pyo3test"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// This has the wrong module and will not raise
|
// This has the wrong module and will not raise, just be emitted
|
||||||
PyErr::warn(py, cls, "I am warning you", 0).unwrap();
|
assert_warnings!(
|
||||||
|
py,
|
||||||
|
{ PyErr::warn(py, cls, "I am warning you", 0).unwrap() },
|
||||||
|
[(exceptions::PyUserWarning, "I am warning you")]
|
||||||
|
);
|
||||||
|
|
||||||
let err =
|
let err =
|
||||||
PyErr::warn_explicit(py, cls, "I am warning you", "pyo3test.py", 427, None, None)
|
PyErr::warn_explicit(py, cls, "I am warning you", "pyo3test.py", 427, None, None)
|
||||||
|
|
11
src/lib.rs
11
src/lib.rs
|
@ -386,6 +386,12 @@ pub use {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use inventory; // Re-exported for `#[pyclass]` and `#[pymethods]` with `multiple-pymethods`.
|
pub use inventory; // Re-exported for `#[pyclass]` and `#[pymethods]` with `multiple-pymethods`.
|
||||||
|
|
||||||
|
/// Tests and helpers which reside inside PyO3's main library. Declared first so that macros
|
||||||
|
/// are available in unit tests.
|
||||||
|
#[cfg(test)]
|
||||||
|
#[macro_use]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod internal_tricks;
|
mod internal_tricks;
|
||||||
|
|
||||||
|
@ -447,11 +453,6 @@ pub use pyo3_macros::pyclass;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
/// Test macro hygiene - this is in the crate since we won't have
|
|
||||||
/// `pyo3` available in the crate root.
|
|
||||||
#[cfg(all(test, feature = "macros"))]
|
|
||||||
mod test_hygiene;
|
|
||||||
|
|
||||||
#[cfg(feature = "experimental-inspect")]
|
#[cfg(feature = "experimental-inspect")]
|
||||||
pub mod inspect;
|
pub mod inspect;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
#[macro_use]
|
||||||
|
pub(crate) mod common {
|
||||||
|
use crate as pyo3;
|
||||||
|
include!("../../tests/common.rs");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test macro hygiene - this is in the crate since we won't have
|
||||||
|
/// `pyo3` available in the crate root.
|
||||||
|
#[cfg(all(test, feature = "macros"))]
|
||||||
|
mod hygiene;
|
|
@ -1,8 +1,18 @@
|
||||||
//! Some common macros for tests
|
// the inner mod enables the #![allow(dead_code)] to
|
||||||
|
// be applied - `test_utils.rs` uses `include!` to pull in this file
|
||||||
|
|
||||||
|
/// Common macros and helpers for tests
|
||||||
|
#[allow(dead_code)] // many tests do not use the complete set of functionality offered here
|
||||||
|
#[macro_use]
|
||||||
|
mod inner {
|
||||||
|
|
||||||
|
#[allow(unused_imports)] // pulls in `use crate as pyo3` in `test_utils.rs`
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[cfg(all(feature = "macros", Py_3_8))]
|
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
use pyo3::types::{IntoPyDict, PyList};
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! py_assert {
|
macro_rules! py_assert {
|
||||||
($py:expr, $($val:ident)+, $assertion:literal) => {
|
($py:expr, $($val:ident)+, $assertion:literal) => {
|
||||||
|
@ -47,14 +57,14 @@ macro_rules! py_expect_exception {
|
||||||
|
|
||||||
// sys.unraisablehook not available until Python 3.8
|
// sys.unraisablehook not available until Python 3.8
|
||||||
#[cfg(all(feature = "macros", Py_3_8))]
|
#[cfg(all(feature = "macros", Py_3_8))]
|
||||||
#[pyclass]
|
#[pyclass(crate = "pyo3")]
|
||||||
pub struct UnraisableCapture {
|
pub struct UnraisableCapture {
|
||||||
pub capture: Option<(PyErr, PyObject)>,
|
pub capture: Option<(PyErr, PyObject)>,
|
||||||
old_hook: Option<PyObject>,
|
old_hook: Option<PyObject>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "macros", Py_3_8))]
|
#[cfg(all(feature = "macros", Py_3_8))]
|
||||||
#[pymethods]
|
#[pymethods(crate = "pyo3")]
|
||||||
impl UnraisableCapture {
|
impl UnraisableCapture {
|
||||||
pub fn hook(&mut self, unraisable: &PyAny) {
|
pub fn hook(&mut self, unraisable: &PyAny) {
|
||||||
let err = PyErr::from_value(unraisable.getattr("exc_value").unwrap());
|
let err = PyErr::from_value(unraisable.getattr("exc_value").unwrap());
|
||||||
|
@ -91,3 +101,52 @@ impl UnraisableCapture {
|
||||||
sys.setattr("unraisablehook", old_hook).unwrap();
|
sys.setattr("unraisablehook", old_hook).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct CatchWarnings<'py> {
|
||||||
|
catch_warnings: &'py PyAny,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'py> CatchWarnings<'py> {
|
||||||
|
pub fn enter<R>(py: Python<'py>, f: impl FnOnce(&PyList) -> PyResult<R>) -> PyResult<R> {
|
||||||
|
let warnings = py.import("warnings")?;
|
||||||
|
let kwargs = [("record", true)].into_py_dict(py);
|
||||||
|
let catch_warnings = warnings.getattr("catch_warnings")?.call((), Some(kwargs))?;
|
||||||
|
let list = catch_warnings.call_method0("__enter__")?.extract()?;
|
||||||
|
let _guard = Self { catch_warnings };
|
||||||
|
f(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for CatchWarnings<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let py = self.catch_warnings.py();
|
||||||
|
self.catch_warnings
|
||||||
|
.call_method1("__exit__", (py.None(), py.None(), py.None()))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! assert_warnings {
|
||||||
|
($py:expr, $body:expr, [$(($category:ty, $message:literal)),+] $(,)? ) => {{
|
||||||
|
CatchWarnings::enter($py, |w| {
|
||||||
|
$body;
|
||||||
|
let expected_warnings = [$((<$category>::type_object($py), $message)),+];
|
||||||
|
assert_eq!(w.len(), expected_warnings.len());
|
||||||
|
for (warning, (category, message)) in w.iter().zip(expected_warnings) {
|
||||||
|
|
||||||
|
assert!(warning.getattr("category").unwrap().is(category));
|
||||||
|
assert_eq!(
|
||||||
|
warning.getattr("message").unwrap().str().unwrap().to_string_lossy(),
|
||||||
|
message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use inner::*;
|
||||||
|
|
Loading…
Reference in New Issue