Implement PyErr::get_type_bound (#3819)

* Implement PyErr::get_type_bound

* Update docs for PyErr::get_type_bound

* Fix doctest for cloning PyErr

* Import the whole prelude in docs example

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Remove unnecessary self lifetime

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Remove more unnecessary self lifetimes

* Use variables to avoid dangling pointers

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Avoid using ffi in fn ptype on Py_3_12

Co-authored-by: David Hewitt <mail@davidhewitt.dev>

* Add missing imports to fn ptype

---------

Co-authored-by: David Hewitt <mail@davidhewitt.dev>
This commit is contained in:
Lily Foote 2024-02-11 21:07:28 +00:00 committed by GitHub
parent c56cd3dd65
commit baf5c8ec0a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 30 additions and 13 deletions

View File

@ -479,7 +479,7 @@ class House(object):
}
Err(e) => {
house
.call_method1("__exit__", (e.get_type(py), e.value(py), e.traceback_bound(py)))
.call_method1("__exit__", (e.get_type_bound(py), e.value(py), e.traceback_bound(py)))
.unwrap();
}
}

View File

@ -16,13 +16,15 @@ pub(crate) struct PyErrStateNormalized {
impl PyErrStateNormalized {
#[cfg(not(Py_3_12))]
pub(crate) fn ptype<'py>(&'py self, py: Python<'py>) -> &'py PyType {
self.ptype.as_ref(py)
pub(crate) fn ptype<'py>(&self, py: Python<'py>) -> Bound<'py, PyType> {
self.ptype.bind(py).clone()
}
#[cfg(Py_3_12)]
pub(crate) fn ptype<'py>(&'py self, py: Python<'py>) -> &'py PyType {
self.pvalue.as_ref(py).get_type()
pub(crate) fn ptype<'py>(&self, py: Python<'py>) -> Bound<'py, PyType> {
use crate::instance::PyNativeType;
use crate::types::any::PyAnyMethods;
self.pvalue.bind(py).get_type().as_borrowed().to_owned()
}
#[cfg(not(Py_3_12))]

View File

@ -225,18 +225,30 @@ impl PyErr {
PyErr::from_state(state)
}
/// Deprecated form of [`PyErr::get_type_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyErr::get_type` will be replaced by `PyErr::get_type_bound` in a future PyO3 version"
)
)]
pub fn get_type<'py>(&'py self, py: Python<'py>) -> &'py PyType {
self.get_type_bound(py).into_gil_ref()
}
/// Returns the type of this exception.
///
/// # Examples
/// ```rust
/// use pyo3::{exceptions::PyTypeError, types::PyType, PyErr, Python};
/// use pyo3::{prelude::*, exceptions::PyTypeError, types::PyType};
///
/// Python::with_gil(|py| {
/// let err: PyErr = PyTypeError::new_err(("some type error",));
/// assert!(err.get_type(py).is(PyType::new::<PyTypeError>(py)));
/// assert!(err.get_type_bound(py).is(PyType::new::<PyTypeError>(py)));
/// });
/// ```
pub fn get_type<'py>(&'py self, py: Python<'py>) -> &'py PyType {
pub fn get_type_bound<'py>(&self, py: Python<'py>) -> Bound<'py, PyType> {
self.normalized(py).ptype(py)
}
@ -493,8 +505,9 @@ impl PyErr {
// after the argument got evaluated, leading to call with a dangling
// pointer.
let traceback = self.traceback_bound(py);
let type_bound = self.get_type_bound(py);
ffi::PyErr_Display(
self.get_type(py).as_ptr(),
type_bound.as_ptr(),
self.value(py).as_ptr(),
traceback
.as_ref()
@ -531,7 +544,8 @@ impl PyErr {
/// Returns true if the current exception is instance of `T`.
#[inline]
pub fn is_instance(&self, py: Python<'_>, ty: &PyAny) -> bool {
(unsafe { ffi::PyErr_GivenExceptionMatches(self.get_type(py).as_ptr(), ty.as_ptr()) }) != 0
let type_bound = self.get_type_bound(py);
(unsafe { ffi::PyErr_GivenExceptionMatches(type_bound.as_ptr(), ty.as_ptr()) }) != 0
}
/// Returns true if the current exception is instance of `T`.
@ -680,7 +694,7 @@ impl PyErr {
/// Python::with_gil(|py| {
/// let err: PyErr = PyTypeError::new_err(("some type error",));
/// let err_clone = err.clone_ref(py);
/// assert!(err.get_type(py).is(err_clone.get_type(py)));
/// assert!(err.get_type_bound(py).is(&err_clone.get_type_bound(py)));
/// assert!(err.value(py).is(err_clone.value(py)));
/// match err.traceback_bound(py) {
/// None => assert!(err_clone.traceback_bound(py).is_none()),
@ -763,7 +777,7 @@ impl std::fmt::Debug for PyErr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
Python::with_gil(|py| {
f.debug_struct("PyErr")
.field("type", self.get_type(py))
.field("type", &self.get_type_bound(py))
.field("value", self.value(py))
.field("traceback", &self.traceback_bound(py))
.finish()

View File

@ -165,7 +165,8 @@ pub fn from_py_with_with_default<'py, T>(
#[doc(hidden)]
#[cold]
pub fn argument_extraction_error(py: Python<'_>, arg_name: &str, error: PyErr) -> PyErr {
if error.get_type(py).is(py.get_type::<PyTypeError>()) {
use crate::types::any::PyAnyMethods;
if error.get_type_bound(py).is(py.get_type::<PyTypeError>()) {
let remapped_error =
PyTypeError::new_err(format!("argument '{}': {}", arg_name, error.value(py)));
remapped_error.set_cause(py, error.cause(py));