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