port `PyErr::warn` to `Bound` API (#3842)

* port `PyErr::new_type`

* port `PyErr::warn` and `PyErr::warn_explicit`
This commit is contained in:
Icxolu 2024-02-16 01:12:43 +01:00 committed by GitHub
parent dc8b948201
commit 05aedc9032
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 106 additions and 27 deletions

View File

@ -52,7 +52,9 @@ use crate::types::{
};
#[cfg(Py_LIMITED_API)]
use crate::{intern, DowncastError};
use crate::{Bound, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject};
use crate::{
Bound, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python, ToPyObject,
};
use chrono::offset::{FixedOffset, Utc};
use chrono::{
DateTime, Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime, Offset, TimeZone, Timelike,
@ -457,9 +459,9 @@ fn naive_datetime_to_py_datetime(
fn warn_truncated_leap_second(obj: &Bound<'_, PyAny>) {
let py = obj.py();
if let Err(e) = PyErr::warn(
if let Err(e) = PyErr::warn_bound(
py,
py.get_type::<PyUserWarning>(),
&py.get_type::<PyUserWarning>().as_borrowed(),
"ignored leap-second, `datetime` does not support leap-seconds",
0,
) {

View File

@ -445,6 +445,30 @@ impl PyErr {
}
}
/// Deprecated form of [`PyErr::new_type_bound`]
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::new_type` will be replaced by `PyErr::new_type_bound` in a future PyO3 version"
)
)]
pub fn new_type(
py: Python<'_>,
name: &str,
doc: Option<&str>,
base: Option<&PyType>,
dict: Option<PyObject>,
) -> PyResult<Py<PyType>> {
Self::new_type_bound(
py,
name,
doc,
base.map(PyNativeType::as_borrowed).as_deref(),
dict,
)
}
/// Creates a new exception type with the given name and docstring.
///
/// - `base` can be an existing exception type to subclass, or a tuple of classes.
@ -459,11 +483,11 @@ impl PyErr {
/// # Panics
///
/// This function will panic if `name` or `doc` cannot be converted to [`CString`]s.
pub fn new_type(
py: Python<'_>,
pub fn new_type_bound<'py>(
py: Python<'py>,
name: &str,
doc: Option<&str>,
base: Option<&PyType>,
base: Option<&Bound<'py, PyType>>,
dict: Option<PyObject>,
) -> PyResult<Py<PyType>> {
let base: *mut ffi::PyObject = match base {
@ -635,6 +659,18 @@ impl PyErr {
unsafe { ffi::PyErr_WriteUnraisable(obj.map_or(std::ptr::null_mut(), Bound::as_ptr)) }
}
/// Deprecated form of [`PyErr::warn_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::warn` will be replaced by `PyErr::warn_bound` in a future PyO3 version"
)
)]
pub fn warn(py: Python<'_>, category: &PyAny, message: &str, stacklevel: i32) -> PyResult<()> {
Self::warn_bound(py, &category.as_borrowed(), message, stacklevel)
}
/// Issues a warning message.
///
/// May return an `Err(PyErr)` if warnings-as-errors is enabled.
@ -650,13 +686,18 @@ impl PyErr {
/// # use pyo3::prelude::*;
/// # fn main() -> PyResult<()> {
/// Python::with_gil(|py| {
/// let user_warning = py.get_type::<pyo3::exceptions::PyUserWarning>();
/// PyErr::warn(py, user_warning, "I am warning you", 0)?;
/// let user_warning = py.get_type::<pyo3::exceptions::PyUserWarning>().as_borrowed();
/// PyErr::warn_bound(py, &user_warning, "I am warning you", 0)?;
/// Ok(())
/// })
/// # }
/// ```
pub fn warn(py: Python<'_>, category: &PyAny, message: &str, stacklevel: i32) -> PyResult<()> {
pub fn warn_bound<'py>(
py: Python<'py>,
category: &Bound<'py, PyAny>,
message: &str,
stacklevel: i32,
) -> PyResult<()> {
let message = CString::new(message)?;
error_on_minusone(py, unsafe {
ffi::PyErr_WarnEx(
@ -667,14 +708,14 @@ 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.
/// Deprecated form of [`PyErr::warn_explicit_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::warn_explicit` will be replaced by `PyErr::warn_explicit_bound` in a future PyO3 version"
)
)]
pub fn warn_explicit(
py: Python<'_>,
category: &PyAny,
@ -683,6 +724,34 @@ impl PyErr {
lineno: i32,
module: Option<&str>,
registry: Option<&PyAny>,
) -> PyResult<()> {
Self::warn_explicit_bound(
py,
&category.as_borrowed(),
message,
filename,
lineno,
module,
registry.map(PyNativeType::as_borrowed).as_deref(),
)
}
/// 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_bound<'py>(
py: Python<'py>,
category: &Bound<'py, PyAny>,
message: &str,
filename: &str,
lineno: i32,
module: Option<&str>,
registry: Option<&Bound<'py, PyAny>>,
) -> PyResult<()> {
let message = CString::new(message)?;
let filename = CString::new(filename)?;
@ -975,7 +1044,7 @@ mod tests {
use super::PyErrState;
use crate::exceptions::{self, PyTypeError, PyValueError};
use crate::types::any::PyAnyMethods;
use crate::{PyErr, PyTypeInfo, Python};
use crate::{PyErr, PyNativeType, PyTypeInfo, Python};
#[test]
fn no_error() {
@ -1172,7 +1241,7 @@ mod tests {
// GIL locked should prevent effects to be visible to other testing
// threads.
Python::with_gil(|py| {
let cls = py.get_type::<exceptions::PyUserWarning>();
let cls = py.get_type::<exceptions::PyUserWarning>().as_borrowed();
// Reset warning filter to default state
let warnings = py.import_bound("warnings").unwrap();
@ -1181,7 +1250,7 @@ mod tests {
// First, test the warning is emitted
assert_warnings!(
py,
{ PyErr::warn(py, cls, "I am warning you", 0).unwrap() },
{ PyErr::warn_bound(py, &cls, "I am warning you", 0).unwrap() },
[(exceptions::PyUserWarning, "I am warning you")]
);
@ -1189,7 +1258,7 @@ mod tests {
warnings
.call_method1("simplefilter", ("error", cls))
.unwrap();
PyErr::warn(py, cls, "I am warning you", 0).unwrap_err();
PyErr::warn_bound(py, &cls, "I am warning you", 0).unwrap_err();
// Test with error for an explicit module
warnings.call_method0("resetwarnings").unwrap();
@ -1200,13 +1269,20 @@ mod tests {
// This has the wrong module and will not raise, just be emitted
assert_warnings!(
py,
{ PyErr::warn(py, cls, "I am warning you", 0).unwrap() },
{ PyErr::warn_bound(py, &cls, "I am warning you", 0).unwrap() },
[(exceptions::PyUserWarning, "I am warning you")]
);
let err =
PyErr::warn_explicit(py, cls, "I am warning you", "pyo3test.py", 427, None, None)
.unwrap_err();
let err = PyErr::warn_explicit_bound(
py,
&cls,
"I am warning you",
"pyo3test.py",
427,
None,
None,
)
.unwrap_err();
assert!(err
.value(py)
.getattr("args")

View File

@ -240,16 +240,17 @@ macro_rules! create_exception_type_object {
impl $name {
fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
use $crate::sync::GILOnceCell;
use $crate::PyNativeType;
static TYPE_OBJECT: GILOnceCell<$crate::Py<$crate::types::PyType>> =
GILOnceCell::new();
TYPE_OBJECT
.get_or_init(py, ||
$crate::PyErr::new_type(
$crate::PyErr::new_type_bound(
py,
concat!(stringify!($module), ".", stringify!($name)),
$doc,
::std::option::Option::Some(py.get_type::<$base>()),
::std::option::Option::Some(&py.get_type::<$base>().as_borrowed()),
::std::option::Option::None,
).expect("Failed to initialize new exception type.")
).as_ptr() as *mut $crate::ffi::PyTypeObject