Pyerr value bound (#3820)
* Implement PyErr::value_bound * Use PyErr::value_bound in conversions * Implement PyErr::from_value_bound * Remove unnecessary clone * Return a reference from PyErr::value_bound * Avoid clone in PyErr::from_value_bound * Use PyErr::from_value_bound instead of from_value * Remove unnecessary .as_borrowed() calls * Remove unused import * Simplify UnraisableCapture.hook * Use Bound::from_owned_ptr_or_opt in fn cause * Update PyErrState::lazy to take Py<PyAny> This is easier to work with elsewhere than `&PyAny` or `Bound<'py, PyAny>`. * Add Bound PyUnicodeDecodeError constructors * Update PyErr::from_value_bound to take Bound * Simplify PyErr::from_value * Simplify PyErr::value * Remove unnecessary reference * Simplify Pyerr::cause implementation * Simplify PyUnicodeDecodeError::new_bound
This commit is contained in:
parent
c24478e8bc
commit
940804fe0d
|
@ -479,7 +479,7 @@ class House(object):
|
|||
}
|
||||
Err(e) => {
|
||||
house
|
||||
.call_method1("__exit__", (e.get_type_bound(py), e.value(py), e.traceback_bound(py)))
|
||||
.call_method1("__exit__", (e.get_type_bound(py), e.value_bound(py), e.traceback_bound(py)))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ mod test_anyhow {
|
|||
Python::with_gil(|py| {
|
||||
let locals = [("err", pyerr)].into_py_dict_bound(py);
|
||||
let pyerr = py.run_bound("raise err", None, Some(&locals)).unwrap_err();
|
||||
assert_eq!(pyerr.value(py).to_string(), expected_contents);
|
||||
assert_eq!(pyerr.value_bound(py).to_string(), expected_contents);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ mod test_anyhow {
|
|||
Python::with_gil(|py| {
|
||||
let locals = [("err", pyerr)].into_py_dict_bound(py);
|
||||
let pyerr = py.run_bound("raise err", None, Some(&locals)).unwrap_err();
|
||||
assert_eq!(pyerr.value(py).to_string(), expected_contents);
|
||||
assert_eq!(pyerr.value_bound(py).to_string(), expected_contents);
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -590,7 +590,7 @@ mod tests {
|
|||
assert!(result.is_err());
|
||||
let res = result.err().unwrap();
|
||||
// Also check the error message is what we expect
|
||||
let msg = res.value(py).repr().unwrap().to_string();
|
||||
let msg = res.value_bound(py).repr().unwrap().to_string();
|
||||
assert_eq!(msg, "TypeError(\"zoneinfo.ZoneInfo(key='Europe/London') is not a fixed offset timezone\")");
|
||||
});
|
||||
}
|
||||
|
@ -605,7 +605,7 @@ mod tests {
|
|||
// Now test that converting a PyDateTime with tzinfo to a NaiveDateTime fails
|
||||
let res: PyResult<NaiveDateTime> = py_datetime.extract();
|
||||
assert_eq!(
|
||||
res.unwrap_err().value(py).repr().unwrap().to_string(),
|
||||
res.unwrap_err().value_bound(py).repr().unwrap().to_string(),
|
||||
"TypeError('expected a datetime without tzinfo')"
|
||||
);
|
||||
});
|
||||
|
@ -620,14 +620,14 @@ mod tests {
|
|||
// Now test that converting a PyDateTime with tzinfo to a NaiveDateTime fails
|
||||
let res: PyResult<DateTime<Utc>> = py_datetime.extract();
|
||||
assert_eq!(
|
||||
res.unwrap_err().value(py).repr().unwrap().to_string(),
|
||||
res.unwrap_err().value_bound(py).repr().unwrap().to_string(),
|
||||
"TypeError('expected a datetime with non-None tzinfo')"
|
||||
);
|
||||
|
||||
// Now test that converting a PyDateTime with tzinfo to a NaiveDateTime fails
|
||||
let res: PyResult<DateTime<FixedOffset>> = py_datetime.extract();
|
||||
assert_eq!(
|
||||
res.unwrap_err().value(py).repr().unwrap().to_string(),
|
||||
res.unwrap_err().value_bound(py).repr().unwrap().to_string(),
|
||||
"TypeError('expected a datetime with non-None tzinfo')"
|
||||
);
|
||||
});
|
||||
|
|
|
@ -154,7 +154,7 @@ mod tests {
|
|||
Python::with_gil(|py| {
|
||||
let locals = [("err", pyerr)].into_py_dict_bound(py);
|
||||
let pyerr = py.run_bound("raise err", None, Some(&locals)).unwrap_err();
|
||||
assert_eq!(pyerr.value(py).to_string(), expected_contents);
|
||||
assert_eq!(pyerr.value_bound(py).to_string(), expected_contents);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ mod tests {
|
|||
Python::with_gil(|py| {
|
||||
let locals = [("err", pyerr)].into_py_dict_bound(py);
|
||||
let pyerr = py.run_bound("raise err", None, Some(&locals)).unwrap_err();
|
||||
assert_eq!(pyerr.value(py).to_string(), expected_contents);
|
||||
assert_eq!(pyerr.value_bound(py).to_string(), expected_contents);
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ impl Coroutine {
|
|||
(Some(exc), Some(cb)) => cb.throw(exc.as_ref(py)),
|
||||
(Some(exc), None) => {
|
||||
self.close();
|
||||
return Err(PyErr::from_value(exc.as_ref(py)));
|
||||
return Err(PyErr::from_value_bound(exc.into_bound(py)));
|
||||
}
|
||||
(None, _) => {}
|
||||
}
|
||||
|
|
|
@ -101,19 +101,20 @@ where
|
|||
}
|
||||
|
||||
impl PyErrState {
|
||||
pub(crate) fn lazy(ptype: &PyAny, args: impl PyErrArguments + 'static) -> Self {
|
||||
let ptype = ptype.into();
|
||||
pub(crate) fn lazy(ptype: Py<PyAny>, args: impl PyErrArguments + 'static) -> Self {
|
||||
PyErrState::Lazy(Box::new(move |py| PyErrStateLazyFnOutput {
|
||||
ptype,
|
||||
pvalue: args.arguments(py),
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn normalized(pvalue: &PyBaseException) -> Self {
|
||||
pub(crate) fn normalized(pvalue: Bound<'_, PyBaseException>) -> Self {
|
||||
#[cfg(not(Py_3_12))]
|
||||
use crate::types::any::PyAnyMethods;
|
||||
|
||||
Self::Normalized(PyErrStateNormalized {
|
||||
#[cfg(not(Py_3_12))]
|
||||
ptype: pvalue.get_type().into(),
|
||||
pvalue: pvalue.into(),
|
||||
#[cfg(not(Py_3_12))]
|
||||
ptraceback: unsafe {
|
||||
Py::from_owned_ptr_or_opt(
|
||||
|
@ -121,6 +122,7 @@ impl PyErrState {
|
|||
ffi::PyException_GetTraceback(pvalue.as_ptr()),
|
||||
)
|
||||
},
|
||||
pvalue: pvalue.into(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -124,6 +124,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn io_errors() {
|
||||
use crate::types::any::PyAnyMethods;
|
||||
|
||||
let check_err = |kind, expected_ty| {
|
||||
Python::with_gil(|py| {
|
||||
let rust_err = io::Error::new(kind, "some error msg");
|
||||
|
@ -139,8 +141,8 @@ mod tests {
|
|||
|
||||
let py_err_recovered_from_rust_err: PyErr = rust_err_from_py_err.into();
|
||||
assert!(py_err_recovered_from_rust_err
|
||||
.value(py)
|
||||
.is(py_error_clone.value(py))); // It should be the same exception
|
||||
.value_bound(py)
|
||||
.is(py_error_clone.value_bound(py))); // It should be the same exception
|
||||
})
|
||||
};
|
||||
|
||||
|
|
|
@ -187,7 +187,19 @@ impl PyErr {
|
|||
where
|
||||
A: PyErrArguments + Send + Sync + 'static,
|
||||
{
|
||||
PyErr::from_state(PyErrState::lazy(ty, args))
|
||||
PyErr::from_state(PyErrState::lazy(ty.into(), args))
|
||||
}
|
||||
|
||||
/// Deprecated form of [`PyErr::from_value_bound`].
|
||||
#[cfg_attr(
|
||||
not(feature = "gil-refs"),
|
||||
deprecated(
|
||||
since = "0.21.0",
|
||||
note = "`PyErr::from_value` will be replaced by `PyErr::from_value_bound` in a future PyO3 version"
|
||||
)
|
||||
)]
|
||||
pub fn from_value(obj: &PyAny) -> PyErr {
|
||||
PyErr::from_value_bound(obj.as_borrowed().to_owned())
|
||||
}
|
||||
|
||||
/// Creates a new PyErr.
|
||||
|
@ -201,33 +213,38 @@ impl PyErr {
|
|||
/// # Examples
|
||||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::PyTypeInfo;
|
||||
/// use pyo3::exceptions::PyTypeError;
|
||||
/// use pyo3::types::{PyType, PyString};
|
||||
/// use pyo3::types::PyString;
|
||||
///
|
||||
/// Python::with_gil(|py| {
|
||||
/// // Case #1: Exception object
|
||||
/// let err = PyErr::from_value(PyTypeError::new_err("some type error").value(py));
|
||||
/// let err = PyErr::from_value_bound(PyTypeError::new_err("some type error")
|
||||
/// .value_bound(py).clone().into_any());
|
||||
/// assert_eq!(err.to_string(), "TypeError: some type error");
|
||||
///
|
||||
/// // Case #2: Exception type
|
||||
/// let err = PyErr::from_value(PyType::new::<PyTypeError>(py));
|
||||
/// let err = PyErr::from_value_bound(PyTypeError::type_object_bound(py).into_any());
|
||||
/// assert_eq!(err.to_string(), "TypeError: ");
|
||||
///
|
||||
/// // Case #3: Invalid exception value
|
||||
/// let err = PyErr::from_value(PyString::new_bound(py, "foo").as_gil_ref());
|
||||
/// let err = PyErr::from_value_bound(PyString::new_bound(py, "foo").into_any());
|
||||
/// assert_eq!(
|
||||
/// err.to_string(),
|
||||
/// "TypeError: exceptions must derive from BaseException"
|
||||
/// );
|
||||
/// });
|
||||
/// ```
|
||||
pub fn from_value(obj: &PyAny) -> PyErr {
|
||||
let state = if let Ok(obj) = obj.downcast::<PyBaseException>() {
|
||||
PyErrState::normalized(obj)
|
||||
} else {
|
||||
// Assume obj is Type[Exception]; let later normalization handle if this
|
||||
// is not the case
|
||||
PyErrState::lazy(obj, obj.py().None())
|
||||
pub fn from_value_bound(obj: Bound<'_, PyAny>) -> PyErr {
|
||||
let state = match obj.downcast_into::<PyBaseException>() {
|
||||
Ok(obj) => PyErrState::normalized(obj),
|
||||
Err(err) => {
|
||||
// Assume obj is Type[Exception]; let later normalization handle if this
|
||||
// is not the case
|
||||
let obj = err.into_inner();
|
||||
let py = obj.py();
|
||||
PyErrState::lazy(obj.into_py(py), py.None())
|
||||
}
|
||||
};
|
||||
|
||||
PyErr::from_state(state)
|
||||
|
@ -260,6 +277,18 @@ impl PyErr {
|
|||
self.normalized(py).ptype(py)
|
||||
}
|
||||
|
||||
/// Deprecated form of [`PyErr::value_bound`].
|
||||
#[cfg_attr(
|
||||
not(feature = "gil-refs"),
|
||||
deprecated(
|
||||
since = "0.21.0",
|
||||
note = "`PyErr::value` will be replaced by `PyErr::value_bound` in a future PyO3 version"
|
||||
)
|
||||
)]
|
||||
pub fn value<'py>(&'py self, py: Python<'py>) -> &'py PyBaseException {
|
||||
self.value_bound(py).as_gil_ref()
|
||||
}
|
||||
|
||||
/// Returns the value of this exception.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -270,11 +299,11 @@ impl PyErr {
|
|||
/// Python::with_gil(|py| {
|
||||
/// let err: PyErr = PyTypeError::new_err(("some type error",));
|
||||
/// assert!(err.is_instance_of::<PyTypeError>(py));
|
||||
/// assert_eq!(err.value(py).to_string(), "some type error");
|
||||
/// assert_eq!(err.value_bound(py).to_string(), "some type error");
|
||||
/// });
|
||||
/// ```
|
||||
pub fn value<'py>(&'py self, py: Python<'py>) -> &'py PyBaseException {
|
||||
self.normalized(py).pvalue.as_ref(py)
|
||||
pub fn value_bound<'py>(&self, py: Python<'py>) -> &Bound<'py, PyBaseException> {
|
||||
self.normalized(py).pvalue.bind(py)
|
||||
}
|
||||
|
||||
/// Consumes self to take ownership of the exception value contained in this error.
|
||||
|
@ -527,7 +556,7 @@ impl PyErr {
|
|||
pub fn display(&self, py: Python<'_>) {
|
||||
#[cfg(Py_3_12)]
|
||||
unsafe {
|
||||
ffi::PyErr_DisplayException(self.value(py).as_ptr())
|
||||
ffi::PyErr_DisplayException(self.value_bound(py).as_ptr())
|
||||
}
|
||||
|
||||
#[cfg(not(Py_3_12))]
|
||||
|
@ -540,7 +569,7 @@ impl PyErr {
|
|||
let type_bound = self.get_type_bound(py);
|
||||
ffi::PyErr_Display(
|
||||
type_bound.as_ptr(),
|
||||
self.value(py).as_ptr(),
|
||||
self.value_bound(py).as_ptr(),
|
||||
traceback
|
||||
.as_ref()
|
||||
.map_or(std::ptr::null_mut(), |traceback| traceback.as_ptr()),
|
||||
|
@ -785,7 +814,7 @@ impl PyErr {
|
|||
/// let err: PyErr = PyTypeError::new_err(("some type error",));
|
||||
/// let err_clone = err.clone_ref(py);
|
||||
/// assert!(err.get_type_bound(py).is(&err_clone.get_type_bound(py)));
|
||||
/// assert!(err.value(py).is(err_clone.value(py)));
|
||||
/// assert!(err.value_bound(py).is(err_clone.value_bound(py)));
|
||||
/// match err.traceback_bound(py) {
|
||||
/// None => assert!(err_clone.traceback_bound(py).is_none()),
|
||||
/// Some(tb) => assert!(err_clone.traceback_bound(py).unwrap().is(&tb)),
|
||||
|
@ -800,15 +829,14 @@ impl PyErr {
|
|||
/// Return the cause (either an exception instance, or None, set by `raise ... from ...`)
|
||||
/// associated with the exception, as accessible from Python through `__cause__`.
|
||||
pub fn cause(&self, py: Python<'_>) -> Option<PyErr> {
|
||||
let value = self.value(py);
|
||||
let obj =
|
||||
unsafe { py.from_owned_ptr_or_opt::<PyAny>(ffi::PyException_GetCause(value.as_ptr())) };
|
||||
obj.map(Self::from_value)
|
||||
use crate::ffi_ptr_ext::FfiPtrExt;
|
||||
unsafe { ffi::PyException_GetCause(self.value_bound(py).as_ptr()).assume_owned_or_opt(py) }
|
||||
.map(Self::from_value_bound)
|
||||
}
|
||||
|
||||
/// Set the cause associated with the exception, pass `None` to clear it.
|
||||
pub fn set_cause(&self, py: Python<'_>, cause: Option<Self>) {
|
||||
let value = self.value(py);
|
||||
let value = self.value_bound(py);
|
||||
let cause = cause.map(|err| err.into_value(py));
|
||||
unsafe {
|
||||
// PyException_SetCause _steals_ a reference to cause, so must use .into_ptr()
|
||||
|
@ -868,7 +896,7 @@ impl std::fmt::Debug for PyErr {
|
|||
Python::with_gil(|py| {
|
||||
f.debug_struct("PyErr")
|
||||
.field("type", &self.get_type_bound(py))
|
||||
.field("value", self.value(py))
|
||||
.field("value", self.value_bound(py))
|
||||
.field("traceback", &self.traceback_bound(py))
|
||||
.finish()
|
||||
})
|
||||
|
@ -877,8 +905,9 @@ impl std::fmt::Debug for PyErr {
|
|||
|
||||
impl std::fmt::Display for PyErr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use crate::types::string::PyStringMethods;
|
||||
Python::with_gil(|py| {
|
||||
let value = self.value(py);
|
||||
let value = self.value_bound(py);
|
||||
let type_name = value.get_type().qualname().map_err(|_| std::fmt::Error)?;
|
||||
write!(f, "{}", type_name)?;
|
||||
if let Ok(s) = value.str() {
|
||||
|
@ -1043,7 +1072,6 @@ impl_signed_integer!(isize);
|
|||
mod tests {
|
||||
use super::PyErrState;
|
||||
use crate::exceptions::{self, PyTypeError, PyValueError};
|
||||
use crate::types::any::PyAnyMethods;
|
||||
use crate::{PyErr, PyNativeType, PyTypeInfo, Python};
|
||||
|
||||
#[test]
|
||||
|
@ -1237,6 +1265,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn warnings() {
|
||||
use crate::types::any::PyAnyMethods;
|
||||
// Note: although the warning filter is interpreter global, keeping the
|
||||
// GIL locked should prevent effects to be visible to other testing
|
||||
// threads.
|
||||
|
@ -1284,7 +1313,7 @@ mod tests {
|
|||
)
|
||||
.unwrap_err();
|
||||
assert!(err
|
||||
.value(py)
|
||||
.value_bound(py)
|
||||
.getattr("args")
|
||||
.unwrap()
|
||||
.get_item(0)
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
//! yourself to import Python classes that are ultimately derived from
|
||||
//! `BaseException`.
|
||||
|
||||
use crate::{ffi, PyResult, Python};
|
||||
use crate::{ffi, Bound, PyResult, Python};
|
||||
use std::ffi::CStr;
|
||||
use std::ops;
|
||||
use std::os::raw::c_char;
|
||||
|
@ -22,6 +22,7 @@ macro_rules! impl_exception_boilerplate {
|
|||
impl ::std::convert::From<&$name> for $crate::PyErr {
|
||||
#[inline]
|
||||
fn from(err: &$name) -> $crate::PyErr {
|
||||
#[allow(deprecated)]
|
||||
$crate::PyErr::from_value(err)
|
||||
}
|
||||
}
|
||||
|
@ -613,7 +614,14 @@ impl_windows_native_exception!(
|
|||
);
|
||||
|
||||
impl PyUnicodeDecodeError {
|
||||
/// Creates a Python `UnicodeDecodeError`.
|
||||
/// Deprecated form of [`PyUnicodeDecodeError::new_bound`].
|
||||
#[cfg_attr(
|
||||
not(feature = "gil-refs"),
|
||||
deprecated(
|
||||
since = "0.21.0",
|
||||
note = "`PyUnicodeDecodeError::new` will be replaced by `PyUnicodeDecodeError::new_bound` in a future PyO3 version"
|
||||
)
|
||||
)]
|
||||
pub fn new<'p>(
|
||||
py: Python<'p>,
|
||||
encoding: &CStr,
|
||||
|
@ -621,16 +629,47 @@ impl PyUnicodeDecodeError {
|
|||
range: ops::Range<usize>,
|
||||
reason: &CStr,
|
||||
) -> PyResult<&'p PyUnicodeDecodeError> {
|
||||
Ok(PyUnicodeDecodeError::new_bound(py, encoding, input, range, reason)?.into_gil_ref())
|
||||
}
|
||||
|
||||
/// Creates a Python `UnicodeDecodeError`.
|
||||
pub fn new_bound<'p>(
|
||||
py: Python<'p>,
|
||||
encoding: &CStr,
|
||||
input: &[u8],
|
||||
range: ops::Range<usize>,
|
||||
reason: &CStr,
|
||||
) -> PyResult<Bound<'p, PyUnicodeDecodeError>> {
|
||||
use crate::ffi_ptr_ext::FfiPtrExt;
|
||||
use crate::py_result_ext::PyResultExt;
|
||||
unsafe {
|
||||
py.from_owned_ptr_or_err(ffi::PyUnicodeDecodeError_Create(
|
||||
ffi::PyUnicodeDecodeError_Create(
|
||||
encoding.as_ptr(),
|
||||
input.as_ptr() as *const c_char,
|
||||
input.len() as ffi::Py_ssize_t,
|
||||
range.start as ffi::Py_ssize_t,
|
||||
range.end as ffi::Py_ssize_t,
|
||||
reason.as_ptr(),
|
||||
))
|
||||
)
|
||||
.assume_owned_or_err(py)
|
||||
}
|
||||
.downcast_into()
|
||||
}
|
||||
|
||||
/// Deprecated form of [`PyUnicodeDecodeError::new_utf8_bound`].
|
||||
#[cfg_attr(
|
||||
not(feature = "gil-refs"),
|
||||
deprecated(
|
||||
since = "0.21.0",
|
||||
note = "`PyUnicodeDecodeError::new_utf8` will be replaced by `PyUnicodeDecodeError::new_utf8_bound` in a future PyO3 version"
|
||||
)
|
||||
)]
|
||||
pub fn new_utf8<'p>(
|
||||
py: Python<'p>,
|
||||
input: &[u8],
|
||||
err: std::str::Utf8Error,
|
||||
) -> PyResult<&'p PyUnicodeDecodeError> {
|
||||
Ok(PyUnicodeDecodeError::new_utf8_bound(py, input, err)?.into_gil_ref())
|
||||
}
|
||||
|
||||
/// Creates a Python `UnicodeDecodeError` from a Rust UTF-8 decoding error.
|
||||
|
@ -646,7 +685,7 @@ impl PyUnicodeDecodeError {
|
|||
/// Python::with_gil(|py| {
|
||||
/// let invalid_utf8 = b"fo\xd8o";
|
||||
/// let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
|
||||
/// let decode_err = PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err)?;
|
||||
/// let decode_err = PyUnicodeDecodeError::new_utf8_bound(py, invalid_utf8, err)?;
|
||||
/// assert_eq!(
|
||||
/// decode_err.to_string(),
|
||||
/// "'utf-8' codec can't decode byte 0xd8 in position 2: invalid utf-8"
|
||||
|
@ -654,13 +693,13 @@ impl PyUnicodeDecodeError {
|
|||
/// Ok(())
|
||||
/// })
|
||||
/// # }
|
||||
pub fn new_utf8<'p>(
|
||||
pub fn new_utf8_bound<'p>(
|
||||
py: Python<'p>,
|
||||
input: &[u8],
|
||||
err: std::str::Utf8Error,
|
||||
) -> PyResult<&'p PyUnicodeDecodeError> {
|
||||
) -> PyResult<Bound<'p, PyUnicodeDecodeError>> {
|
||||
let pos = err.valid_up_to();
|
||||
PyUnicodeDecodeError::new(
|
||||
PyUnicodeDecodeError::new_bound(
|
||||
py,
|
||||
CStr::from_bytes_with_nul(b"utf-8\0").unwrap(),
|
||||
input,
|
||||
|
@ -745,7 +784,7 @@ macro_rules! test_exception {
|
|||
|
||||
assert!(err.is_instance_of::<$exc_ty>(py));
|
||||
|
||||
let value: &$exc_ty = err.value(py).downcast().unwrap();
|
||||
let value: &$exc_ty = err.value_bound(py).clone().into_gil_ref().downcast().unwrap();
|
||||
assert!(value.source().is_none());
|
||||
|
||||
err.set_cause(py, Some($crate::exceptions::PyValueError::new_err("a cause")));
|
||||
|
@ -1025,14 +1064,14 @@ mod tests {
|
|||
#[cfg_attr(invalid_from_utf8_lint, allow(invalid_from_utf8))]
|
||||
let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
|
||||
Python::with_gil(|py| {
|
||||
let decode_err = PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err).unwrap();
|
||||
let decode_err = PyUnicodeDecodeError::new_utf8_bound(py, invalid_utf8, err).unwrap();
|
||||
assert_eq!(
|
||||
format!("{:?}", decode_err),
|
||||
"UnicodeDecodeError('utf-8', b'fo\\xd8o', 2, 3, 'invalid utf-8')"
|
||||
);
|
||||
|
||||
// Restoring should preserve the same error
|
||||
let e: PyErr = decode_err.into();
|
||||
let e = PyErr::from_value_bound(decode_err.into_any());
|
||||
e.restore(py);
|
||||
|
||||
assert_eq!(
|
||||
|
@ -1081,7 +1120,11 @@ mod tests {
|
|||
let invalid_utf8 = b"fo\xd8o";
|
||||
#[cfg_attr(invalid_from_utf8_lint, allow(invalid_from_utf8))]
|
||||
let err = std::str::from_utf8(invalid_utf8).expect_err("should be invalid utf8");
|
||||
PyErr::from_value(PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err).unwrap())
|
||||
PyErr::from_value_bound(
|
||||
PyUnicodeDecodeError::new_utf8_bound(py, invalid_utf8, err)
|
||||
.unwrap()
|
||||
.into_any(),
|
||||
)
|
||||
});
|
||||
test_exception!(PyUnicodeEncodeError, |py| py
|
||||
.eval_bound("chr(40960).encode('ascii')", None, None)
|
||||
|
|
|
@ -167,8 +167,11 @@ pub fn from_py_with_with_default<'a, 'py, T>(
|
|||
pub fn argument_extraction_error(py: Python<'_>, arg_name: &str, error: PyErr) -> PyErr {
|
||||
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)));
|
||||
let remapped_error = PyTypeError::new_err(format!(
|
||||
"argument '{}': {}",
|
||||
arg_name,
|
||||
error.value_bound(py)
|
||||
));
|
||||
remapped_error.set_cause(py, error.cause(py));
|
||||
remapped_error
|
||||
} else {
|
||||
|
|
|
@ -73,8 +73,8 @@ mod inner {
|
|||
#[cfg(all(feature = "macros", Py_3_8))]
|
||||
#[pymethods(crate = "pyo3")]
|
||||
impl UnraisableCapture {
|
||||
pub fn hook(&mut self, unraisable: &PyAny) {
|
||||
let err = PyErr::from_value(unraisable.getattr("exc_value").unwrap());
|
||||
pub fn hook(&mut self, unraisable: Bound<'_, PyAny>) {
|
||||
let err = PyErr::from_value_bound(unraisable.getattr("exc_value").unwrap());
|
||||
let instance = unraisable.getattr("object").unwrap();
|
||||
self.capture = Some((err, instance.into()));
|
||||
}
|
||||
|
|
|
@ -73,9 +73,9 @@ impl<'a> PyStringData<'a> {
|
|||
match self {
|
||||
Self::Ucs1(data) => match str::from_utf8(data) {
|
||||
Ok(s) => Ok(Cow::Borrowed(s)),
|
||||
Err(e) => Err(crate::PyErr::from_value(PyUnicodeDecodeError::new_utf8(
|
||||
py, data, e,
|
||||
)?)),
|
||||
Err(e) => Err(crate::PyErr::from_value_bound(
|
||||
PyUnicodeDecodeError::new_utf8_bound(py, data, e)?.into_any(),
|
||||
)),
|
||||
},
|
||||
Self::Ucs2(data) => match String::from_utf16(data) {
|
||||
Ok(s) => Ok(Cow::Owned(s)),
|
||||
|
@ -83,24 +83,30 @@ impl<'a> PyStringData<'a> {
|
|||
let mut message = e.to_string().as_bytes().to_vec();
|
||||
message.push(0);
|
||||
|
||||
Err(crate::PyErr::from_value(PyUnicodeDecodeError::new(
|
||||
py,
|
||||
CStr::from_bytes_with_nul(b"utf-16\0").unwrap(),
|
||||
self.as_bytes(),
|
||||
0..self.as_bytes().len(),
|
||||
CStr::from_bytes_with_nul(&message).unwrap(),
|
||||
)?))
|
||||
Err(crate::PyErr::from_value_bound(
|
||||
PyUnicodeDecodeError::new_bound(
|
||||
py,
|
||||
CStr::from_bytes_with_nul(b"utf-16\0").unwrap(),
|
||||
self.as_bytes(),
|
||||
0..self.as_bytes().len(),
|
||||
CStr::from_bytes_with_nul(&message).unwrap(),
|
||||
)?
|
||||
.into_any(),
|
||||
))
|
||||
}
|
||||
},
|
||||
Self::Ucs4(data) => match data.iter().map(|&c| std::char::from_u32(c)).collect() {
|
||||
Some(s) => Ok(Cow::Owned(s)),
|
||||
None => Err(crate::PyErr::from_value(PyUnicodeDecodeError::new(
|
||||
py,
|
||||
CStr::from_bytes_with_nul(b"utf-32\0").unwrap(),
|
||||
self.as_bytes(),
|
||||
0..self.as_bytes().len(),
|
||||
CStr::from_bytes_with_nul(b"error converting utf-32\0").unwrap(),
|
||||
)?)),
|
||||
None => Err(crate::PyErr::from_value_bound(
|
||||
PyUnicodeDecodeError::new_bound(
|
||||
py,
|
||||
CStr::from_bytes_with_nul(b"utf-32\0").unwrap(),
|
||||
self.as_bytes(),
|
||||
0..self.as_bytes().len(),
|
||||
CStr::from_bytes_with_nul(b"error converting utf-32\0").unwrap(),
|
||||
)?
|
||||
.into_any(),
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,9 +150,9 @@ except Exception as e:
|
|||
Some(&locals),
|
||||
)
|
||||
.unwrap();
|
||||
let err = PyErr::from_value(locals.get_item("err").unwrap().unwrap().into_gil_ref());
|
||||
let traceback = err.value(py).getattr("__traceback__").unwrap();
|
||||
assert!(err.traceback_bound(py).unwrap().is(traceback));
|
||||
let err = PyErr::from_value_bound(locals.get_item("err").unwrap().unwrap());
|
||||
let traceback = err.value_bound(py).getattr("__traceback__").unwrap();
|
||||
assert!(err.traceback_bound(py).unwrap().is(&traceback));
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ fn cancelled_coroutine() {
|
|||
)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
err.value(gil).get_type().qualname().unwrap(),
|
||||
err.value_bound(gil).get_type().qualname().unwrap(),
|
||||
"CancelledError"
|
||||
);
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue