implement `PyTracebackMethods`
This commit is contained in:
parent
f449fc0fc1
commit
f86053e2c2
|
@ -479,7 +479,7 @@ class House(object):
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
house
|
house
|
||||||
.call_method1("__exit__", (e.get_type(py), e.value(py), e.traceback(py)))
|
.call_method1("__exit__", (e.get_type(py), e.value(py), e.traceback_bound(py)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
exceptions::{PyBaseException, PyTypeError},
|
exceptions::{PyBaseException, PyTypeError},
|
||||||
ffi,
|
ffi,
|
||||||
types::{PyTraceback, PyType},
|
types::{PyTraceback, PyType},
|
||||||
IntoPy, Py, PyAny, PyObject, PyTypeInfo, Python,
|
Bound, IntoPy, Py, PyAny, PyObject, PyTypeInfo, Python,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -26,15 +26,21 @@ impl PyErrStateNormalized {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(Py_3_12))]
|
#[cfg(not(Py_3_12))]
|
||||||
pub(crate) fn ptraceback<'py>(&'py self, py: Python<'py>) -> Option<&'py PyTraceback> {
|
pub(crate) fn ptraceback<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTraceback>> {
|
||||||
self.ptraceback
|
self.ptraceback
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|traceback| traceback.as_ref(py))
|
.map(|traceback| traceback.bind(py).clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(Py_3_12)]
|
#[cfg(Py_3_12)]
|
||||||
pub(crate) fn ptraceback<'py>(&'py self, py: Python<'py>) -> Option<&'py PyTraceback> {
|
pub(crate) fn ptraceback<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTraceback>> {
|
||||||
unsafe { py.from_owned_ptr_or_opt(ffi::PyException_GetTraceback(self.pvalue.as_ptr())) }
|
use crate::ffi_ptr_ext::FfiPtrExt;
|
||||||
|
use crate::types::any::PyAnyMethods;
|
||||||
|
unsafe {
|
||||||
|
ffi::PyException_GetTraceback(self.pvalue.as_ptr())
|
||||||
|
.assume_owned_or_opt(py)
|
||||||
|
.map(|b| b.downcast_into_unchecked())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(Py_3_12)]
|
#[cfg(Py_3_12)]
|
||||||
|
|
|
@ -272,6 +272,18 @@ impl PyErr {
|
||||||
exc
|
exc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deprecated form of [`PyErr::traceback_bound`].
|
||||||
|
#[cfg_attr(
|
||||||
|
not(feature = "gil-refs"),
|
||||||
|
deprecated(
|
||||||
|
since = "0.21.0",
|
||||||
|
note = "`PyErr::traceback` will be replaced by `PyErr::traceback_bound` in a future PyO3 version"
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub fn traceback<'py>(&'py self, py: Python<'py>) -> Option<&'py PyTraceback> {
|
||||||
|
self.normalized(py).ptraceback(py).map(|b| b.into_gil_ref())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the traceback of this exception object.
|
/// Returns the traceback of this exception object.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -280,10 +292,10 @@ impl PyErr {
|
||||||
///
|
///
|
||||||
/// Python::with_gil(|py| {
|
/// Python::with_gil(|py| {
|
||||||
/// let err = PyTypeError::new_err(("some type error",));
|
/// let err = PyTypeError::new_err(("some type error",));
|
||||||
/// assert!(err.traceback(py).is_none());
|
/// assert!(err.traceback_bound(py).is_none());
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn traceback<'py>(&'py self, py: Python<'py>) -> Option<&'py PyTraceback> {
|
pub fn traceback_bound<'py>(&self, py: Python<'py>) -> Option<Bound<'py, PyTraceback>> {
|
||||||
self.normalized(py).ptraceback(py)
|
self.normalized(py).ptraceback(py)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,10 +488,16 @@ impl PyErr {
|
||||||
|
|
||||||
#[cfg(not(Py_3_12))]
|
#[cfg(not(Py_3_12))]
|
||||||
unsafe {
|
unsafe {
|
||||||
|
// keep the bound `traceback` alive for entire duration of
|
||||||
|
// PyErr_Display. if we inline this, the `Bound` will be dropped
|
||||||
|
// after the argument got evaluated, leading to call with a dangling
|
||||||
|
// pointer.
|
||||||
|
let traceback = self.traceback_bound(py);
|
||||||
ffi::PyErr_Display(
|
ffi::PyErr_Display(
|
||||||
self.get_type(py).as_ptr(),
|
self.get_type(py).as_ptr(),
|
||||||
self.value(py).as_ptr(),
|
self.value(py).as_ptr(),
|
||||||
self.traceback(py)
|
traceback
|
||||||
|
.as_ref()
|
||||||
.map_or(std::ptr::null_mut(), |traceback| traceback.as_ptr()),
|
.map_or(std::ptr::null_mut(), |traceback| traceback.as_ptr()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -658,15 +676,15 @@ impl PyErr {
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use pyo3::{exceptions::PyTypeError, PyErr, Python};
|
/// use pyo3::{exceptions::PyTypeError, PyErr, Python, prelude::PyAnyMethods};
|
||||||
/// Python::with_gil(|py| {
|
/// Python::with_gil(|py| {
|
||||||
/// let err: PyErr = PyTypeError::new_err(("some type error",));
|
/// let err: PyErr = PyTypeError::new_err(("some type error",));
|
||||||
/// let err_clone = err.clone_ref(py);
|
/// let err_clone = err.clone_ref(py);
|
||||||
/// assert!(err.get_type(py).is(err_clone.get_type(py)));
|
/// assert!(err.get_type(py).is(err_clone.get_type(py)));
|
||||||
/// assert!(err.value(py).is(err_clone.value(py)));
|
/// assert!(err.value(py).is(err_clone.value(py)));
|
||||||
/// match err.traceback(py) {
|
/// match err.traceback_bound(py) {
|
||||||
/// None => assert!(err_clone.traceback(py).is_none()),
|
/// None => assert!(err_clone.traceback_bound(py).is_none()),
|
||||||
/// Some(tb) => assert!(err_clone.traceback(py).unwrap().is(tb)),
|
/// Some(tb) => assert!(err_clone.traceback_bound(py).unwrap().is(&tb)),
|
||||||
/// }
|
/// }
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -747,7 +765,7 @@ impl std::fmt::Debug for PyErr {
|
||||||
f.debug_struct("PyErr")
|
f.debug_struct("PyErr")
|
||||||
.field("type", self.get_type(py))
|
.field("type", self.get_type(py))
|
||||||
.field("value", self.value(py))
|
.field("value", self.value(py))
|
||||||
.field("traceback", &self.traceback(py))
|
.field("traceback", &self.traceback_bound(py))
|
||||||
.finish()
|
.finish()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,7 @@ macro_rules! import_exception {
|
||||||
impl $name {
|
impl $name {
|
||||||
fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
|
fn type_object_raw(py: $crate::Python<'_>) -> *mut $crate::ffi::PyTypeObject {
|
||||||
use $crate::sync::GILOnceCell;
|
use $crate::sync::GILOnceCell;
|
||||||
|
use $crate::prelude::PyTracebackMethods;
|
||||||
static TYPE_OBJECT: GILOnceCell<$crate::Py<$crate::types::PyType>> =
|
static TYPE_OBJECT: GILOnceCell<$crate::Py<$crate::types::PyType>> =
|
||||||
GILOnceCell::new();
|
GILOnceCell::new();
|
||||||
|
|
||||||
|
@ -109,7 +110,7 @@ macro_rules! import_exception {
|
||||||
.import(stringify!($module))
|
.import(stringify!($module))
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
let traceback = err
|
let traceback = err
|
||||||
.traceback(py)
|
.traceback_bound(py)
|
||||||
.map(|tb| tb.format().expect("raised exception will have a traceback"))
|
.map(|tb| tb.format().expect("raised exception will have a traceback"))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
::std::panic!("Can not import module {}: {}\n{}", stringify!($module), err, traceback);
|
::std::panic!("Can not import module {}: {}\n{}", stringify!($module), err, traceback);
|
||||||
|
|
|
@ -38,4 +38,5 @@ pub use crate::types::module::PyModuleMethods;
|
||||||
pub use crate::types::sequence::PySequenceMethods;
|
pub use crate::types::sequence::PySequenceMethods;
|
||||||
pub use crate::types::set::PySetMethods;
|
pub use crate::types::set::PySetMethods;
|
||||||
pub use crate::types::string::PyStringMethods;
|
pub use crate::types::string::PyStringMethods;
|
||||||
|
pub use crate::types::traceback::PyTracebackMethods;
|
||||||
pub use crate::types::tuple::PyTupleMethods;
|
pub use crate::types::tuple::PyTupleMethods;
|
||||||
|
|
|
@ -304,6 +304,6 @@ pub(crate) mod sequence;
|
||||||
pub(crate) mod set;
|
pub(crate) mod set;
|
||||||
mod slice;
|
mod slice;
|
||||||
pub(crate) mod string;
|
pub(crate) mod string;
|
||||||
mod traceback;
|
pub(crate) mod traceback;
|
||||||
pub(crate) mod tuple;
|
pub(crate) mod tuple;
|
||||||
mod typeobject;
|
mod typeobject;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::err::{error_on_minusone, PyResult};
|
use crate::err::{error_on_minusone, PyResult};
|
||||||
use crate::ffi;
|
|
||||||
use crate::types::PyString;
|
use crate::types::PyString;
|
||||||
use crate::PyAny;
|
use crate::{ffi, Bound};
|
||||||
|
use crate::{PyAny, PyNativeType};
|
||||||
|
|
||||||
/// Represents a Python traceback.
|
/// Represents a Python traceback.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
@ -24,14 +24,14 @@ impl PyTraceback {
|
||||||
/// The following code formats a Python traceback and exception pair from Rust:
|
/// The following code formats a Python traceback and exception pair from Rust:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use pyo3::{Python, PyResult};
|
/// # use pyo3::{Python, PyResult, prelude::PyTracebackMethods};
|
||||||
/// # let result: PyResult<()> =
|
/// # let result: PyResult<()> =
|
||||||
/// Python::with_gil(|py| {
|
/// Python::with_gil(|py| {
|
||||||
/// let err = py
|
/// let err = py
|
||||||
/// .run("raise Exception('banana')", None, None)
|
/// .run("raise Exception('banana')", None, None)
|
||||||
/// .expect_err("raise will create a Python error");
|
/// .expect_err("raise will create a Python error");
|
||||||
///
|
///
|
||||||
/// let traceback = err.traceback(py).expect("raised exception will have a traceback");
|
/// let traceback = err.traceback_bound(py).expect("raised exception will have a traceback");
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// format!("{}{}", traceback.format()?, err),
|
/// format!("{}{}", traceback.format()?, err),
|
||||||
/// "\
|
/// "\
|
||||||
|
@ -46,6 +46,53 @@ impl PyTraceback {
|
||||||
/// # result.expect("example failed");
|
/// # result.expect("example failed");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn format(&self) -> PyResult<String> {
|
pub fn format(&self) -> PyResult<String> {
|
||||||
|
self.as_borrowed().format()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implementation of functionality for [`PyTraceback`].
|
||||||
|
///
|
||||||
|
/// These methods are defined for the `Bound<'py, PyTraceback>` smart pointer, so to use method call
|
||||||
|
/// syntax these methods are separated into a trait, because stable Rust does not yet support
|
||||||
|
/// `arbitrary_self_types`.
|
||||||
|
#[doc(alias = "PyTraceback")]
|
||||||
|
pub trait PyTracebackMethods<'py> {
|
||||||
|
/// Formats the traceback as a string.
|
||||||
|
///
|
||||||
|
/// This does not include the exception type and value. The exception type and value can be
|
||||||
|
/// formatted using the `Display` implementation for `PyErr`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// The following code formats a Python traceback and exception pair from Rust:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use pyo3::{Python, PyResult, prelude::PyTracebackMethods};
|
||||||
|
/// # let result: PyResult<()> =
|
||||||
|
/// Python::with_gil(|py| {
|
||||||
|
/// let err = py
|
||||||
|
/// .run("raise Exception('banana')", None, None)
|
||||||
|
/// .expect_err("raise will create a Python error");
|
||||||
|
///
|
||||||
|
/// let traceback = err.traceback_bound(py).expect("raised exception will have a traceback");
|
||||||
|
/// assert_eq!(
|
||||||
|
/// format!("{}{}", traceback.format()?, err),
|
||||||
|
/// "\
|
||||||
|
/// Traceback (most recent call last):
|
||||||
|
/// File \"<string>\", line 1, in <module>
|
||||||
|
/// Exception: banana\
|
||||||
|
/// "
|
||||||
|
/// );
|
||||||
|
/// Ok(())
|
||||||
|
/// })
|
||||||
|
/// # ;
|
||||||
|
/// # result.expect("example failed");
|
||||||
|
/// ```
|
||||||
|
fn format(&self) -> PyResult<String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'py> PyTracebackMethods<'py> for Bound<'py, PyTraceback> {
|
||||||
|
fn format(&self) -> PyResult<String> {
|
||||||
let py = self.py();
|
let py = self.py();
|
||||||
let string_io = py
|
let string_io = py
|
||||||
.import(intern!(py, "io"))?
|
.import(intern!(py, "io"))?
|
||||||
|
@ -65,7 +112,10 @@ impl PyTraceback {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{prelude::*, types::PyDict};
|
use crate::{
|
||||||
|
prelude::*,
|
||||||
|
types::{traceback::PyTracebackMethods, PyDict},
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn format_traceback() {
|
fn format_traceback() {
|
||||||
|
@ -75,7 +125,7 @@ mod tests {
|
||||||
.expect_err("raising should have given us an error");
|
.expect_err("raising should have given us an error");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
err.traceback(py).unwrap().format().unwrap(),
|
err.traceback_bound(py).unwrap().format().unwrap(),
|
||||||
"Traceback (most recent call last):\n File \"<string>\", line 1, in <module>\n"
|
"Traceback (most recent call last):\n File \"<string>\", line 1, in <module>\n"
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -99,7 +149,7 @@ except Exception as e:
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let err = PyErr::from_value(locals.get_item("err").unwrap().unwrap());
|
let err = PyErr::from_value(locals.get_item("err").unwrap().unwrap());
|
||||||
let traceback = err.value(py).getattr("__traceback__").unwrap();
|
let traceback = err.value(py).getattr("__traceback__").unwrap();
|
||||||
assert!(err.traceback(py).unwrap().is(traceback));
|
assert!(err.traceback_bound(py).unwrap().is(traceback));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,10 +169,10 @@ def f():
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let f = locals.get_item("f").unwrap().unwrap();
|
let f = locals.get_item("f").unwrap().unwrap();
|
||||||
let err = f.call0().unwrap_err();
|
let err = f.call0().unwrap_err();
|
||||||
let traceback = err.traceback(py).unwrap();
|
let traceback = err.traceback_bound(py).unwrap();
|
||||||
let err_object = err.clone_ref(py).into_py(py).into_ref(py);
|
let err_object = err.clone_ref(py).into_py(py).into_ref(py);
|
||||||
|
|
||||||
assert!(err_object.getattr("__traceback__").unwrap().is(traceback));
|
assert!(err_object.getattr("__traceback__").unwrap().is(&traceback));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue