Skip to content

Commit 05aedc9

Browse files
authored
port PyErr::warn to Bound API (#3842)
* port `PyErr::new_type` * port `PyErr::warn` and `PyErr::warn_explicit`
1 parent dc8b948 commit 05aedc9

File tree

3 files changed

+102
-23
lines changed

3 files changed

+102
-23
lines changed

src/conversions/chrono.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ use crate::types::{
5252
};
5353
#[cfg(Py_LIMITED_API)]
5454
use crate::{intern, DowncastError};
55-
use crate::{Bound, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject};
55+
use crate::{
56+
Bound, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python, ToPyObject,
57+
};
5658
use chrono::offset::{FixedOffset, Utc};
5759
use chrono::{
5860
DateTime, Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime, Offset, TimeZone, Timelike,
@@ -457,9 +459,9 @@ fn naive_datetime_to_py_datetime(
457459

458460
fn warn_truncated_leap_second(obj: &Bound<'_, PyAny>) {
459461
let py = obj.py();
460-
if let Err(e) = PyErr::warn(
462+
if let Err(e) = PyErr::warn_bound(
461463
py,
462-
py.get_type::<PyUserWarning>(),
464+
&py.get_type::<PyUserWarning>().as_borrowed(),
463465
"ignored leap-second, `datetime` does not support leap-seconds",
464466
0,
465467
) {

src/err/mod.rs

Lines changed: 94 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,30 @@ impl PyErr {
445445
}
446446
}
447447

448+
/// Deprecated form of [`PyErr::new_type_bound`]
449+
#[cfg_attr(
450+
not(feature = "gil-refs"),
451+
deprecated(
452+
since = "0.21.0",
453+
note = "`PyErr::new_type` will be replaced by `PyErr::new_type_bound` in a future PyO3 version"
454+
)
455+
)]
456+
pub fn new_type(
457+
py: Python<'_>,
458+
name: &str,
459+
doc: Option<&str>,
460+
base: Option<&PyType>,
461+
dict: Option<PyObject>,
462+
) -> PyResult<Py<PyType>> {
463+
Self::new_type_bound(
464+
py,
465+
name,
466+
doc,
467+
base.map(PyNativeType::as_borrowed).as_deref(),
468+
dict,
469+
)
470+
}
471+
448472
/// Creates a new exception type with the given name and docstring.
449473
///
450474
/// - `base` can be an existing exception type to subclass, or a tuple of classes.
@@ -459,11 +483,11 @@ impl PyErr {
459483
/// # Panics
460484
///
461485
/// This function will panic if `name` or `doc` cannot be converted to [`CString`]s.
462-
pub fn new_type(
463-
py: Python<'_>,
486+
pub fn new_type_bound<'py>(
487+
py: Python<'py>,
464488
name: &str,
465489
doc: Option<&str>,
466-
base: Option<&PyType>,
490+
base: Option<&Bound<'py, PyType>>,
467491
dict: Option<PyObject>,
468492
) -> PyResult<Py<PyType>> {
469493
let base: *mut ffi::PyObject = match base {
@@ -635,6 +659,18 @@ impl PyErr {
635659
unsafe { ffi::PyErr_WriteUnraisable(obj.map_or(std::ptr::null_mut(), Bound::as_ptr)) }
636660
}
637661

662+
/// Deprecated form of [`PyErr::warn_bound`].
663+
#[cfg_attr(
664+
not(feature = "gil-refs"),
665+
deprecated(
666+
since = "0.21.0",
667+
note = "`PyErr::warn` will be replaced by `PyErr::warn_bound` in a future PyO3 version"
668+
)
669+
)]
670+
pub fn warn(py: Python<'_>, category: &PyAny, message: &str, stacklevel: i32) -> PyResult<()> {
671+
Self::warn_bound(py, &category.as_borrowed(), message, stacklevel)
672+
}
673+
638674
/// Issues a warning message.
639675
///
640676
/// May return an `Err(PyErr)` if warnings-as-errors is enabled.
@@ -650,13 +686,18 @@ impl PyErr {
650686
/// # use pyo3::prelude::*;
651687
/// # fn main() -> PyResult<()> {
652688
/// Python::with_gil(|py| {
653-
/// let user_warning = py.get_type::<pyo3::exceptions::PyUserWarning>();
654-
/// PyErr::warn(py, user_warning, "I am warning you", 0)?;
689+
/// let user_warning = py.get_type::<pyo3::exceptions::PyUserWarning>().as_borrowed();
690+
/// PyErr::warn_bound(py, &user_warning, "I am warning you", 0)?;
655691
/// Ok(())
656692
/// })
657693
/// # }
658694
/// ```
659-
pub fn warn(py: Python<'_>, category: &PyAny, message: &str, stacklevel: i32) -> PyResult<()> {
695+
pub fn warn_bound<'py>(
696+
py: Python<'py>,
697+
category: &Bound<'py, PyAny>,
698+
message: &str,
699+
stacklevel: i32,
700+
) -> PyResult<()> {
660701
let message = CString::new(message)?;
661702
error_on_minusone(py, unsafe {
662703
ffi::PyErr_WarnEx(
@@ -667,6 +708,34 @@ impl PyErr {
667708
})
668709
}
669710

711+
/// Deprecated form of [`PyErr::warn_explicit_bound`].
712+
#[cfg_attr(
713+
not(feature = "gil-refs"),
714+
deprecated(
715+
since = "0.21.0",
716+
note = "`PyErr::warn_explicit` will be replaced by `PyErr::warn_explicit_bound` in a future PyO3 version"
717+
)
718+
)]
719+
pub fn warn_explicit(
720+
py: Python<'_>,
721+
category: &PyAny,
722+
message: &str,
723+
filename: &str,
724+
lineno: i32,
725+
module: Option<&str>,
726+
registry: Option<&PyAny>,
727+
) -> PyResult<()> {
728+
Self::warn_explicit_bound(
729+
py,
730+
&category.as_borrowed(),
731+
message,
732+
filename,
733+
lineno,
734+
module,
735+
registry.map(PyNativeType::as_borrowed).as_deref(),
736+
)
737+
}
738+
670739
/// Issues a warning message, with more control over the warning attributes.
671740
///
672741
/// May return a `PyErr` if warnings-as-errors is enabled.
@@ -675,14 +744,14 @@ impl PyErr {
675744
///
676745
/// The `category` should be one of the `Warning` classes available in
677746
/// [`pyo3::exceptions`](crate::exceptions), or a subclass.
678-
pub fn warn_explicit(
679-
py: Python<'_>,
680-
category: &PyAny,
747+
pub fn warn_explicit_bound<'py>(
748+
py: Python<'py>,
749+
category: &Bound<'py, PyAny>,
681750
message: &str,
682751
filename: &str,
683752
lineno: i32,
684753
module: Option<&str>,
685-
registry: Option<&PyAny>,
754+
registry: Option<&Bound<'py, PyAny>>,
686755
) -> PyResult<()> {
687756
let message = CString::new(message)?;
688757
let filename = CString::new(filename)?;
@@ -975,7 +1044,7 @@ mod tests {
9751044
use super::PyErrState;
9761045
use crate::exceptions::{self, PyTypeError, PyValueError};
9771046
use crate::types::any::PyAnyMethods;
978-
use crate::{PyErr, PyTypeInfo, Python};
1047+
use crate::{PyErr, PyNativeType, PyTypeInfo, Python};
9791048

9801049
#[test]
9811050
fn no_error() {
@@ -1172,7 +1241,7 @@ mod tests {
11721241
// GIL locked should prevent effects to be visible to other testing
11731242
// threads.
11741243
Python::with_gil(|py| {
1175-
let cls = py.get_type::<exceptions::PyUserWarning>();
1244+
let cls = py.get_type::<exceptions::PyUserWarning>().as_borrowed();
11761245

11771246
// Reset warning filter to default state
11781247
let warnings = py.import_bound("warnings").unwrap();
@@ -1181,15 +1250,15 @@ mod tests {
11811250
// First, test the warning is emitted
11821251
assert_warnings!(
11831252
py,
1184-
{ PyErr::warn(py, cls, "I am warning you", 0).unwrap() },
1253+
{ PyErr::warn_bound(py, &cls, "I am warning you", 0).unwrap() },
11851254
[(exceptions::PyUserWarning, "I am warning you")]
11861255
);
11871256

11881257
// Test with raising
11891258
warnings
11901259
.call_method1("simplefilter", ("error", cls))
11911260
.unwrap();
1192-
PyErr::warn(py, cls, "I am warning you", 0).unwrap_err();
1261+
PyErr::warn_bound(py, &cls, "I am warning you", 0).unwrap_err();
11931262

11941263
// Test with error for an explicit module
11951264
warnings.call_method0("resetwarnings").unwrap();
@@ -1200,13 +1269,20 @@ mod tests {
12001269
// This has the wrong module and will not raise, just be emitted
12011270
assert_warnings!(
12021271
py,
1203-
{ PyErr::warn(py, cls, "I am warning you", 0).unwrap() },
1272+
{ PyErr::warn_bound(py, &cls, "I am warning you", 0).unwrap() },
12041273
[(exceptions::PyUserWarning, "I am warning you")]
12051274
);
12061275

1207-
let err =
1208-
PyErr::warn_explicit(py, cls, "I am warning you", "pyo3test.py", 427, None, None)
1209-
.unwrap_err();
1276+
let err = PyErr::warn_explicit_bound(
1277+
py,
1278+
&cls,
1279+
"I am warning you",
1280+
"pyo3test.py",
1281+
427,
1282+
None,
1283+
None,
1284+
)
1285+
.unwrap_err();
12101286
assert!(err
12111287
.value(py)
12121288
.getattr("args")

src/exceptions.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,16 +240,17 @@ macro_rules! create_exception_type_object {
240240
impl $name {
241241
fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
242242
use $crate::sync::GILOnceCell;
243+
use $crate::PyNativeType;
243244
static TYPE_OBJECT: GILOnceCell<$crate::Py<$crate::types::PyType>> =
244245
GILOnceCell::new();
245246

246247
TYPE_OBJECT
247248
.get_or_init(py, ||
248-
$crate::PyErr::new_type(
249+
$crate::PyErr::new_type_bound(
249250
py,
250251
concat!(stringify!($module), ".", stringify!($name)),
251252
$doc,
252-
::std::option::Option::Some(py.get_type::<$base>()),
253+
::std::option::Option::Some(&py.get_type::<$base>().as_borrowed()),
253254
::std::option::Option::None,
254255
).expect("Failed to initialize new exception type.")
255256
).as_ptr() as *mut $crate::ffi::PyTypeObject

0 commit comments

Comments
 (0)