pyo3/types/
traceback.rs
1use crate::err::{error_on_minusone, PyResult};
2use crate::types::{any::PyAnyMethods, string::PyStringMethods, PyString};
3use crate::{ffi, Bound, PyAny};
4
5#[repr(transparent)]
13pub struct PyTraceback(PyAny);
14
15pyobject_native_type_core!(
16 PyTraceback,
17 pyobject_native_static_type_object!(ffi::PyTraceBack_Type),
18 #checkfunction=ffi::PyTraceBack_Check
19);
20
21#[doc(alias = "PyTraceback")]
27pub trait PyTracebackMethods<'py>: crate::sealed::Sealed {
28 fn format(&self) -> PyResult<String>;
60}
61
62impl<'py> PyTracebackMethods<'py> for Bound<'py, PyTraceback> {
63 fn format(&self) -> PyResult<String> {
64 let py = self.py();
65 let string_io = py
66 .import(intern!(py, "io"))?
67 .getattr(intern!(py, "StringIO"))?
68 .call0()?;
69 let result = unsafe { ffi::PyTraceBack_Print(self.as_ptr(), string_io.as_ptr()) };
70 error_on_minusone(py, result)?;
71 let formatted = string_io
72 .getattr(intern!(py, "getvalue"))?
73 .call0()?
74 .downcast::<PyString>()?
75 .to_cow()?
76 .into_owned();
77 Ok(formatted)
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use crate::IntoPyObject;
84 use crate::{
85 ffi,
86 types::{any::PyAnyMethods, dict::PyDictMethods, traceback::PyTracebackMethods, PyDict},
87 PyErr, Python,
88 };
89
90 #[test]
91 fn format_traceback() {
92 Python::with_gil(|py| {
93 let err = py
94 .run(ffi::c_str!("raise Exception('banana')"), None, None)
95 .expect_err("raising should have given us an error");
96
97 assert_eq!(
98 err.traceback(py).unwrap().format().unwrap(),
99 "Traceback (most recent call last):\n File \"<string>\", line 1, in <module>\n"
100 );
101 })
102 }
103
104 #[test]
105 fn test_err_from_value() {
106 Python::with_gil(|py| {
107 let locals = PyDict::new(py);
108 py.run(
110 ffi::c_str!(
111 r"
112try:
113 raise ValueError('raised exception')
114except Exception as e:
115 err = e
116"
117 ),
118 None,
119 Some(&locals),
120 )
121 .unwrap();
122 let err = PyErr::from_value(locals.get_item("err").unwrap().unwrap());
123 let traceback = err.value(py).getattr("__traceback__").unwrap();
124 assert!(err.traceback(py).unwrap().is(&traceback));
125 })
126 }
127
128 #[test]
129 fn test_err_into_py() {
130 Python::with_gil(|py| {
131 let locals = PyDict::new(py);
132 py.run(
134 ffi::c_str!(
135 r"
136def f():
137 raise ValueError('raised exception')
138"
139 ),
140 None,
141 Some(&locals),
142 )
143 .unwrap();
144 let f = locals.get_item("f").unwrap().unwrap();
145 let err = f.call0().unwrap_err();
146 let traceback = err.traceback(py).unwrap();
147 let err_object = err.clone_ref(py).into_pyobject(py).unwrap();
148
149 assert!(err_object.getattr("__traceback__").unwrap().is(&traceback));
150 })
151 }
152}