Skip to content

Commit f074493

Browse files
Fix main.py reference and line number in PyO3 stacktrace
1 parent 6a18208 commit f074493

File tree

1 file changed

+81
-9
lines changed

1 file changed

+81
-9
lines changed

src/error.rs

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,92 @@ impl From<rustpython_vm::PyRef<rustpython_vm::builtins::PyBaseException>> for Er
6363
#[cfg(feature = "pyo3")]
6464
impl From<PyErr> for Error {
6565
fn from(error: PyErr) -> Self {
66-
let error_msg = match pyo3::Python::with_gil(|py| {
66+
let error_msg = match pyo3::Python::with_gil(|py| -> Result<Vec<String>> {
6767
let traceback_module = py.import("traceback")?;
6868
let traceback_object = error
6969
.traceback(py)
7070
.ok_or(pyo3::exceptions::PyWarning::new_err("No traceback found."))?;
71-
let format_traceback = traceback_module.getattr("format_tb")?;
72-
format_traceback
73-
.call1((traceback_object,))
74-
.and_then(|r| r.extract::<Vec<String>>())
71+
let extract_traceback = traceback_module.getattr("extract_tb")?;
72+
73+
// Get the formatted traceback lines
74+
let result = extract_traceback.call1((traceback_object,)).and_then(|r| {
75+
match r.extract::<Vec<PyObject>>() {
76+
Ok(v) => {
77+
let mut formatted_lines = Vec::new();
78+
for arg in v.iter() {
79+
let frame = arg.bind(py);
80+
81+
// Extract filename
82+
let filename = match frame.getattr("filename") {
83+
Ok(f) => match f.extract::<String>() {
84+
Ok(s) if s == "<string>".to_string() => {
85+
// Special handling for <string>
86+
frame.setattr("filename", "main.py")?;
87+
let lineno = frame.getattr("lineno")?.extract::<usize>()?;
88+
frame.setattr("lineno", lineno - 2)?;
89+
"main.py".to_string()
90+
}
91+
Ok(s) => s,
92+
Err(_) => "<unknown>".to_string(),
93+
},
94+
Err(_) => "<unknown>".to_string(),
95+
};
96+
97+
// Extract line number
98+
let lineno = match frame.getattr("lineno") {
99+
Ok(l) => match l.extract::<usize>() {
100+
Ok(n) => n,
101+
Err(_) => 0,
102+
},
103+
Err(_) => 0,
104+
};
105+
106+
// Extract function name
107+
let name = match frame.getattr("name") {
108+
Ok(n) => match n.extract::<String>() {
109+
Ok(s) => s,
110+
Err(_) => "<unknown>".to_string(),
111+
},
112+
Err(_) => "<unknown>".to_string(),
113+
};
114+
115+
// Extract line content (if available)
116+
let line = match frame.getattr("line") {
117+
Ok(l) => match l.extract::<Option<String>>() {
118+
Ok(Some(s)) => format!("\t{}", s),
119+
_ => "".to_string(),
120+
},
121+
Err(_) => "".to_string(),
122+
};
123+
124+
// Format the line like requested
125+
let formatted_line = format!(
126+
"File \"{}\", line {}, in {}\n{}",
127+
filename, lineno, name, line
128+
);
129+
130+
formatted_lines.push(formatted_line);
131+
}
132+
133+
Ok(formatted_lines)
134+
}
135+
Err(_) => Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
136+
"Failed to extract traceback",
137+
)),
138+
}
139+
})?;
140+
141+
// Add traceback header
142+
let mut full_traceback = vec!["Traceback (most recent call last):".to_string()];
143+
full_traceback.extend(result);
144+
145+
// Add error type and message
146+
full_traceback.push(error.to_string());
147+
148+
Ok(full_traceback)
75149
}) {
76-
Ok(formatted) => formatted.join(""),
77-
Err(_) => {
78-
error.to_string() // Fall back to simple error message
79-
}
150+
Ok(formatted) => formatted.join("\n"),
151+
Err(_) => error.to_string(), // Fall back to simple error message
80152
};
81153

82154
Error::String(error_msg)

0 commit comments

Comments
 (0)