Format and write a SyntaxError This logic is derived from TracebackException._format_syntax_error The logic has support for `end_offset` to highlight a range in the source code, but it looks like `end_offset` is not used yet when SyntaxErrors are created.
(
&self,
output: &mut W,
exc: &Py<PyBaseException>,
exc_type: &Py<PyType>,
args_repr: &[PyRef<PyStr>],
)
| 175 | /// The logic has support for `end_offset` to highlight a range in the source code, |
| 176 | /// but it looks like `end_offset` is not used yet when SyntaxErrors are created. |
| 177 | fn write_syntaxerror<W: Write>( |
| 178 | &self, |
| 179 | output: &mut W, |
| 180 | exc: &Py<PyBaseException>, |
| 181 | exc_type: &Py<PyType>, |
| 182 | args_repr: &[PyRef<PyStr>], |
| 183 | ) -> Result<(), W::Error> { |
| 184 | let vm = self; |
| 185 | debug_assert!(exc_type.fast_issubclass(vm.ctx.exceptions.syntax_error)); |
| 186 | |
| 187 | let getattr = |attr: &'static str| exc.as_object().get_attr(attr, vm).ok(); |
| 188 | |
| 189 | let maybe_lineno = getattr("lineno").map(|obj| { |
| 190 | obj.str(vm) |
| 191 | .unwrap_or_else(|_| vm.ctx.new_str("<lineno str() failed>")) |
| 192 | }); |
| 193 | let maybe_filename = getattr("filename").and_then(|obj| obj.str(vm).ok()); |
| 194 | |
| 195 | let maybe_text = getattr("text").map(|obj| { |
| 196 | obj.str(vm) |
| 197 | .unwrap_or_else(|_| vm.ctx.new_str("<text str() failed>")) |
| 198 | }); |
| 199 | |
| 200 | let mut filename_suffix = String::new(); |
| 201 | |
| 202 | if let Some(lineno) = maybe_lineno { |
| 203 | let filename = match maybe_filename { |
| 204 | Some(filename) => filename, |
| 205 | None => vm.ctx.new_str("<string>"), |
| 206 | }; |
| 207 | writeln!(output, r##" File "{filename}", line {lineno}"##,)?; |
| 208 | } else if let Some(filename) = maybe_filename { |
| 209 | filename_suffix = format!(" ({filename})"); |
| 210 | } |
| 211 | |
| 212 | if let Some(text) = maybe_text { |
| 213 | // if text ends with \n or \r\n, remove it |
| 214 | use rustpython_common::wtf8::CodePoint; |
| 215 | let text_wtf8 = text.as_wtf8(); |
| 216 | let r_text = text_wtf8.trim_end_matches(|cp: CodePoint| { |
| 217 | cp == CodePoint::from_char('\n') || cp == CodePoint::from_char('\r') |
| 218 | }); |
| 219 | let l_text = r_text.trim_start_matches(|cp: CodePoint| { |
| 220 | cp == CodePoint::from_char(' ') |
| 221 | || cp == CodePoint::from_char('\n') |
| 222 | || cp == CodePoint::from_char('\x0c') // \f |
| 223 | }); |
| 224 | let spaces = (r_text.len() - l_text.len()) as isize; |
| 225 | |
| 226 | writeln!(output, " {l_text}")?; |
| 227 | |
| 228 | let maybe_offset: Option<isize> = |
| 229 | getattr("offset").and_then(|obj| obj.try_to_value::<isize>(vm).ok()); |
| 230 | |
| 231 | if let Some(offset) = maybe_offset { |
| 232 | let maybe_end_offset: Option<isize> = |
| 233 | getattr("end_offset").and_then(|obj| obj.try_to_value::<isize>(vm).ok()); |
| 234 | let maybe_end_lineno: Option<isize> = |
no test coverage detected