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

462 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, Callable, List, Optional, Tuple 

80 

81import stack_data 

82from pygments.formatters.terminal256 import Terminal256Formatter 

83from pygments.token import Token 

84 

85from IPython import get_ipython 

86from IPython.utils import path as util_path 

87from IPython.utils import py3compat 

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

89from IPython.utils.terminal import get_terminal_size 

90 

91from .display_trap import DisplayTrap 

92from .doctb import DocTB 

93from .tbtools import ( 

94 FrameInfo, 

95 TBTools, 

96 _format_traceback_lines, 

97 _safe_string, 

98 _simple_format_traceback_lines, 

99 _tokens_filename, 

100 eqrepr, 

101 get_line_number_of_frame, 

102 nullrepr, 

103 text_repr, 

104) 

105 

106# Globals 

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

108INDENT_SIZE = 8 

109 

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

111# it is too long. 

112FAST_THRESHOLD = 10_000 

113 

114# --------------------------------------------------------------------------- 

115class ListTB(TBTools): 

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

117 

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

119 as would be obtained by:: 

120 

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

122 if tb: 

123 elist = traceback.extract_tb(tb) 

124 else: 

125 elist = None 

126 

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

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

129 standard library). 

130 

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

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

133 

134 def __call__( 

135 self, 

136 etype: type[BaseException], 

137 evalue: BaseException | None, 

138 etb: TracebackType | None, 

139 ) -> None: 

140 self.ostream.flush() 

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

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

143 

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

145 if tb: 

146 return traceback.extract_tb(tb) 

147 else: 

148 return None 

149 

150 def structured_traceback( 

151 self, 

152 etype: type, 

153 evalue: Optional[BaseException], 

154 etb: Optional[TracebackType] = None, 

155 tb_offset: Optional[int] = None, 

156 context: int = 5, 

157 ) -> list[str]: 

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

159 

160 Parameters 

161 ---------- 

162 etype : exception type 

163 Type of the exception raised. 

164 evalue : object 

165 Data stored in the exception 

166 etb : list | TracebackType | None 

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

168 If Traceback: Traceback of the exception. 

169 tb_offset : int, optional 

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

171 instance evalue is used (set in constructor). 

172 context : int, optional 

173 Number of lines of context information to print. 

174 

175 Returns 

176 ------- 

177 String with formatted exception. 

178 """ 

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

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

181 if isinstance(etb, tuple): 

182 etb, chained_exc_ids = etb 

183 else: 

184 chained_exc_ids = set() 

185 elist: list[Any] 

186 if isinstance(etb, list): 

187 elist = etb 

188 elif etb is not None: 

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

190 else: 

191 elist = [] 

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

193 assert isinstance(tb_offset, int) 

194 out_list: list[str] = [] 

195 if elist: 

196 if tb_offset and len(elist) > tb_offset: 

197 elist = elist[tb_offset:] 

198 

199 out_list.append( 

200 theme_table[self._theme_name].format( 

201 [ 

202 (Token, "Traceback"), 

203 (Token, " "), 

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

205 (Token, ":"), 

206 (Token, "\n"), 

207 ] 

208 ), 

209 ) 

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

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

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

213 out_list.append(lines) 

214 

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

216 if etb is not None: 

217 exception = self.get_parts_of_chained_exception(evalue) 

218 

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

220 chained_exception_message: list[str] = ( 

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

222 if evalue is not None 

223 else [""] 

224 ) 

225 etype, evalue, etb = exception 

226 # Trace exception to avoid infinite 'cause' loop 

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

228 chained_exceptions_tb_offset = 0 

229 ol1 = self.structured_traceback( 

230 etype, 

231 evalue, 

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

233 chained_exceptions_tb_offset, 

234 context, 

235 ) 

236 ol2 = chained_exception_message 

237 

238 out_list = ol1 + ol2 + out_list 

239 

240 return out_list 

241 

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

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

244 

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

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

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

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

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

250 whose source text line is not None. 

251 

252 Lifted almost verbatim from traceback.py 

253 """ 

254 

255 output_list = [] 

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

257 # Will emphasize the last entry 

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

259 

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

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

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

263 ) 

264 

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

266 # look at _tokens_filename in forma_record. 

267 if name != "<module>": 

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

269 [ 

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

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

272 ] 

273 ) 

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

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

276 ) 

277 if line: 

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

279 [ 

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

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

282 (Token, "\n"), 

283 ] 

284 ) 

285 output_list.append(item) 

286 

287 return output_list 

288 

289 def _format_exception_only( 

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

291 ) -> list[str]: 

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

293 

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

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

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

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

298 printed) display detailed information about where the syntax error 

299 occurred. The message indicating which exception occurred is the 

300 always last string in the list. 

301 

302 Also lifted nearly verbatim from traceback.py 

303 """ 

304 have_filedata = False 

305 output_list = [] 

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

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

308 if value is None: 

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

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

311 else: 

312 if issubclass(etype, SyntaxError): 

313 assert hasattr(value, "filename") 

314 assert hasattr(value, "lineno") 

315 assert hasattr(value, "text") 

316 assert hasattr(value, "offset") 

317 assert hasattr(value, "msg") 

318 have_filedata = True 

319 if not value.filename: 

320 value.filename = "<string>" 

321 if value.lineno: 

322 lineno = value.lineno 

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

324 else: 

325 lineno = "unknown" 

326 textline = "" 

327 output_list.append( 

328 theme_table[self._theme_name].format( 

329 [(Token, " ")] 

330 + _tokens_filename( 

331 True, 

332 value.filename, 

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

334 ) 

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

336 ) 

337 ) 

338 if textline == "": 

339 # sep 2025: 

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

341 if value.text is None: 

342 textline = "" 

343 else: 

344 assert isinstance(value.text, str) 

345 textline = value.text 

346 

347 if textline is not None: 

348 i = 0 

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

350 i += 1 

351 output_list.append( 

352 theme_table[self._theme_name].format( 

353 [ 

354 (Token.Line, " "), 

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

356 (Token, "\n"), 

357 ] 

358 ) 

359 ) 

360 if value.offset is not None: 

361 s = " " 

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

363 if c.isspace(): 

364 s += c 

365 else: 

366 s += " " 

367 output_list.append( 

368 theme_table[self._theme_name].format( 

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

370 ) 

371 ) 

372 s = value.msg 

373 else: 

374 s = self._some_str(value) 

375 if s: 

376 output_list.append( 

377 theme_table[self._theme_name].format( 

378 stype_tokens 

379 + [ 

380 (Token.ExcName, ":"), 

381 (Token, " "), 

382 (Token, s), 

383 (Token, "\n"), 

384 ] 

385 ) 

386 ) 

387 else: 

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

389 

390 # PEP-678 notes 

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

392 

393 # sync with user hooks 

394 if have_filedata: 

395 ipinst = get_ipython() 

396 if ipinst is not None: 

397 assert value is not None 

398 assert hasattr(value, "lineno") 

399 assert hasattr(value, "filename") 

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

401 

402 return output_list 

403 

404 def get_exception_only(self, etype, value): 

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

406 

407 Parameters 

408 ---------- 

409 etype : exception type 

410 value : exception value 

411 """ 

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

413 

414 def show_exception_only( 

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

416 ) -> None: 

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

418 

419 Parameters 

420 ---------- 

421 etype : exception type 

422 evalue : exception value 

423 """ 

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

425 # a subclass whose signature or behavior may be different 

426 ostream = self.ostream 

427 ostream.flush() 

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

429 ostream.flush() 

430 

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

432 # Lifted from traceback.py 

433 try: 

434 return str(value) 

435 except: 

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

437 

438 

439_sentinel = object() 

440_default = "default" 

441 

442 

443# ---------------------------------------------------------------------------- 

444class VerboseTB(TBTools): 

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

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

447 

448 Modified version which optionally strips the topmost entries from the 

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

450 would appear in the traceback).""" 

451 

452 tb_highlight = "bg:ansiyellow" 

453 tb_highlight_style = "default" 

454 

455 _mode: str 

456 

457 def __init__( 

458 self, 

459 # TODO: no default ? 

460 theme_name: str = _default, 

461 call_pdb: bool = False, 

462 ostream: Any = None, 

463 tb_offset: int = 0, 

464 long_header: bool = False, 

465 include_vars: bool = True, 

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

467 debugger_cls: type | None = None, 

468 *, 

469 color_scheme: Any = _sentinel, 

470 ): 

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

472 

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

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

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

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

477 if color_scheme is not _sentinel: 

478 assert isinstance(color_scheme, str) 

479 theme_name = color_scheme.lower() 

480 

481 warnings.warn( 

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

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

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

485 "theme.", 

486 stacklevel=2, 

487 category=DeprecationWarning, 

488 ) 

489 

490 if theme_name != _default: 

491 warnings.warn( 

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

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

494 "be ignored for the time being.", 

495 stacklevel=2, 

496 category=DeprecationWarning, 

497 ) 

498 

499 if theme_name == _default: 

500 theme_name = "linux" 

501 

502 assert isinstance(theme_name, str) 

503 super().__init__( 

504 theme_name=theme_name, 

505 call_pdb=call_pdb, 

506 ostream=ostream, 

507 debugger_cls=debugger_cls, 

508 ) 

509 self.tb_offset = tb_offset 

510 self.long_header = long_header 

511 self.include_vars = include_vars 

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

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

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

515 if check_cache is None: 

516 check_cache = linecache.checkcache 

517 self.check_cache = check_cache 

518 

519 self.skip_hidden = True 

520 

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

522 """Format a single stack frame""" 

523 assert isinstance(frame_info, FrameInfo) 

524 

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

526 return theme_table[self._theme_name].format( 

527 [ 

528 (Token, " "), 

529 ( 

530 Token.ExcName, 

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

532 ), 

533 (Token, "\n"), 

534 ] 

535 ) 

536 

537 indent: str = " " * INDENT_SIZE 

538 

539 assert isinstance(frame_info.lineno, int) 

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

541 if frame_info.executing is not None: 

542 func = frame_info.executing.code_qualname() 

543 else: 

544 func = "?" 

545 if func == "<module>": 

546 call = "" 

547 else: 

548 # Decide whether to include variable details or not 

549 var_repr = eqrepr if self.include_vars else nullrepr 

550 try: 

551 scope = inspect.formatargvalues( 

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

553 ) 

554 assert isinstance(scope, str) 

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

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

557 ) 

558 except KeyError: 

559 # This happens in situations like errors inside generator 

560 # expressions, where local variables are listed in the 

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

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

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

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

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

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

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

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

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

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

571 # test, but running a script consisting of: 

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

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

574 # disabled. 

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

576 [ 

577 (Token, "in "), 

578 (Token.VName, func), 

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

580 ] 

581 ) 

582 

583 lvals_toks: list[TokenStream] = [] 

584 if self.include_vars: 

585 try: 

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

587 # still need a workaround. 

588 fibp = frame_info.variables_in_executing_piece 

589 for var in fibp: 

590 lvals_toks.append( 

591 [ 

592 (Token, var.name), 

593 (Token, " "), 

594 (Token.ValEm, "= "), 

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

596 ] 

597 ) 

598 except Exception: 

599 lvals_toks.append( 

600 [ 

601 ( 

602 Token, 

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

604 ), 

605 ] 

606 ) 

607 

608 if frame_info._sd is None: 

609 # fast fallback if file is too long 

610 assert frame_info.filename is not None 

611 level_tokens = [ 

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

613 (Token, " "), 

614 (Token, call), 

615 (Token, "\n"), 

616 ] 

617 

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

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

620 first_line: int = frame_info.code.co_firstlineno 

621 current_line: int = frame_info.lineno 

622 raw_lines: list[str] = frame_info.raw_lines 

623 index: int = current_line - first_line 

624 assert frame_info.context is not None 

625 if index >= frame_info.context: 

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

627 stop = index + frame_info.context 

628 index = frame_info.context 

629 else: 

630 start = 0 

631 stop = index + frame_info.context 

632 raw_lines = raw_lines[start:stop] 

633 

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

635 raw_color_err = [(s, _line_format(s, "str")) for s in raw_lines] 

636 

637 tb_tokens = _simple_format_traceback_lines( 

638 current_line, 

639 index, 

640 raw_color_err, 

641 lvals_toks, 

642 theme=theme_table[self._theme_name], 

643 ) 

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

645 

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

647 else: 

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

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

650 ) 

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

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

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

654 _format_traceback_lines( 

655 frame_info.lines, 

656 theme_table[self._theme_name], 

657 self.has_colors, 

658 lvals_toks, 

659 ) 

660 ) 

661 return result 

662 

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

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

665 if long_version: 

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

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

668 date = time.ctime(time.time()) 

669 theme = theme_table[self._theme_name] 

670 head = theme.format( 

671 [ 

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

673 (Token, "\n"), 

674 (Token.ExcName, etype), 

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

676 (Token, pyver), 

677 (Token, "\n"), 

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

679 ] 

680 ) 

681 head += ( 

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

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

684 ) 

685 else: 

686 # Simplified header 

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

688 [ 

689 (Token.ExcName, etype), 

690 ( 

691 Token, 

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

693 ), 

694 ] 

695 ) 

696 

697 return head 

698 

699 def format_exception(self, etype, evalue): 

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

701 try: 

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

703 except: 

704 # User exception is improperly defined. 

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

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

707 

708 # PEP-678 notes 

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

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

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

712 

713 for note in notes: 

714 assert isinstance(note, str) 

715 

716 str_notes: Sequence[str] = notes 

717 

718 # ... and format it 

719 return [ 

720 theme_table[self._theme_name].format( 

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

722 ), 

723 *( 

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

725 for note in str_notes 

726 ), 

727 ] 

728 

729 def format_exception_as_a_whole( 

730 self, 

731 etype: type, 

732 evalue: Optional[BaseException], 

733 etb: Optional[TracebackType], 

734 context: int, 

735 tb_offset: Optional[int], 

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

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

738 

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

740 (PEP 3134). 

741 """ 

742 # some locals 

743 orig_etype = etype 

744 try: 

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

746 except AttributeError: 

747 pass 

748 

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

750 assert isinstance(tb_offset, int) 

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

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

753 

754 frames = [] 

755 skipped = 0 

756 lastrecord = len(records) - 1 

757 for i, record in enumerate(records): 

758 if ( 

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

760 and self.skip_hidden 

761 ): 

762 if ( 

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

764 and i != lastrecord 

765 ): 

766 skipped += 1 

767 continue 

768 if skipped: 

769 frames.append( 

770 theme_table[self._theme_name].format( 

771 [ 

772 (Token, " "), 

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

774 (Token, "\n"), 

775 ] 

776 ) 

777 ) 

778 skipped = 0 

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

780 if skipped: 

781 frames.append( 

782 theme_table[self._theme_name].format( 

783 [ 

784 (Token, " "), 

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

786 (Token, "\n"), 

787 ] 

788 ) 

789 ) 

790 

791 formatted_exception = self.format_exception(etype, evalue) 

792 if records: 

793 frame_info = records[-1] 

794 ipinst = get_ipython() 

795 if ipinst is not None: 

796 ipinst.hooks.synchronize_with_editor( 

797 frame_info.filename, frame_info.lineno, 0 

798 ) 

799 

800 return [[head] + frames + formatted_exception] 

801 

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

803 assert etb is not None 

804 context = context - 1 

805 after = context // 2 

806 before = context - after 

807 if self.has_colors: 

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

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

810 formatter = Terminal256Formatter(style=style) 

811 else: 

812 formatter = None 

813 options = stack_data.Options( 

814 before=before, 

815 after=after, 

816 pygments_formatter=formatter, 

817 ) 

818 

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

820 cf: Optional[TracebackType] = etb 

821 max_len = 0 

822 tbs = [] 

823 while cf is not None: 

824 try: 

825 mod = inspect.getmodule(cf.tb_frame) 

826 if mod is not None: 

827 mod_name = mod.__name__ 

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

829 if root_name == "IPython": 

830 cf = cf.tb_next 

831 continue 

832 max_len = get_line_number_of_frame(cf.tb_frame) 

833 

834 except OSError: 

835 max_len = 0 

836 max_len = max(max_len, max_len) 

837 tbs.append(cf) 

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

839 

840 if max_len > FAST_THRESHOLD: 

841 FIs: list[FrameInfo] = [] 

842 for tb in tbs: 

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

844 lineno = frame.f_lineno 

845 code = frame.f_code 

846 filename = code.co_filename 

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

848 FIs.append( 

849 FrameInfo( 

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

851 ) 

852 ) 

853 return FIs 

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

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

856 return res2 

857 

858 def structured_traceback( 

859 self, 

860 etype: type, 

861 evalue: Optional[BaseException], 

862 etb: Optional[TracebackType] = None, 

863 tb_offset: Optional[int] = None, 

864 context: int = 5, 

865 ) -> list[str]: 

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

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

868 etype, evalue, etb, context, tb_offset 

869 ) 

870 

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

872 theme = theme_table[self._theme_name] 

873 head: str = theme.format( 

874 [ 

875 ( 

876 Token.Topline, 

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

878 ), 

879 ] 

880 ) 

881 structured_traceback_parts: list[str] = [head] 

882 chained_exceptions_tb_offset = 0 

883 lines_of_context = 3 

884 exception = self.get_parts_of_chained_exception(evalue) 

885 if exception: 

886 assert evalue is not None 

887 formatted_exceptions += self.prepare_chained_exception_message( 

888 evalue.__cause__ 

889 ) 

890 etype, evalue, etb = exception 

891 else: 

892 evalue = None 

893 chained_exc_ids = set() 

894 while evalue: 

895 formatted_exceptions += self.format_exception_as_a_whole( 

896 etype, evalue, etb, lines_of_context, chained_exceptions_tb_offset 

897 ) 

898 exception = self.get_parts_of_chained_exception(evalue) 

899 

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

901 chained_exc_ids.add( 

902 id(exception[1]) 

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

904 formatted_exceptions += self.prepare_chained_exception_message( 

905 evalue.__cause__ 

906 ) 

907 etype, evalue, etb = exception 

908 else: 

909 evalue = None 

910 

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

912 # the first exception should be on top 

913 for fx in reversed(formatted_exceptions): 

914 structured_traceback_parts += fx 

915 

916 return structured_traceback_parts 

917 

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

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

920 reference. 

921 

922 Keywords: 

923 

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

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

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

927 is false. 

928 

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

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

931 is deleted to prevent lingering references which hamper memory 

932 management. 

933 

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

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

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

937 

938 if force or self.call_pdb: 

939 if self.pdb is None: 

940 self.pdb = self.debugger_cls() 

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

942 # for pdb 

943 display_trap = DisplayTrap(hook=sys.__displayhook__) 

944 with display_trap: 

945 self.pdb.reset() 

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

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

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

949 else: 

950 etb = self.tb = sys.last_traceback 

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

952 assert self.tb.tb_next is not None 

953 self.tb = self.tb.tb_next 

954 if etb and etb.tb_next: 

955 etb = etb.tb_next 

956 self.pdb.botframe = etb.tb_frame 

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

958 # please check why later and remove the getattr. 

959 exc = ( 

960 sys.last_value 

961 if sys.version_info < (3, 12) 

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

963 ) # type: ignore[attr-defined] 

964 if exc: 

965 self.pdb.interaction(None, exc) 

966 else: 

967 self.pdb.interaction(None, etb) 

968 

969 if hasattr(self, "tb"): 

970 del self.tb 

971 

972 def handler(self, info=None): 

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

974 self.tb = etb 

975 ostream = self.ostream 

976 ostream.flush() 

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

978 ostream.write("\n") 

979 ostream.flush() 

980 

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

982 # out the right info on its own. 

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

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

985 if etb is None: 

986 self.handler() 

987 else: 

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

989 try: 

990 self.debugger() 

991 except KeyboardInterrupt: 

992 print("\nKeyboardInterrupt") 

993 

994 

995# ---------------------------------------------------------------------------- 

996class FormattedTB(VerboseTB, ListTB): 

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

998 

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

1000 

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

1002 

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

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

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

1006 like Python shells).""" 

1007 

1008 mode: str 

1009 

1010 def __init__( 

1011 self, 

1012 mode="Plain", 

1013 # TODO: no default 

1014 theme_name="linux", 

1015 call_pdb=False, 

1016 ostream=None, 

1017 tb_offset=0, 

1018 long_header=False, 

1019 include_vars=False, 

1020 check_cache=None, 

1021 debugger_cls=None, 

1022 ): 

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

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

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

1026 

1027 VerboseTB.__init__( 

1028 self, 

1029 theme_name=theme_name, 

1030 call_pdb=call_pdb, 

1031 ostream=ostream, 

1032 tb_offset=tb_offset, 

1033 long_header=long_header, 

1034 include_vars=include_vars, 

1035 check_cache=check_cache, 

1036 debugger_cls=debugger_cls, 

1037 ) 

1038 

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

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

1041 self._join_chars = dict( 

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

1043 ) 

1044 # set_mode also sets the tb_join_char attribute 

1045 self.set_mode(mode) 

1046 

1047 def structured_traceback( 

1048 self, 

1049 etype: type, 

1050 evalue: BaseException | None, 

1051 etb: TracebackType | None = None, 

1052 tb_offset: int | None = None, 

1053 context: int = 5, 

1054 ) -> list[str]: 

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

1056 mode = self.mode 

1057 if mode in self.verbose_modes: 

1058 # Verbose modes need a full traceback 

1059 return VerboseTB.structured_traceback( 

1060 self, etype, evalue, etb, tb_offset, context 

1061 ) 

1062 elif mode == "Docs": 

1063 # return DocTB 

1064 return DocTB( 

1065 theme_name=self._theme_name, 

1066 call_pdb=self.call_pdb, 

1067 ostream=self.ostream, 

1068 tb_offset=tb_offset, 

1069 long_header=self.long_header, 

1070 include_vars=self.include_vars, 

1071 check_cache=self.check_cache, 

1072 debugger_cls=self.debugger_cls, 

1073 ).structured_traceback( 

1074 etype, evalue, etb, tb_offset, 1 

1075 ) # type: ignore[arg-type] 

1076 

1077 elif mode == "Minimal": 

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

1079 else: 

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

1081 # out-of-date source code. 

1082 self.check_cache() 

1083 # Now we can extract and format the exception 

1084 return ListTB.structured_traceback( 

1085 self, etype, evalue, etb, tb_offset, context 

1086 ) 

1087 

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

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

1090 return self.tb_join_char.join(stb) 

1091 

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

1093 """Switch to the desired mode. 

1094 

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

1096 

1097 if not mode: 

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

1099 self.mode = self.valid_modes[new_idx] 

1100 elif mode not in self.valid_modes: 

1101 raise ValueError( 

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

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

1104 ) 

1105 else: 

1106 assert isinstance(mode, str) 

1107 self.mode = mode 

1108 # include variable details only in 'Verbose' mode 

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

1110 # Set the join character for generating text tracebacks 

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

1112 

1113 # some convenient shortcuts 

1114 def plain(self) -> None: 

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

1116 

1117 def context(self) -> None: 

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

1119 

1120 def verbose(self) -> None: 

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

1122 

1123 def minimal(self) -> None: 

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

1125 

1126 

1127# ---------------------------------------------------------------------------- 

1128class AutoFormattedTB(FormattedTB): 

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

1130 

1131 It will find out about exceptions by itself. 

1132 

1133 A brief example:: 

1134 

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

1136 try: 

1137 ... 

1138 except: 

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

1140 """ 

1141 

1142 def __call__( 

1143 self, 

1144 etype: type | None = None, 

1145 evalue: BaseException | None = None, 

1146 etb: TracebackType | None = None, 

1147 out: Any = None, 

1148 tb_offset: int | None = None, 

1149 ) -> None: 

1150 """Print out a formatted exception traceback. 

1151 

1152 Optional arguments: 

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

1154 

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

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

1157 given at initialization time.""" 

1158 

1159 if out is None: 

1160 out = self.ostream 

1161 out.flush() 

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

1163 out.write("\n") 

1164 out.flush() 

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

1166 # that to the clients. 

1167 try: 

1168 self.debugger() 

1169 except KeyboardInterrupt: 

1170 print("\nKeyboardInterrupt") 

1171 

1172 def structured_traceback( 

1173 self, 

1174 etype: type, 

1175 evalue: Optional[BaseException], 

1176 etb: Optional[TracebackType] = None, 

1177 tb_offset: Optional[int] = None, 

1178 context: int = 5, 

1179 ) -> list[str]: 

1180 # tb: TracebackType or tupleof tb types ? 

1181 if etype is None: 

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

1183 if isinstance(etb, tuple): 

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

1185 self.tb = etb[0] 

1186 else: 

1187 self.tb = etb 

1188 return FormattedTB.structured_traceback( 

1189 self, etype, evalue, etb, tb_offset, context 

1190 ) 

1191 

1192 

1193# --------------------------------------------------------------------------- 

1194 

1195 

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

1197class ColorTB(FormattedTB): 

1198 """Deprecated since IPython 9.0.""" 

1199 

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

1201 warnings.warn( 

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

1203 DeprecationWarning, 

1204 stacklevel=2, 

1205 ) 

1206 

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

1208 

1209 

1210class SyntaxTB(ListTB): 

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

1212 

1213 last_syntax_error: BaseException | None 

1214 

1215 def __init__(self, *, theme_name): 

1216 super().__init__(theme_name=theme_name) 

1217 self.last_syntax_error = None 

1218 

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

1220 self.last_syntax_error = value 

1221 

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

1223 

1224 def structured_traceback( 

1225 self, 

1226 etype: type, 

1227 evalue: BaseException | None, 

1228 etb: TracebackType | None = None, 

1229 tb_offset: int | None = None, 

1230 context: int = 5, 

1231 ) -> list[str]: 

1232 value = evalue 

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

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

1235 # the current value. 

1236 if ( 

1237 isinstance(value, SyntaxError) 

1238 and isinstance(value.filename, str) 

1239 and isinstance(value.lineno, int) 

1240 ): 

1241 linecache.checkcache(value.filename) 

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

1243 if newtext: 

1244 value.text = newtext 

1245 self.last_syntax_error = value 

1246 return super(SyntaxTB, self).structured_traceback( 

1247 etype, value, etb, tb_offset=tb_offset, context=context 

1248 ) 

1249 

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

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

1252 e = self.last_syntax_error 

1253 self.last_syntax_error = None 

1254 return e 

1255 

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

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

1258 return "".join(stb)