Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/IPython/core/ultratb.py: 16%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

467 statements  

1""" 

2Verbose and colourful traceback formatting. 

3 

4**ColorTB** 

5 

6I've always found it a bit hard to visually parse tracebacks in Python. The 

7ColorTB class is a solution to that problem. It colors the different parts of a 

8traceback in a manner similar to what you would expect from a syntax-highlighting 

9text editor. 

10 

11Installation instructions for ColorTB:: 

12 

13 import sys,ultratb 

14 sys.excepthook = ultratb.ColorTB() 

15 

16**VerboseTB** 

17 

18I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds 

19of useful info when a traceback occurs. Ping originally had it spit out HTML 

20and intended it for CGI programmers, but why should they have all the fun? I 

21altered it to spit out colored text to the terminal. It's a bit overwhelming, 

22but kind of neat, and maybe useful for long-running programs that you believe 

23are bug-free. If a crash *does* occur in that type of program you want details. 

24Give it a shot--you'll love it or you'll hate it. 

25 

26.. note:: 

27 

28 The Verbose mode prints the variables currently visible where the exception 

29 happened (shortening their strings if too long). This can potentially be 

30 very slow, if you happen to have a huge data structure whose string 

31 representation is complex to compute. Your computer may appear to freeze for 

32 a while with cpu usage at 100%. If this occurs, you can cancel the traceback 

33 with Ctrl-C (maybe hitting it more than once). 

34 

35 If you encounter this kind of situation often, you may want to use the 

36 Verbose_novars mode instead of the regular Verbose, which avoids formatting 

37 variables (but otherwise includes the information and context given by 

38 Verbose). 

39 

40.. note:: 

41 

42 The verbose mode print all variables in the stack, which means it can 

43 potentially leak sensitive information like access keys, or unencrypted 

44 password. 

45 

46Installation instructions for VerboseTB:: 

47 

48 import sys,ultratb 

49 sys.excepthook = ultratb.VerboseTB() 

50 

51Note: Much of the code in this module was lifted verbatim from the standard 

52library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'. 

53 

54 

55Inheritance diagram: 

56 

57.. inheritance-diagram:: IPython.core.ultratb 

58 :parts: 3 

59""" 

60 

61# ***************************************************************************** 

62# Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu> 

63# Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu> 

64# 

65# Distributed under the terms of the BSD License. The full license is in 

66# the file COPYING, distributed as part of this software. 

67# ***************************************************************************** 

68 

69import functools 

70import inspect 

71import linecache 

72import sys 

73import time 

74import traceback 

75import types 

76import warnings 

77from collections.abc import Sequence 

78from types import TracebackType 

79from typing import Any, List, Optional, Tuple 

80from collections.abc import Callable 

81 

82import stack_data 

83from pygments.formatters.terminal256 import Terminal256Formatter 

84from pygments.token import Token 

85 

86from IPython import get_ipython 

87from IPython.utils import path as util_path 

88from IPython.utils import py3compat 

89from IPython.utils.PyColorize import Parser, Theme, TokenStream, theme_table 

90from IPython.utils.terminal import get_terminal_size 

91 

92from .display_trap import DisplayTrap 

93from .doctb import DocTB 

94from .tbtools import ( 

95 FrameInfo, 

96 TBTools, 

97 _format_traceback_lines, 

98 _safe_string, 

99 _simple_format_traceback_lines, 

100 _tokens_filename, 

101 eqrepr, 

102 get_line_number_of_frame, 

103 nullrepr, 

104 text_repr, 

105) 

106 

107# Globals 

108# amount of space to put line numbers before verbose tracebacks 

109INDENT_SIZE = 8 

110 

111# When files are too long do not use stackdata to get frames. 

112# it is too long. 

113FAST_THRESHOLD = 10_000 

114 

115# --------------------------------------------------------------------------- 

116class ListTB(TBTools): 

117 """Print traceback information from a traceback list, with optional color. 

118 

119 Calling requires 3 arguments: (etype, evalue, elist) 

120 as would be obtained by:: 

121 

122 etype, evalue, tb = sys.exc_info() 

123 if tb: 

124 elist = traceback.extract_tb(tb) 

125 else: 

126 elist = None 

127 

128 It can thus be used by programs which need to process the traceback before 

129 printing (such as console replacements based on the code module from the 

130 standard library). 

131 

132 Because they are meant to be called without a full traceback (only a 

133 list), instances of this class can't call the interactive pdb debugger.""" 

134 

135 def __call__( 

136 self, 

137 etype: type[BaseException], 

138 evalue: BaseException | None, 

139 etb: TracebackType | None, 

140 ) -> None: 

141 self.ostream.flush() 

142 self.ostream.write(self.text(etype, evalue, etb)) 

143 self.ostream.write("\n") 

144 

145 def _extract_tb(self, tb: TracebackType | None) -> traceback.StackSummary | None: 

146 if tb: 

147 return traceback.extract_tb(tb) 

148 else: 

149 return None 

150 

151 def structured_traceback( 

152 self, 

153 etype: type, 

154 evalue: Optional[BaseException], 

155 etb: Optional[TracebackType] = None, 

156 tb_offset: Optional[int] = None, 

157 context: int = 5, 

158 ) -> list[str]: 

159 """Return a color formatted string with the traceback info. 

160 

161 Parameters 

162 ---------- 

163 etype : exception type 

164 Type of the exception raised. 

165 evalue : object 

166 Data stored in the exception 

167 etb : list | TracebackType | None 

168 If list: List of frames, see class docstring for details. 

169 If Traceback: Traceback of the exception. 

170 tb_offset : int, optional 

171 Number of frames in the traceback to skip. If not given, the 

172 instance evalue is used (set in constructor). 

173 context : int, optional 

174 Number of lines of context information to print. 

175 

176 Returns 

177 ------- 

178 String with formatted exception. 

179 """ 

180 # This is a workaround to get chained_exc_ids in recursive calls 

181 # etb should not be a tuple if structured_traceback is not recursive 

182 if isinstance(etb, tuple): 

183 etb, chained_exc_ids = etb 

184 else: 

185 chained_exc_ids = set() 

186 elist: list[Any] 

187 if isinstance(etb, list): 

188 elist = etb 

189 elif etb is not None: 

190 elist = self._extract_tb(etb) # type: ignore[assignment] 

191 else: 

192 elist = [] 

193 tb_offset = self.tb_offset if tb_offset is None else tb_offset 

194 assert isinstance(tb_offset, int) 

195 out_list: list[str] = [] 

196 if elist: 

197 if tb_offset and len(elist) > tb_offset: 

198 elist = elist[tb_offset:] 

199 

200 out_list.append( 

201 theme_table[self._theme_name].format( 

202 [ 

203 (Token, "Traceback"), 

204 (Token, " "), 

205 (Token.NormalEm, "(most recent call last)"), 

206 (Token, ":"), 

207 (Token, "\n"), 

208 ] 

209 ), 

210 ) 

211 out_list.extend(self._format_list(elist)) 

212 # The exception info should be a single entry in the list. 

213 lines = "".join(self._format_exception_only(etype, evalue)) 

214 out_list.append(lines) 

215 

216 # Find chained exceptions if we have a traceback (not for exception-only mode) 

217 if etb is not None: 

218 exception = self.get_parts_of_chained_exception(evalue) 

219 

220 if exception and (id(exception[1]) not in chained_exc_ids): 

221 chained_exception_message: list[str] = ( 

222 self.prepare_chained_exception_message(evalue.__cause__)[0] 

223 if evalue is not None 

224 else [""] 

225 ) 

226 etype, evalue, etb = exception 

227 # Trace exception to avoid infinite 'cause' loop 

228 chained_exc_ids.add(id(exception[1])) 

229 chained_exceptions_tb_offset = 0 

230 ol1 = self.structured_traceback( 

231 etype, 

232 evalue, 

233 (etb, chained_exc_ids), # type: ignore[arg-type] 

234 chained_exceptions_tb_offset, 

235 context, 

236 ) 

237 ol2 = chained_exception_message 

238 

239 out_list = ol1 + ol2 + out_list 

240 

241 return out_list 

242 

243 def _format_list(self, extracted_list: list[Any]) -> list[str]: 

244 """Format a list of traceback entry tuples for printing. 

245 

246 Given a list of tuples as returned by extract_tb() or 

247 extract_stack(), return a list of strings ready for printing. 

248 Each string in the resulting list corresponds to the item with the 

249 same index in the argument list. Each string ends in a newline; 

250 the strings may contain internal newlines as well, for those items 

251 whose source text line is not None. 

252 

253 Lifted almost verbatim from traceback.py 

254 """ 

255 

256 output_list = [] 

257 for ind, (filename, lineno, name, line) in enumerate(extracted_list): 

258 # Will emphasize the last entry 

259 em = True if ind == len(extracted_list) - 1 else False 

260 

261 item = theme_table[self._theme_name].format( 

262 [(Token.NormalEm if em else Token.Normal, " ")] 

263 + _tokens_filename(em, filename, lineno=lineno) 

264 ) 

265 

266 # This seem to be only in xmode plain (%run sinpleer), investigate why not share with verbose. 

267 # look at _tokens_filename in forma_record. 

268 if name != "<module>": 

269 item += theme_table[self._theme_name].format( 

270 [ 

271 (Token.NormalEm if em else Token.Normal, " in "), 

272 (Token.TB.NameEm if em else Token.TB.Name, name), 

273 ] 

274 ) 

275 item += theme_table[self._theme_name].format( 

276 [(Token.NormalEm if em else Token, "\n")] 

277 ) 

278 if line: 

279 item += theme_table[self._theme_name].format( 

280 [ 

281 (Token.Line if em else Token, " "), 

282 (Token.Line if em else Token, line.strip()), 

283 (Token, "\n"), 

284 ] 

285 ) 

286 output_list.append(item) 

287 

288 return output_list 

289 

290 def _format_exception_only( 

291 self, etype: type[BaseException], value: BaseException | None 

292 ) -> list[str]: 

293 """Format the exception part of a traceback. 

294 

295 The arguments are the exception type and value such as given by 

296 sys.exc_info()[:2]. The return value is a list of strings, each ending 

297 in a newline. Normally, the list contains a single string; however, 

298 for SyntaxError exceptions, it contains several lines that (when 

299 printed) display detailed information about where the syntax error 

300 occurred. The message indicating which exception occurred is the 

301 always last string in the list. 

302 

303 Also lifted nearly verbatim from traceback.py 

304 """ 

305 have_filedata = False 

306 output_list = [] 

307 stype_tokens = [(Token.ExcName, etype.__name__)] 

308 stype: str = theme_table[self._theme_name].format(stype_tokens) 

309 if value is None: 

310 # Not sure if this can still happen in Python 2.6 and above 

311 output_list.append(stype + "\n") 

312 else: 

313 if issubclass(etype, SyntaxError): 

314 assert hasattr(value, "filename") 

315 assert hasattr(value, "lineno") 

316 assert hasattr(value, "text") 

317 assert hasattr(value, "offset") 

318 assert hasattr(value, "msg") 

319 have_filedata = True 

320 if not value.filename: 

321 value.filename = "<string>" 

322 if value.lineno: 

323 lineno = value.lineno 

324 textline = linecache.getline(value.filename, value.lineno) 

325 else: 

326 lineno = "unknown" 

327 textline = "" 

328 output_list.append( 

329 theme_table[self._theme_name].format( 

330 [(Token, " ")] 

331 + _tokens_filename( 

332 True, 

333 value.filename, 

334 lineno=(None if lineno == "unknown" else lineno), 

335 ) 

336 + [(Token, "\n")] 

337 ) 

338 ) 

339 if textline == "": 

340 # sep 2025: 

341 # textline = py3compat.cast_unicode(value.text, "utf-8") 

342 if value.text is None: 

343 textline = "" 

344 else: 

345 assert isinstance(value.text, str) 

346 textline = value.text 

347 

348 if textline is not None: 

349 i = 0 

350 while i < len(textline) and textline[i].isspace(): 

351 i += 1 

352 output_list.append( 

353 theme_table[self._theme_name].format( 

354 [ 

355 (Token.Line, " "), 

356 (Token.Line, textline.strip()), 

357 (Token, "\n"), 

358 ] 

359 ) 

360 ) 

361 if value.offset is not None: 

362 s = " " 

363 for c in textline[i : value.offset - 1]: 

364 if c.isspace(): 

365 s += c 

366 else: 

367 s += " " 

368 output_list.append( 

369 theme_table[self._theme_name].format( 

370 [(Token.Caret, s + "^"), (Token, "\n")] 

371 ) 

372 ) 

373 s = value.msg 

374 else: 

375 s = self._some_str(value) 

376 if s: 

377 output_list.append( 

378 theme_table[self._theme_name].format( 

379 stype_tokens 

380 + [ 

381 (Token.ExcName, ":"), 

382 (Token, " "), 

383 (Token, s), 

384 (Token, "\n"), 

385 ] 

386 ) 

387 ) 

388 else: 

389 output_list.append("%s\n" % stype) 

390 

391 # PEP-678 notes 

392 output_list.extend(f"{x}\n" for x in getattr(value, "__notes__", [])) 

393 

394 # sync with user hooks 

395 if have_filedata: 

396 ipinst = get_ipython() 

397 if ipinst is not None: 

398 assert value is not None 

399 assert hasattr(value, "lineno") 

400 assert hasattr(value, "filename") 

401 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0) 

402 

403 return output_list 

404 

405 def get_exception_only(self, etype, value): 

406 """Only print the exception type and message, without a traceback. 

407 

408 Parameters 

409 ---------- 

410 etype : exception type 

411 value : exception value 

412 """ 

413 return ListTB.structured_traceback(self, etype, value) 

414 

415 def show_exception_only( 

416 self, etype: BaseException | None, evalue: TracebackType | None 

417 ) -> None: 

418 """Only print the exception type and message, without a traceback. 

419 

420 Parameters 

421 ---------- 

422 etype : exception type 

423 evalue : exception value 

424 """ 

425 # This method needs to use __call__ from *this* class, not the one from 

426 # a subclass whose signature or behavior may be different 

427 ostream = self.ostream 

428 ostream.flush() 

429 ostream.write("\n".join(self.get_exception_only(etype, evalue))) 

430 ostream.flush() 

431 

432 def _some_str(self, value: Any) -> str: 

433 # Lifted from traceback.py 

434 try: 

435 return str(value) 

436 except: 

437 return "<unprintable %s object>" % type(value).__name__ 

438 

439 

440_sentinel = object() 

441_default = "default" 

442 

443 

444# ---------------------------------------------------------------------------- 

445class VerboseTB(TBTools): 

446 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead 

447 of HTML. Requires inspect and pydoc. Crazy, man. 

448 

449 Modified version which optionally strips the topmost entries from the 

450 traceback, to be used with alternate interpreters (because their own code 

451 would appear in the traceback).""" 

452 

453 tb_highlight = "bg:ansiyellow" 

454 tb_highlight_style = "default" 

455 

456 _mode: str 

457 

458 def __init__( 

459 self, 

460 # TODO: no default ? 

461 theme_name: str = _default, 

462 call_pdb: bool = False, 

463 ostream: Any = None, 

464 tb_offset: int = 0, 

465 long_header: bool = False, 

466 include_vars: bool = True, 

467 check_cache: Callable[[], None] | None = None, 

468 debugger_cls: type | None = None, 

469 *, 

470 color_scheme: Any = _sentinel, 

471 ): 

472 """Specify traceback offset, headers and color scheme. 

473 

474 Define how many frames to drop from the tracebacks. Calling it with 

475 tb_offset=1 allows use of this handler in interpreters which will have 

476 their own code at the top of the traceback (VerboseTB will first 

477 remove that frame before printing the traceback info).""" 

478 if color_scheme is not _sentinel: 

479 assert isinstance(color_scheme, str) 

480 theme_name = color_scheme.lower() 

481 

482 warnings.warn( 

483 "color_scheme is deprecated as of IPython 9.0 and replaced by " 

484 "theme_name (which should be lowercase). As you passed a " 

485 "color_scheme value I will try to see if I have corresponding " 

486 "theme.", 

487 stacklevel=2, 

488 category=DeprecationWarning, 

489 ) 

490 

491 if theme_name != _default: 

492 warnings.warn( 

493 "You passed both `theme_name` and `color_scheme` " 

494 "(deprecated) to VerboseTB constructor. `theme_name` will " 

495 "be ignored for the time being.", 

496 stacklevel=2, 

497 category=DeprecationWarning, 

498 ) 

499 

500 if theme_name == _default: 

501 theme_name = "linux" 

502 

503 assert isinstance(theme_name, str) 

504 super().__init__( 

505 theme_name=theme_name, 

506 call_pdb=call_pdb, 

507 ostream=ostream, 

508 debugger_cls=debugger_cls, 

509 ) 

510 self.tb_offset = tb_offset 

511 self.long_header = long_header 

512 self.include_vars = include_vars 

513 # By default we use linecache.checkcache, but the user can provide a 

514 # different check_cache implementation. This was formerly used by the 

515 # IPython kernel for interactive code, but is no longer necessary. 

516 if check_cache is None: 

517 check_cache = linecache.checkcache 

518 self.check_cache = check_cache 

519 

520 self.skip_hidden = True 

521 

522 def format_record(self, frame_info: FrameInfo) -> str: 

523 """Format a single stack frame""" 

524 assert isinstance(frame_info, FrameInfo) 

525 

526 if isinstance(frame_info._sd, stack_data.RepeatedFrames): 

527 return theme_table[self._theme_name].format( 

528 [ 

529 (Token, " "), 

530 ( 

531 Token.ExcName, 

532 "[... skipping similar frames: %s]" % frame_info.description, 

533 ), 

534 (Token, "\n"), 

535 ] 

536 ) 

537 

538 indent: str = " " * INDENT_SIZE 

539 

540 assert isinstance(frame_info.lineno, int) 

541 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame) 

542 if frame_info.executing is not None: 

543 func = frame_info.executing.code_qualname() 

544 else: 

545 func = "?" 

546 if func == "<module>": 

547 call = "" 

548 else: 

549 # Decide whether to include variable details or not 

550 var_repr = eqrepr if self.include_vars else nullrepr 

551 try: 

552 scope = inspect.formatargvalues( 

553 args, varargs, varkw, locals_, formatvalue=var_repr 

554 ) 

555 assert isinstance(scope, str) 

556 call = theme_table[self._theme_name].format( 

557 [(Token, "in "), (Token.VName, func), (Token.ValEm, scope)] 

558 ) 

559 except KeyError: 

560 # This happens in situations like errors inside generator 

561 # expressions, where local variables are listed in the 

562 # line, but can't be extracted from the frame. I'm not 

563 # 100% sure this isn't actually a bug in inspect itself, 

564 # but since there's no info for us to compute with, the 

565 # best we can do is report the failure and move on. Here 

566 # we must *not* call any traceback construction again, 

567 # because that would mess up use of %debug later on. So we 

568 # simply report the failure and move on. The only 

569 # limitation will be that this frame won't have locals 

570 # listed in the call signature. Quite subtle problem... 

571 # I can't think of a good way to validate this in a unit 

572 # test, but running a script consisting of: 

573 # dict( (k,v.strip()) for (k,v) in range(10) ) 

574 # will illustrate the error, if this exception catch is 

575 # disabled. 

576 call = theme_table[self._theme_name].format( 

577 [ 

578 (Token, "in "), 

579 (Token.VName, func), 

580 (Token.ValEm, "(***failed resolving arguments***)"), 

581 ] 

582 ) 

583 

584 lvals_toks: list[TokenStream] = [] 

585 if self.include_vars: 

586 try: 

587 # we likely want to fix stackdata at some point, but 

588 # still need a workaround. 

589 fibp = frame_info.variables_in_executing_piece 

590 for var in fibp: 

591 lvals_toks.append( 

592 [ 

593 (Token, var.name), 

594 (Token, " "), 

595 (Token.ValEm, "= "), 

596 (Token.ValEm, repr(var.value)), 

597 ] 

598 ) 

599 except Exception: 

600 lvals_toks.append( 

601 [ 

602 ( 

603 Token, 

604 "Exception trying to inspect frame. No more locals available.", 

605 ), 

606 ] 

607 ) 

608 

609 if frame_info._sd is None: 

610 # fast fallback if file is too long 

611 assert frame_info.filename is not None 

612 level_tokens = [ 

613 (Token.FilenameEm, util_path.compress_user(frame_info.filename)), 

614 (Token, " "), 

615 (Token, call), 

616 (Token, "\n"), 

617 ] 

618 

619 _line_format = Parser(theme_name=self._theme_name).format2 

620 assert isinstance(frame_info.code, types.CodeType) 

621 first_line: int = frame_info.code.co_firstlineno 

622 current_line: int = frame_info.lineno 

623 raw_lines: list[str] = frame_info.raw_lines 

624 index: int = current_line - first_line 

625 assert frame_info.context is not None 

626 if index >= frame_info.context: 

627 start = max(index - frame_info.context, 0) 

628 stop = index + frame_info.context 

629 index = frame_info.context 

630 else: 

631 start = 0 

632 stop = index + frame_info.context 

633 raw_lines = raw_lines[start:stop] 

634 

635 # Jan 2025: may need _line_format(py3ompat.cast_unicode(s)) 

636 raw_color_err = [] 

637 for s in raw_lines: 

638 formatted, is_error = _line_format(s, "str") 

639 assert formatted is not None, "format2 should return str when out='str'" 

640 raw_color_err.append((s, (formatted, is_error))) 

641 

642 tb_tokens = _simple_format_traceback_lines( 

643 current_line, 

644 index, 

645 raw_color_err, 

646 lvals_toks, 

647 theme=theme_table[self._theme_name], 

648 ) 

649 _tb_lines: str = theme_table[self._theme_name].format(tb_tokens) 

650 

651 return theme_table[self._theme_name].format(level_tokens + tb_tokens) 

652 else: 

653 result = theme_table[self._theme_name].format( 

654 _tokens_filename(True, frame_info.filename, lineno=frame_info.lineno) 

655 ) 

656 result += ", " if call else "" 

657 result += f"{call}\n" 

658 result += theme_table[self._theme_name].format( 

659 _format_traceback_lines( 

660 frame_info.lines, 

661 theme_table[self._theme_name], 

662 self.has_colors, 

663 lvals_toks, 

664 ) 

665 ) 

666 return result 

667 

668 def prepare_header(self, etype: str, long_version: bool = False) -> str: 

669 width = min(75, get_terminal_size()[0]) 

670 if long_version: 

671 # Header with the exception type, python version, and date 

672 pyver = "Python " + sys.version.split()[0] + ": " + sys.executable 

673 date = time.ctime(time.time()) 

674 theme = theme_table[self._theme_name] 

675 head = theme.format( 

676 [ 

677 (Token.Topline, theme.symbols["top_line"] * width), 

678 (Token, "\n"), 

679 (Token.ExcName, etype), 

680 (Token, " " * (width - len(etype) - len(pyver))), 

681 (Token, pyver), 

682 (Token, "\n"), 

683 (Token, date.rjust(width)), 

684 ] 

685 ) 

686 head += ( 

687 "\nA problem occurred executing Python code. Here is the sequence of function" 

688 "\ncalls leading up to the error, with the most recent (innermost) call last." 

689 ) 

690 else: 

691 # Simplified header 

692 head = theme_table[self._theme_name].format( 

693 [ 

694 (Token.ExcName, etype), 

695 ( 

696 Token, 

697 "Traceback (most recent call last)".rjust(width - len(etype)), 

698 ), 

699 ] 

700 ) 

701 

702 return head 

703 

704 def format_exception(self, etype, evalue): 

705 # Get (safely) a string form of the exception info 

706 try: 

707 etype_str, evalue_str = map(str, (etype, evalue)) 

708 except: 

709 # User exception is improperly defined. 

710 etype, evalue = str, sys.exc_info()[:2] 

711 etype_str, evalue_str = map(str, (etype, evalue)) 

712 

713 # PEP-678 notes 

714 notes = getattr(evalue, "__notes__", []) 

715 if not isinstance(notes, Sequence) or isinstance(notes, (str, bytes)): 

716 notes = [_safe_string(notes, "__notes__", func=repr)] 

717 

718 for note in notes: 

719 assert isinstance(note, str) 

720 

721 str_notes: Sequence[str] = notes 

722 

723 # ... and format it 

724 return [ 

725 theme_table[self._theme_name].format( 

726 [(Token.ExcName, etype_str), (Token, ": "), (Token, evalue_str)] 

727 ), 

728 *( 

729 theme_table[self._theme_name].format([(Token, note)]) 

730 for note in str_notes 

731 ), 

732 ] 

733 

734 def format_exception_as_a_whole( 

735 self, 

736 etype: type, 

737 evalue: Optional[BaseException], 

738 etb: Optional[TracebackType], 

739 context: int, 

740 tb_offset: Optional[int], 

741 ) -> list[list[str]]: 

742 """Formats the header, traceback and exception message for a single exception. 

743 

744 This may be called multiple times by Python 3 exception chaining 

745 (PEP 3134). 

746 """ 

747 # some locals 

748 orig_etype = etype 

749 try: 

750 etype = etype.__name__ # type: ignore[assignment] 

751 except AttributeError: 

752 pass 

753 

754 tb_offset = self.tb_offset if tb_offset is None else tb_offset 

755 assert isinstance(tb_offset, int) 

756 head = self.prepare_header(str(etype), self.long_header) 

757 records = self.get_records(etb, context, tb_offset) if etb else [] 

758 

759 frames = [] 

760 skipped = 0 

761 lastrecord = len(records) - 1 

762 for i, record in enumerate(records): 

763 if ( 

764 not isinstance(record._sd, stack_data.RepeatedFrames) 

765 and self.skip_hidden 

766 ): 

767 if ( 

768 record.frame.f_locals.get("__tracebackhide__", 0) 

769 and i != lastrecord 

770 ): 

771 skipped += 1 

772 continue 

773 if skipped: 

774 frames.append( 

775 theme_table[self._theme_name].format( 

776 [ 

777 (Token, " "), 

778 (Token.ExcName, "[... skipping hidden %s frame]" % skipped), 

779 (Token, "\n"), 

780 ] 

781 ) 

782 ) 

783 skipped = 0 

784 frames.append(self.format_record(record)) 

785 if skipped: 

786 frames.append( 

787 theme_table[self._theme_name].format( 

788 [ 

789 (Token, " "), 

790 (Token.ExcName, "[... skipping hidden %s frame]" % skipped), 

791 (Token, "\n"), 

792 ] 

793 ) 

794 ) 

795 

796 formatted_exception = self.format_exception(etype, evalue) 

797 if records: 

798 frame_info = records[-1] 

799 ipinst = get_ipython() 

800 if ipinst is not None: 

801 ipinst.hooks.synchronize_with_editor( 

802 frame_info.filename, frame_info.lineno, 0 

803 ) 

804 

805 return [[head] + frames + formatted_exception] 

806 

807 def get_records(self, etb: TracebackType, context: int, tb_offset: int) -> Any: 

808 assert etb is not None 

809 context = context - 1 

810 after = context // 2 

811 before = context - after 

812 if self.has_colors: 

813 base_style = theme_table[self._theme_name].as_pygments_style() 

814 style = stack_data.style_with_executing_node(base_style, self.tb_highlight) 

815 formatter = Terminal256Formatter(style=style) 

816 else: 

817 formatter = None 

818 options = stack_data.Options( 

819 before=before, 

820 after=after, 

821 pygments_formatter=formatter, 

822 ) 

823 

824 # Let's estimate the amount of code we will have to parse/highlight. 

825 cf: Optional[TracebackType] = etb 

826 max_len = 0 

827 tbs = [] 

828 while cf is not None: 

829 try: 

830 mod = inspect.getmodule(cf.tb_frame) 

831 if mod is not None: 

832 mod_name = mod.__name__ 

833 root_name, *_ = mod_name.split(".") 

834 if root_name == "IPython": 

835 cf = cf.tb_next 

836 continue 

837 max_len = get_line_number_of_frame(cf.tb_frame) 

838 

839 except OSError: 

840 max_len = 0 

841 max_len = max(max_len, max_len) 

842 tbs.append(cf) 

843 cf = getattr(cf, "tb_next", None) 

844 

845 if max_len > FAST_THRESHOLD: 

846 FIs: list[FrameInfo] = [] 

847 for tb in tbs: 

848 frame = tb.tb_frame # type: ignore[union-attr] 

849 lineno = frame.f_lineno 

850 code = frame.f_code 

851 filename = code.co_filename 

852 # TODO: Here we need to use before/after/ 

853 FIs.append( 

854 FrameInfo( 

855 "Raw frame", filename, lineno, frame, code, context=context 

856 ) 

857 ) 

858 return FIs 

859 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:] 

860 res2 = [FrameInfo._from_stack_data_FrameInfo(r) for r in res] 

861 return res2 

862 

863 def structured_traceback( 

864 self, 

865 etype: type, 

866 evalue: Optional[BaseException], 

867 etb: Optional[TracebackType] = None, 

868 tb_offset: Optional[int] = None, 

869 context: int = 5, 

870 ) -> list[str]: 

871 """Return a nice text document describing the traceback.""" 

872 formatted_exceptions: list[list[str]] = self.format_exception_as_a_whole( 

873 etype, evalue, etb, context, tb_offset 

874 ) 

875 

876 termsize = min(75, get_terminal_size()[0]) 

877 theme = theme_table[self._theme_name] 

878 head: str = theme.format( 

879 [ 

880 ( 

881 Token.Topline, 

882 theme.symbols["top_line"] * termsize, 

883 ), 

884 ] 

885 ) 

886 structured_traceback_parts: list[str] = [head] 

887 chained_exceptions_tb_offset = 0 

888 lines_of_context = 3 

889 exception = self.get_parts_of_chained_exception(evalue) 

890 if exception: 

891 assert evalue is not None 

892 formatted_exceptions += self.prepare_chained_exception_message( 

893 evalue.__cause__ 

894 ) 

895 etype, evalue, etb = exception 

896 else: 

897 evalue = None 

898 chained_exc_ids = set() 

899 while evalue: 

900 formatted_exceptions += self.format_exception_as_a_whole( 

901 etype, evalue, etb, lines_of_context, chained_exceptions_tb_offset 

902 ) 

903 exception = self.get_parts_of_chained_exception(evalue) 

904 

905 if exception and id(exception[1]) not in chained_exc_ids: 

906 chained_exc_ids.add( 

907 id(exception[1]) 

908 ) # trace exception to avoid infinite 'cause' loop 

909 formatted_exceptions += self.prepare_chained_exception_message( 

910 evalue.__cause__ 

911 ) 

912 etype, evalue, etb = exception 

913 else: 

914 evalue = None 

915 

916 # we want to see exceptions in a reversed order: 

917 # the first exception should be on top 

918 for fx in reversed(formatted_exceptions): 

919 structured_traceback_parts += fx 

920 

921 return structured_traceback_parts 

922 

923 def debugger(self, force: bool = False) -> None: 

924 """Call up the pdb debugger if desired, always clean up the tb 

925 reference. 

926 

927 Keywords: 

928 

929 - force(False): by default, this routine checks the instance call_pdb 

930 flag and does not actually invoke the debugger if the flag is false. 

931 The 'force' option forces the debugger to activate even if the flag 

932 is false. 

933 

934 If the call_pdb flag is set, the pdb interactive debugger is 

935 invoked. In all cases, the self.tb reference to the current traceback 

936 is deleted to prevent lingering references which hamper memory 

937 management. 

938 

939 Note that each call to pdb() does an 'import readline', so if your app 

940 requires a special setup for the readline completers, you'll have to 

941 fix that by hand after invoking the exception handler.""" 

942 

943 if force or self.call_pdb: 

944 if self.pdb is None: 

945 self.pdb = self.debugger_cls() 

946 # the system displayhook may have changed, restore the original 

947 # for pdb 

948 display_trap = DisplayTrap(hook=sys.__displayhook__) 

949 with display_trap: 

950 self.pdb.reset() 

951 # Find the right frame so we don't pop up inside ipython itself 

952 if hasattr(self, "tb") and self.tb is not None: # type: ignore[has-type] 

953 etb = self.tb # type: ignore[has-type] 

954 else: 

955 etb = self.tb = sys.last_traceback 

956 while self.tb is not None and self.tb.tb_next is not None: 

957 assert self.tb.tb_next is not None 

958 self.tb = self.tb.tb_next 

959 if etb and etb.tb_next: 

960 etb = etb.tb_next 

961 self.pdb.botframe = etb.tb_frame 

962 # last_value should be deprecated, but last-exc sometimme not set 

963 # please check why later and remove the getattr. 

964 exc = ( 

965 sys.last_value 

966 if sys.version_info < (3, 12) 

967 else getattr(sys, "last_exc", sys.last_value) 

968 ) # type: ignore[attr-defined] 

969 if exc: 

970 self.pdb.interaction(None, exc) 

971 else: 

972 self.pdb.interaction(None, etb) 

973 

974 if hasattr(self, "tb"): 

975 del self.tb 

976 

977 def handler(self, info=None): 

978 (etype, evalue, etb) = info or sys.exc_info() 

979 self.tb = etb 

980 ostream = self.ostream 

981 ostream.flush() 

982 ostream.write(self.text(etype, evalue, etb)) # type:ignore[arg-type] 

983 ostream.write("\n") 

984 ostream.flush() 

985 

986 # Changed so an instance can just be called as VerboseTB_inst() and print 

987 # out the right info on its own. 

988 def __call__(self, etype=None, evalue=None, etb=None): 

989 """This hook can replace sys.excepthook (for Python 2.1 or higher).""" 

990 if etb is None: 

991 self.handler() 

992 else: 

993 self.handler((etype, evalue, etb)) 

994 try: 

995 self.debugger() 

996 except KeyboardInterrupt: 

997 print("\nKeyboardInterrupt") 

998 

999 

1000# ---------------------------------------------------------------------------- 

1001class FormattedTB(VerboseTB, ListTB): 

1002 """Subclass ListTB but allow calling with a traceback. 

1003 

1004 It can thus be used as a sys.excepthook for Python > 2.1. 

1005 

1006 Also adds 'Context' and 'Verbose' modes, not available in ListTB. 

1007 

1008 Allows a tb_offset to be specified. This is useful for situations where 

1009 one needs to remove a number of topmost frames from the traceback (such as 

1010 occurs with python programs that themselves execute other python code, 

1011 like Python shells).""" 

1012 

1013 mode: str 

1014 

1015 def __init__( 

1016 self, 

1017 mode="Plain", 

1018 # TODO: no default 

1019 theme_name="linux", 

1020 call_pdb=False, 

1021 ostream=None, 

1022 tb_offset=0, 

1023 long_header=False, 

1024 include_vars=False, 

1025 check_cache=None, 

1026 debugger_cls=None, 

1027 ): 

1028 # NEVER change the order of this list. Put new modes at the end: 

1029 self.valid_modes = ["Plain", "Context", "Verbose", "Minimal", "Docs"] 

1030 self.verbose_modes = self.valid_modes[1:3] 

1031 

1032 VerboseTB.__init__( 

1033 self, 

1034 theme_name=theme_name, 

1035 call_pdb=call_pdb, 

1036 ostream=ostream, 

1037 tb_offset=tb_offset, 

1038 long_header=long_header, 

1039 include_vars=include_vars, 

1040 check_cache=check_cache, 

1041 debugger_cls=debugger_cls, 

1042 ) 

1043 

1044 # Different types of tracebacks are joined with different separators to 

1045 # form a single string. They are taken from this dict 

1046 self._join_chars = dict( 

1047 Plain="", Context="\n", Verbose="\n", Minimal="", Docs="" 

1048 ) 

1049 # set_mode also sets the tb_join_char attribute 

1050 self.set_mode(mode) 

1051 

1052 def structured_traceback( 

1053 self, 

1054 etype: type, 

1055 evalue: BaseException | None, 

1056 etb: TracebackType | None = None, 

1057 tb_offset: int | None = None, 

1058 context: int = 5, 

1059 ) -> list[str]: 

1060 tb_offset = self.tb_offset if tb_offset is None else tb_offset 

1061 mode = self.mode 

1062 if mode in self.verbose_modes: 

1063 # Verbose modes need a full traceback 

1064 return VerboseTB.structured_traceback( 

1065 self, etype, evalue, etb, tb_offset, context 

1066 ) 

1067 elif mode == "Docs": 

1068 # return DocTB 

1069 return DocTB( 

1070 theme_name=self._theme_name, 

1071 call_pdb=self.call_pdb, 

1072 ostream=self.ostream, 

1073 tb_offset=tb_offset, 

1074 long_header=self.long_header, 

1075 include_vars=self.include_vars, 

1076 check_cache=self.check_cache, 

1077 debugger_cls=self.debugger_cls, 

1078 ).structured_traceback( 

1079 etype, evalue, etb, tb_offset, 1 

1080 ) # type: ignore[arg-type] 

1081 

1082 elif mode == "Minimal": 

1083 return ListTB.get_exception_only(self, etype, evalue) 

1084 else: 

1085 # We must check the source cache because otherwise we can print 

1086 # out-of-date source code. 

1087 self.check_cache() 

1088 # Now we can extract and format the exception 

1089 return ListTB.structured_traceback( 

1090 self, etype, evalue, etb, tb_offset, context 

1091 ) 

1092 

1093 def stb2text(self, stb: list[str]) -> str: 

1094 """Convert a structured traceback (a list) to a string.""" 

1095 return self.tb_join_char.join(stb) 

1096 

1097 def set_mode(self, mode: Optional[str] = None) -> None: 

1098 """Switch to the desired mode. 

1099 

1100 If mode is not specified, cycles through the available modes.""" 

1101 

1102 if not mode: 

1103 new_idx = (self.valid_modes.index(self.mode) + 1) % len(self.valid_modes) 

1104 self.mode = self.valid_modes[new_idx] 

1105 elif mode not in self.valid_modes: 

1106 raise ValueError( 

1107 "Unrecognized mode in FormattedTB: <" + mode + ">\n" 

1108 "Valid modes: " + str(self.valid_modes) 

1109 ) 

1110 else: 

1111 assert isinstance(mode, str) 

1112 self.mode = mode 

1113 # include variable details only in 'Verbose' mode 

1114 self.include_vars = self.mode == self.valid_modes[2] 

1115 # Set the join character for generating text tracebacks 

1116 self.tb_join_char = self._join_chars[self.mode] 

1117 

1118 # some convenient shortcuts 

1119 def plain(self) -> None: 

1120 self.set_mode(self.valid_modes[0]) 

1121 

1122 def context(self) -> None: 

1123 self.set_mode(self.valid_modes[1]) 

1124 

1125 def verbose(self) -> None: 

1126 self.set_mode(self.valid_modes[2]) 

1127 

1128 def minimal(self) -> None: 

1129 self.set_mode(self.valid_modes[3]) 

1130 

1131 

1132# ---------------------------------------------------------------------------- 

1133class AutoFormattedTB(FormattedTB): 

1134 """A traceback printer which can be called on the fly. 

1135 

1136 It will find out about exceptions by itself. 

1137 

1138 A brief example:: 

1139 

1140 AutoTB = AutoFormattedTB(mode = 'Verbose', theme_name='linux') 

1141 try: 

1142 ... 

1143 except: 

1144 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object 

1145 """ 

1146 

1147 def __call__( 

1148 self, 

1149 etype: type | None = None, 

1150 evalue: BaseException | None = None, 

1151 etb: TracebackType | None = None, 

1152 out: Any = None, 

1153 tb_offset: int | None = None, 

1154 ) -> None: 

1155 """Print out a formatted exception traceback. 

1156 

1157 Optional arguments: 

1158 - out: an open file-like object to direct output to. 

1159 

1160 - tb_offset: the number of frames to skip over in the stack, on a 

1161 per-call basis (this overrides temporarily the instance's tb_offset 

1162 given at initialization time.""" 

1163 

1164 if out is None: 

1165 out = self.ostream 

1166 out.flush() 

1167 out.write(self.text(etype, evalue, etb, tb_offset)) # type:ignore[arg-type] 

1168 out.write("\n") 

1169 out.flush() 

1170 # FIXME: we should remove the auto pdb behavior from here and leave 

1171 # that to the clients. 

1172 try: 

1173 self.debugger() 

1174 except KeyboardInterrupt: 

1175 print("\nKeyboardInterrupt") 

1176 

1177 def structured_traceback( 

1178 self, 

1179 etype: type, 

1180 evalue: Optional[BaseException], 

1181 etb: Optional[TracebackType] = None, 

1182 tb_offset: Optional[int] = None, 

1183 context: int = 5, 

1184 ) -> list[str]: 

1185 # tb: TracebackType or tupleof tb types ? 

1186 if etype is None: 

1187 etype, evalue, etb = sys.exc_info() 

1188 if isinstance(etb, tuple): 

1189 # tb is a tuple if this is a chained exception. 

1190 self.tb = etb[0] 

1191 else: 

1192 self.tb = etb 

1193 return FormattedTB.structured_traceback( 

1194 self, etype, evalue, etb, tb_offset, context 

1195 ) 

1196 

1197 

1198# --------------------------------------------------------------------------- 

1199 

1200 

1201# A simple class to preserve Nathan's original functionality. 

1202class ColorTB(FormattedTB): 

1203 """Deprecated since IPython 9.0.""" 

1204 

1205 def __init__(self, *args, **kwargs): 

1206 warnings.warn( 

1207 "Deprecated since IPython 9.0 use FormattedTB directly ColorTB is just an alias", 

1208 DeprecationWarning, 

1209 stacklevel=2, 

1210 ) 

1211 

1212 super().__init__(*args, **kwargs) 

1213 

1214 

1215class SyntaxTB(ListTB): 

1216 """Extension which holds some state: the last exception value""" 

1217 

1218 last_syntax_error: BaseException | None 

1219 

1220 def __init__(self, *, theme_name): 

1221 super().__init__(theme_name=theme_name) 

1222 self.last_syntax_error = None 

1223 

1224 def __call__(self, etype, value, elist): 

1225 self.last_syntax_error = value 

1226 

1227 super().__call__(etype, value, elist) 

1228 

1229 def structured_traceback( 

1230 self, 

1231 etype: type, 

1232 evalue: BaseException | None, 

1233 etb: TracebackType | None = None, 

1234 tb_offset: int | None = None, 

1235 context: int = 5, 

1236 ) -> list[str]: 

1237 value = evalue 

1238 # If the source file has been edited, the line in the syntax error can 

1239 # be wrong (retrieved from an outdated cache). This replaces it with 

1240 # the current value. 

1241 if ( 

1242 isinstance(value, SyntaxError) 

1243 and isinstance(value.filename, str) 

1244 and isinstance(value.lineno, int) 

1245 ): 

1246 linecache.checkcache(value.filename) 

1247 newtext = linecache.getline(value.filename, value.lineno) 

1248 if newtext: 

1249 value.text = newtext 

1250 self.last_syntax_error = value 

1251 return super(SyntaxTB, self).structured_traceback( 

1252 etype, value, etb, tb_offset=tb_offset, context=context 

1253 ) 

1254 

1255 def clear_err_state(self) -> Any | None: 

1256 """Return the current error state and clear it""" 

1257 e = self.last_syntax_error 

1258 self.last_syntax_error = None 

1259 return e 

1260 

1261 def stb2text(self, stb: list[str]) -> str: 

1262 """Convert a structured traceback (a list) to a string.""" 

1263 return "".join(stb)