rustpython_vm/builtins/
traceback.rs1use super::{PyList, PyType};
2use crate::{
3 AsObject, Context, Py, PyPayload, PyRef, PyResult, VirtualMachine, class::PyClassImpl,
4 frame::FrameRef, function::PySetterValue, types::Constructor,
5};
6use rustpython_common::lock::PyMutex;
7use rustpython_compiler_core::OneIndexed;
8
9#[pyclass(module = false, name = "traceback", traverse)]
10#[derive(Debug)]
11pub struct PyTraceback {
12 pub next: PyMutex<Option<PyTracebackRef>>,
13 pub frame: FrameRef,
14 #[pytraverse(skip)]
15 pub lasti: u32,
16 #[pytraverse(skip)]
17 pub lineno: OneIndexed,
18}
19
20pub type PyTracebackRef = PyRef<PyTraceback>;
21
22impl PyPayload for PyTraceback {
23 #[inline]
24 fn class(ctx: &Context) -> &'static Py<PyType> {
25 ctx.types.traceback_type
26 }
27}
28
29#[pyclass(with(Constructor))]
30impl PyTraceback {
31 pub const fn new(
32 next: Option<PyRef<Self>>,
33 frame: FrameRef,
34 lasti: u32,
35 lineno: OneIndexed,
36 ) -> Self {
37 Self {
38 next: PyMutex::new(next),
39 frame,
40 lasti,
41 lineno,
42 }
43 }
44
45 #[pygetset]
46 fn tb_frame(&self) -> FrameRef {
47 self.frame.clone()
48 }
49
50 #[pygetset]
51 const fn tb_lasti(&self) -> u32 {
52 self.lasti
53 }
54
55 #[pygetset]
56 const fn tb_lineno(&self) -> usize {
57 self.lineno.get()
58 }
59
60 #[pygetset]
61 fn tb_next(&self) -> Option<PyRef<Self>> {
62 self.next.lock().as_ref().cloned()
63 }
64
65 #[pymethod]
66 fn __dir__(&self, vm: &VirtualMachine) -> PyList {
67 PyList::from(
68 ["tb_frame", "tb_next", "tb_lasti", "tb_lineno"]
69 .iter()
70 .map(|&s| vm.ctx.new_str(s).into())
71 .collect::<Vec<_>>(),
72 )
73 }
74
75 #[pygetset(setter)]
76 fn set_tb_next(
77 zelf: &Py<Self>,
78 value: PySetterValue<Option<PyRef<Self>>>,
79 vm: &VirtualMachine,
80 ) -> PyResult<()> {
81 let value = match value {
82 PySetterValue::Assign(v) => v,
83 PySetterValue::Delete => {
84 return Err(vm.new_type_error("can't delete tb_next attribute"));
85 }
86 };
87 if let Some(ref new_next) = value {
88 let mut cursor = new_next.clone();
89 loop {
90 if cursor.is(zelf) {
91 return Err(vm.new_value_error("traceback loop detected"));
92 }
93 let next = cursor.next.lock().clone();
94 match next {
95 Some(n) => cursor = n,
96 None => break,
97 }
98 }
99 }
100 *zelf.next.lock() = value;
101 Ok(())
102 }
103}
104
105impl Constructor for PyTraceback {
106 type Args = (Option<PyRef<Self>>, FrameRef, u32, usize);
107
108 fn py_new(_cls: &Py<PyType>, args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
109 let (next, frame, lasti, lineno) = args;
110 let lineno =
111 OneIndexed::new(lineno).ok_or_else(|| vm.new_value_error("lineno must be positive"))?;
112 Ok(Self::new(next, frame, lasti, lineno))
113 }
114}
115
116impl PyTracebackRef {
117 pub fn iter(&self) -> impl Iterator<Item = Self> {
118 core::iter::successors(Some(self.clone()), |tb| tb.next.lock().clone())
119 }
120}
121
122pub fn init(context: &'static Context) {
123 PyTraceback::extend_class(context, context.types.traceback_type);
124}
125
126#[cfg(feature = "serde")]
127impl serde::Serialize for PyTraceback {
128 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
129 use serde::ser::SerializeStruct;
130
131 let mut struc = s.serialize_struct("PyTraceback", 3)?;
132 struc.serialize_field("name", self.frame.code.obj_name.as_str())?;
133 struc.serialize_field("lineno", &self.lineno.get())?;
134 struc.serialize_field("filename", self.frame.code.source_path().as_str())?;
135 struc.end()
136 }
137}