Export warning classes and add PyErr::warn_explicit() (#2742)
This commit is contained in:
parent
f5735614fb
commit
1d20f2a531
1
newsfragments/2742.added.md
Normal file
1
newsfragments/2742.added.md
Normal file
|
@ -0,0 +1 @@
|
|||
Added exports for all built-in `Warning` classes as well as `PyErr::warn_explicit`.
|
|
@ -245,6 +245,8 @@ extern "C" {
|
|||
pub static mut PyExc_BytesWarning: *mut PyObject;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyExc_ResourceWarning")]
|
||||
pub static mut PyExc_ResourceWarning: *mut PyObject;
|
||||
#[cfg(Py_3_10)]
|
||||
pub static mut PyExc_EncodingWarning: *mut PyObject;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
|
|
@ -22,6 +22,7 @@ extern "C" {
|
|||
format: *const c_char,
|
||||
...
|
||||
) -> c_int;
|
||||
#[cfg_attr(PyPy, link_name = "PyPyErr_WarnExplicit")]
|
||||
pub fn PyErr_WarnExplicit(
|
||||
category: *mut PyObject,
|
||||
message: *const c_char,
|
||||
|
|
121
src/err/mod.rs
121
src/err/mod.rs
|
@ -477,7 +477,28 @@ impl PyErr {
|
|||
}
|
||||
|
||||
/// Issues a warning message.
|
||||
/// May return a `PyErr` if warnings-as-errors is enabled.
|
||||
///
|
||||
/// May return an `Err(PyErr)` if warnings-as-errors is enabled.
|
||||
///
|
||||
/// Equivalent to `warnings.warn()` in Python.
|
||||
///
|
||||
/// The `category` should be one of the `Warning` classes available in
|
||||
/// [`pyo3::exceptions`](crate::exceptions), or a subclass. The Python
|
||||
/// object can be retrieved using [`PyTypeInfo::type_object()`].
|
||||
///
|
||||
/// Example:
|
||||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::PyTypeInfo;
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| {
|
||||
/// let user_warning = pyo3::exceptions::PyUserWarning::type_object(py);
|
||||
/// PyErr::warn(py, user_warning, "I am warning you", 0)?;
|
||||
/// Ok(())
|
||||
/// })
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn warn(py: Python<'_>, category: &PyAny, message: &str, stacklevel: i32) -> PyResult<()> {
|
||||
let message = CString::new(message)?;
|
||||
unsafe {
|
||||
|
@ -492,6 +513,49 @@ impl PyErr {
|
|||
}
|
||||
}
|
||||
|
||||
/// Issues a warning message, with more control over the warning attributes.
|
||||
///
|
||||
/// May return a `PyErr` if warnings-as-errors is enabled.
|
||||
///
|
||||
/// Equivalent to `warnings.warn_explicit()` in Python.
|
||||
///
|
||||
/// The `category` should be one of the `Warning` classes available in
|
||||
/// [`pyo3::exceptions`](crate::exceptions), or a subclass.
|
||||
pub fn warn_explicit(
|
||||
py: Python<'_>,
|
||||
category: &PyAny,
|
||||
message: &str,
|
||||
filename: &str,
|
||||
lineno: i32,
|
||||
module: Option<&str>,
|
||||
registry: Option<&PyAny>,
|
||||
) -> PyResult<()> {
|
||||
let message = CString::new(message)?;
|
||||
let filename = CString::new(filename)?;
|
||||
let module = module.map(CString::new).transpose()?;
|
||||
let module_ptr = match module {
|
||||
None => std::ptr::null_mut(),
|
||||
Some(s) => s.as_ptr(),
|
||||
};
|
||||
let registry: *mut ffi::PyObject = match registry {
|
||||
None => std::ptr::null_mut(),
|
||||
Some(obj) => obj.as_ptr(),
|
||||
};
|
||||
unsafe {
|
||||
error_on_minusone(
|
||||
py,
|
||||
ffi::PyErr_WarnExplicit(
|
||||
category.as_ptr(),
|
||||
message.as_ptr(),
|
||||
filename.as_ptr(),
|
||||
lineno,
|
||||
module_ptr,
|
||||
registry,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Clone the PyErr. This requires the GIL, which is why PyErr does not implement Clone.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -769,7 +833,7 @@ fn exceptions_must_derive_from_base_exception(py: Python<'_>) -> PyErr {
|
|||
mod tests {
|
||||
use super::PyErrState;
|
||||
use crate::exceptions;
|
||||
use crate::{AsPyPointer, PyErr, Python};
|
||||
use crate::{AsPyPointer, PyErr, PyTypeInfo, Python};
|
||||
|
||||
#[test]
|
||||
fn no_error() {
|
||||
|
@ -938,4 +1002,57 @@ mod tests {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn warnings() {
|
||||
// Note: although the warning filter is interpreter global, keeping the
|
||||
// GIL locked should prevent effects to be visible to other testing
|
||||
// threads.
|
||||
Python::with_gil(|py| {
|
||||
let cls = exceptions::PyUserWarning::type_object(py);
|
||||
|
||||
// Reset warning filter to default state
|
||||
let warnings = py.import("warnings").unwrap();
|
||||
warnings.call_method0("resetwarnings").unwrap();
|
||||
|
||||
// First, test with ignoring the warning
|
||||
warnings
|
||||
.call_method1("simplefilter", ("ignore", cls))
|
||||
.unwrap();
|
||||
PyErr::warn(py, cls, "I am warning you", 0).unwrap();
|
||||
|
||||
// Test with raising
|
||||
warnings
|
||||
.call_method1("simplefilter", ("error", cls))
|
||||
.unwrap();
|
||||
PyErr::warn(py, cls, "I am warning you", 0).unwrap_err();
|
||||
|
||||
// Test with explicit module and specific filter
|
||||
warnings.call_method0("resetwarnings").unwrap();
|
||||
warnings
|
||||
.call_method1("simplefilter", ("ignore", cls))
|
||||
.unwrap();
|
||||
warnings
|
||||
.call_method1("filterwarnings", ("error", "", cls, "pyo3test"))
|
||||
.unwrap();
|
||||
|
||||
// This has the wrong module and will not raise
|
||||
PyErr::warn(py, cls, "I am warning you", 0).unwrap();
|
||||
|
||||
let err =
|
||||
PyErr::warn_explicit(py, cls, "I am warning you", "pyo3test.py", 427, None, None)
|
||||
.unwrap_err();
|
||||
assert!(err
|
||||
.value(py)
|
||||
.getattr("args")
|
||||
.unwrap()
|
||||
.get_item(0)
|
||||
.unwrap()
|
||||
.eq("I am warning you")
|
||||
.unwrap());
|
||||
|
||||
// Finally, reset filter again
|
||||
warnings.call_method0("resetwarnings").unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||
|
||||
//! Exception types defined by Python.
|
||||
//! Exception and warning types defined by Python.
|
||||
//!
|
||||
//! The structs in this module represent Python's built-in exceptions, while the modules comprise
|
||||
//! structs representing errors defined in Python code.
|
||||
//! The structs in this module represent Python's built-in exceptions and
|
||||
//! warnings, while the modules comprise structs representing errors defined in
|
||||
//! Python code.
|
||||
//!
|
||||
//! The latter are created with the [`import_exception`](crate::import_exception) macro, which you
|
||||
//! can use yourself to import Python exceptions.
|
||||
//! The latter are created with the
|
||||
//! [`import_exception`](crate::import_exception) macro, which you can use
|
||||
//! yourself to import Python classes that are ultimately derived from
|
||||
//! `BaseException`.
|
||||
|
||||
use crate::{ffi, PyResult, Python};
|
||||
use std::ffi::CStr;
|
||||
|
@ -660,6 +663,61 @@ impl PyUnicodeDecodeError {
|
|||
}
|
||||
}
|
||||
|
||||
impl_native_exception!(PyWarning, PyExc_Warning, native_doc!("Warning"));
|
||||
impl_native_exception!(PyUserWarning, PyExc_UserWarning, native_doc!("UserWarning"));
|
||||
impl_native_exception!(
|
||||
PyDeprecationWarning,
|
||||
PyExc_DeprecationWarning,
|
||||
native_doc!("DeprecationWarning")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyPendingDeprecationWarning,
|
||||
PyExc_PendingDeprecationWarning,
|
||||
native_doc!("PendingDeprecationWarning")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PySyntaxWarning,
|
||||
PyExc_SyntaxWarning,
|
||||
native_doc!("SyntaxWarning")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyRuntimeWarning,
|
||||
PyExc_RuntimeWarning,
|
||||
native_doc!("RuntimeWarning")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyFutureWarning,
|
||||
PyExc_FutureWarning,
|
||||
native_doc!("FutureWarning")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyImportWarning,
|
||||
PyExc_ImportWarning,
|
||||
native_doc!("ImportWarning")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyUnicodeWarning,
|
||||
PyExc_UnicodeWarning,
|
||||
native_doc!("UnicodeWarning")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyBytesWarning,
|
||||
PyExc_BytesWarning,
|
||||
native_doc!("BytesWarning")
|
||||
);
|
||||
impl_native_exception!(
|
||||
PyResourceWarning,
|
||||
PyExc_ResourceWarning,
|
||||
native_doc!("ResourceWarning")
|
||||
);
|
||||
|
||||
#[cfg(Py_3_10)]
|
||||
impl_native_exception!(
|
||||
PyEncodingWarning,
|
||||
PyExc_EncodingWarning,
|
||||
native_doc!("EncodingWarning")
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! test_exception {
|
||||
($exc_ty:ident $(, $constructor:expr)?) => {
|
||||
|
@ -1017,4 +1075,17 @@ mod tests {
|
|||
test_exception!(PyIOError);
|
||||
#[cfg(windows)]
|
||||
test_exception!(PyWindowsError);
|
||||
|
||||
test_exception!(PyWarning);
|
||||
test_exception!(PyUserWarning);
|
||||
test_exception!(PyDeprecationWarning);
|
||||
test_exception!(PyPendingDeprecationWarning);
|
||||
test_exception!(PySyntaxWarning);
|
||||
test_exception!(PyRuntimeWarning);
|
||||
test_exception!(PyFutureWarning);
|
||||
test_exception!(PyImportWarning);
|
||||
test_exception!(PyUnicodeWarning);
|
||||
test_exception!(PyBytesWarning);
|
||||
#[cfg(Py_3_10)]
|
||||
test_exception!(PyEncodingWarning);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue