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

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

519 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 

61from __future__ import annotations 

62 

63# ***************************************************************************** 

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

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

66# 

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

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

69# ***************************************************************************** 

70 

71import linecache 

72import sys 

73import time 

74import types 

75import warnings 

76from collections.abc import Sequence 

77from types import TracebackType 

78from typing import Any 

79from collections.abc import Callable 

80from typing import TYPE_CHECKING 

81 

82from pygments.token import Token 

83 

84from IPython.core.getipython import get_ipython 

85from IPython.utils.terminal import get_terminal_size 

86 

87from .display_trap import DisplayTrap 

88from .doctb import DocTB 

89from .tbtools import ( 

90 FrameInfo, 

91 TBTools, 

92 _format_traceback_lines, 

93 _safe_string, 

94 _simple_format_traceback_lines, 

95 _tokens_filename, 

96 eqrepr, 

97 get_line_number_of_frame, 

98 nullrepr, 

99) 

100 

101if TYPE_CHECKING: 

102 import stack_data 

103 from IPython.utils.PyColorize import TokenStream 

104 import traceback 

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 import traceback 

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: BaseException | None, 

155 etb: TracebackType | None = None, 

156 tb_offset: int | None = 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 # (see the recursive self.structured_traceback() call below), and can 

183 # also be a pre-built list of frames per the docstring above; neither 

184 # is expressible in the public `TracebackType | None` signature. 

185 from IPython.utils.PyColorize import theme_table 

186 if isinstance(etb, tuple): 

187 etb, chained_exc_ids = etb # type: ignore[unreachable] 

188 else: 

189 chained_exc_ids = set() 

190 elist: list[Any] 

191 if isinstance(etb, list): 

192 elist = etb # type: ignore[unreachable] 

193 elif etb is not None: 

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

195 else: 

196 elist = [] 

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

198 assert isinstance(tb_offset, int) 

199 out_list: list[str] = [] 

200 if elist: 

201 if tb_offset and len(elist) > tb_offset: 

202 elist = elist[tb_offset:] 

203 

204 out_list.append( 

205 theme_table[self._theme_name].format( 

206 [ 

207 (Token, "Traceback"), 

208 (Token, " "), 

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

210 (Token, ":"), 

211 (Token, "\n"), 

212 ] 

213 ), 

214 ) 

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

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

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

218 out_list.append(lines) 

219 

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

221 if etb is not None: 

222 exception = self.get_parts_of_chained_exception(evalue) 

223 

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

225 chained_exception_message: list[str] = ( 

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

227 if evalue is not None 

228 else [""] 

229 ) 

230 etype, evalue, etb = exception 

231 # Trace exception to avoid infinite 'cause' loop 

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

233 chained_exceptions_tb_offset = 0 

234 ol1 = self.structured_traceback( 

235 etype, 

236 evalue, 

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

238 chained_exceptions_tb_offset, 

239 context, 

240 ) 

241 ol2 = chained_exception_message 

242 

243 out_list = ol1 + ol2 + out_list 

244 

245 return out_list 

246 

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

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

249 

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

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

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

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

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

255 whose source text line is not None. 

256 

257 Lifted almost verbatim from traceback.py 

258 """ 

259 from IPython.utils.PyColorize import theme_table 

260 

261 output_list = [] 

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

263 # Will emphasize the last entry 

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

265 

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

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

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

269 ) 

270 

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

272 # look at _tokens_filename in forma_record. 

273 if name != "<module>": 

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

275 [ 

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

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

278 ] 

279 ) 

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

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

282 ) 

283 if line: 

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

285 [ 

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

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

288 (Token, "\n"), 

289 ] 

290 ) 

291 output_list.append(item) 

292 

293 return output_list 

294 

295 def _format_exception_only( 

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

297 ) -> list[str]: 

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

299 

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

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

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

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

304 printed) display detailed information about where the syntax error 

305 occurred. The message indicating which exception occurred is the 

306 always last string in the list. 

307 

308 Also lifted nearly verbatim from traceback.py 

309 """ 

310 from IPython.utils.PyColorize import theme_table 

311 

312 have_filedata = False 

313 output_list = [] 

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

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

316 if value is None: 

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

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

319 else: 

320 if issubclass(etype, SyntaxError): 

321 assert hasattr(value, "filename") 

322 assert hasattr(value, "lineno") 

323 assert hasattr(value, "text") 

324 assert hasattr(value, "offset") 

325 assert hasattr(value, "msg") 

326 have_filedata = True 

327 if not value.filename: 

328 value.filename = "<string>" 

329 if value.lineno: 

330 lineno = value.lineno 

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

332 else: 

333 lineno = "unknown" 

334 textline = "" 

335 output_list.append( 

336 theme_table[self._theme_name].format( 

337 [(Token, " ")] 

338 + _tokens_filename( 

339 True, 

340 value.filename, 

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

342 ) 

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

344 ) 

345 ) 

346 if textline == "": 

347 # sep 2025: 

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

349 if value.text is None: 

350 textline = "" 

351 else: 

352 assert isinstance(value.text, str) 

353 textline = value.text 

354 

355 if textline is not None: 

356 i = 0 

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

358 i += 1 

359 output_list.append( 

360 theme_table[self._theme_name].format( 

361 [ 

362 (Token.Line, " "), 

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

364 (Token, "\n"), 

365 ] 

366 ) 

367 ) 

368 if value.offset is not None: 

369 s = " " 

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

371 if c.isspace(): 

372 s += c 

373 else: 

374 s += " " 

375 output_list.append( 

376 theme_table[self._theme_name].format( 

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

378 ) 

379 ) 

380 s = value.msg 

381 else: 

382 s = self._some_str(value) 

383 if s: 

384 output_list.append( 

385 theme_table[self._theme_name].format( 

386 stype_tokens 

387 + [ 

388 (Token.ExcName, ":"), 

389 (Token, " "), 

390 (Token, s), 

391 (Token, "\n"), 

392 ] 

393 ) 

394 ) 

395 else: 

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

397 

398 # PEP-678 notes 

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

400 

401 # sync with user hooks 

402 if have_filedata: 

403 ipinst = get_ipython() 

404 if ipinst is not None: 

405 assert value is not None 

406 assert hasattr(value, "lineno") 

407 assert hasattr(value, "filename") 

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

409 

410 return output_list 

411 

412 def get_exception_only(self, etype, value): 

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

414 

415 Parameters 

416 ---------- 

417 etype : exception type 

418 value : exception value 

419 """ 

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

421 

422 def structured_traceback_doctest( 

423 self, 

424 etype, 

425 evalue, 

426 etb=None, 

427 tb_offset=None, 

428 context=5, 

429 ): 

430 """Return a doctest-freindly traceback. 

431 

432 Shows only the header, an ellipsis, and the excepttion line. 

433 """ 

434 # Handle chained exception rule 

435 if isinstance(etb, tuple): 

436 etb, chained_exc_ids = etb 

437 else: 

438 chained_exc_ids = set() 

439 

440 have_traceback = etb is not None 

441 

442 out_list = [] 

443 if have_traceback: 

444 out_list.append("Traceback (most recent call last):\n") 

445 out_list.append(" ...\n") 

446 

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

448 out_list.append(lines) 

449 

450 # Handle chained exceptions 

451 if etb is not None: 

452 exception = self.get_parts_of_chained_exception(evalue) 

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

454 chained_exception_message = (self.prepare_chained_exception_message(evalue.__cause__)[0] if evalue is not None else [""]) 

455 etype, evalue, etb = exception 

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

457 chained_tb = self.structured_traceback_doctest(etype, evalue, (etb, chained_exc_ids), 0, context) 

458 out_list = chained_tb + chained_exception_message + out_list 

459 

460 return out_list 

461 

462 def show_exception_only( 

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

464 ) -> None: 

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

466 

467 Parameters 

468 ---------- 

469 etype : exception type 

470 evalue : exception value 

471 """ 

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

473 # a subclass whose signature or behavior may be different 

474 ostream = self.ostream 

475 ostream.flush() 

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

477 ostream.flush() 

478 

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

480 # Lifted from traceback.py 

481 try: 

482 return str(value) 

483 except Exception: 

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

485 

486 

487_sentinel = object() 

488_default = "default" 

489 

490 

491# ---------------------------------------------------------------------------- 

492class VerboseTB(TBTools): 

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

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

495 

496 Modified version which optionally strips the topmost entries from the 

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

498 would appear in the traceback).""" 

499 

500 tb_highlight = "bg:ansiyellow" 

501 tb_highlight_style = "default" 

502 

503 _mode: str 

504 

505 def __init__( 

506 self, 

507 # TODO: no default ? 

508 theme_name: str = _default, 

509 call_pdb: bool = False, 

510 ostream: Any = None, 

511 tb_offset: int = 0, 

512 long_header: bool = False, 

513 include_vars: bool = True, 

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

515 debugger_cls: type | None = None, 

516 *, 

517 color_scheme: Any = _sentinel, 

518 ): 

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

520 

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

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

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

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

525 if color_scheme is not _sentinel: 

526 assert isinstance(color_scheme, str) 

527 theme_name = color_scheme.lower() 

528 

529 warnings.warn( 

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

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

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

533 "theme.", 

534 stacklevel=2, 

535 category=DeprecationWarning, 

536 ) 

537 

538 if theme_name != _default: 

539 warnings.warn( 

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

541 "(deprecated since IPython 9.0) to VerboseTB constructor. `theme_name` will " 

542 "be ignored for the time being.", 

543 stacklevel=2, 

544 category=DeprecationWarning, 

545 ) 

546 

547 if theme_name == _default: 

548 theme_name = "linux" 

549 

550 assert isinstance(theme_name, str) 

551 super().__init__( 

552 theme_name=theme_name, 

553 call_pdb=call_pdb, 

554 ostream=ostream, 

555 debugger_cls=debugger_cls, 

556 ) 

557 self.tb_offset = tb_offset 

558 self.long_header = long_header 

559 self.include_vars = include_vars 

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

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

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

563 if check_cache is None: 

564 check_cache = linecache.checkcache 

565 self.check_cache = check_cache 

566 

567 self.skip_hidden = True 

568 

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

570 """Format a single stack frame""" 

571 import inspect 

572 import stack_data 

573 from IPython.utils.PyColorize import theme_table 

574 

575 assert isinstance(frame_info, FrameInfo) 

576 

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

578 return theme_table[self._theme_name].format( 

579 [ 

580 (Token, " "), 

581 ( 

582 Token.ExcName, 

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

584 ), 

585 (Token, "\n"), 

586 ] 

587 ) 

588 

589 indent: str = " " * INDENT_SIZE 

590 

591 assert isinstance(frame_info.lineno, int) 

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

593 func: str 

594 if frame_info.executing is not None: 

595 func = frame_info.executing.code_qualname() 

596 elif frame_info.code is not None: 

597 func = ( 

598 getattr(frame_info.code, "co_qualname", None) or frame_info.code.co_name 

599 ) 

600 else: 

601 func = "?" 

602 if func == "<module>": 

603 call = "" 

604 else: 

605 # Decide whether to include variable details or not 

606 var_repr = eqrepr if self.include_vars else nullrepr 

607 try: 

608 scope = inspect.formatargvalues( 

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

610 ) 

611 assert isinstance(scope, str) 

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

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

614 ) 

615 except KeyError: 

616 # This happens in situations like errors inside generator 

617 # expressions, where local variables are listed in the 

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

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

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

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

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

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

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

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

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

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

628 # test, but running a script consisting of: 

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

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

631 # disabled. 

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

633 [ 

634 (Token, "in "), 

635 (Token.VName, func), 

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

637 ] 

638 ) 

639 

640 lvals_toks: list[TokenStream] = [] 

641 if self.include_vars: 

642 try: 

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

644 # still need a workaround. 

645 fibp = frame_info.variables_in_executing_piece 

646 for var in fibp: 

647 lvals_toks.append( 

648 [ 

649 (Token, var.name), 

650 (Token, " "), 

651 (Token.ValEm, "= "), 

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

653 ] 

654 ) 

655 except Exception: 

656 lvals_toks.append( 

657 [ 

658 ( 

659 Token, 

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

661 ), 

662 ] 

663 ) 

664 

665 if frame_info._sd is None: 

666 # fast fallback if file is too long 

667 assert frame_info.filename is not None 

668 level_tokens = ( 

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

670 + [ 

671 (Token, ", " if call else ""), 

672 (Token, call), 

673 (Token, "\n"), 

674 ] 

675 ) 

676 

677 from IPython.utils.PyColorize import Parser 

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

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

680 first_line: int = frame_info.code.co_firstlineno 

681 current_line: int = frame_info.lineno 

682 raw_lines: list[str] = frame_info.raw_lines 

683 index: int = current_line - first_line 

684 assert frame_info.context is not None 

685 if index >= frame_info.context: 

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

687 stop = index + frame_info.context 

688 index = frame_info.context 

689 else: 

690 start = 0 

691 stop = index + frame_info.context 

692 raw_lines = raw_lines[start:stop] 

693 

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

695 raw_color_err = [] 

696 for s in raw_lines: 

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

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

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

700 

701 tb_tokens = _simple_format_traceback_lines( 

702 current_line, 

703 index, 

704 raw_color_err, 

705 lvals_toks, 

706 theme=theme_table[self._theme_name], 

707 ) 

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

709 

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

711 else: 

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

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

714 ) 

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

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

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

718 _format_traceback_lines( 

719 frame_info.lines, 

720 theme_table[self._theme_name], 

721 self.has_colors, 

722 lvals_toks, 

723 ) 

724 ) 

725 return result 

726 

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

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

729 from IPython.utils.PyColorize import theme_table 

730 if long_version: 

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

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

733 date = time.ctime(time.time()) 

734 theme = theme_table[self._theme_name] 

735 head = theme.format( 

736 [ 

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

738 (Token, "\n"), 

739 (Token.ExcName, etype), 

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

741 (Token, pyver), 

742 (Token, "\n"), 

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

744 ] 

745 ) 

746 head += ( 

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

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

749 ) 

750 else: 

751 # Simplified header 

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

753 [ 

754 (Token.ExcName, etype), 

755 ( 

756 Token, 

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

758 ), 

759 ] 

760 ) 

761 

762 return head 

763 

764 def format_exception(self, etype, evalue): 

765 from IPython.utils.PyColorize import theme_table 

766 

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

768 try: 

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

770 except Exception: 

771 # User exception is improperly defined. 

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

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

774 

775 # PEP-678 notes 

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

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

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

779 

780 for note in notes: 

781 assert isinstance(note, str) 

782 

783 str_notes: Sequence[str] = notes 

784 

785 # ... and format it 

786 return [ 

787 theme_table[self._theme_name].format( 

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

789 ), 

790 *( 

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

792 for note in str_notes 

793 ), 

794 ] 

795 

796 def format_exception_as_a_whole( 

797 self, 

798 etype: type, 

799 evalue: BaseException | None, 

800 etb: TracebackType | None, 

801 context: int, 

802 tb_offset: int | None, 

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

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

805 

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

807 (PEP 3134). 

808 """ 

809 import stack_data 

810 

811 from IPython.utils.PyColorize import theme_table 

812 

813 # some locals 

814 orig_etype = etype 

815 try: 

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

817 except AttributeError: 

818 pass 

819 

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

821 assert isinstance(tb_offset, int) 

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

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

824 

825 frames = [] 

826 skipped = 0 

827 lastrecord = len(records) - 1 

828 for i, record in enumerate(records): 

829 if ( 

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

831 and self.skip_hidden 

832 ): 

833 if ( 

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

835 and i != lastrecord 

836 ): 

837 skipped += 1 

838 continue 

839 if skipped: 

840 frames.append( 

841 theme_table[self._theme_name].format( 

842 [ 

843 (Token, " "), 

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

845 (Token, "\n"), 

846 ] 

847 ) 

848 ) 

849 skipped = 0 

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

851 if skipped: 

852 frames.append( 

853 theme_table[self._theme_name].format( 

854 [ 

855 (Token, " "), 

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

857 (Token, "\n"), 

858 ] 

859 ) 

860 ) 

861 

862 formatted_exception = self.format_exception(etype, evalue) 

863 if records: 

864 frame_info = records[-1] 

865 ipinst = get_ipython() 

866 if ipinst is not None: 

867 ipinst.hooks.synchronize_with_editor( 

868 frame_info.filename, frame_info.lineno, 0 

869 ) 

870 

871 return [[head] + frames + formatted_exception] 

872 

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

874 import inspect 

875 import stack_data 

876 

877 from IPython.utils.PyColorize import theme_table 

878 

879 assert etb is not None 

880 context = context - 1 

881 after = context // 2 

882 before = context - after 

883 if self.has_colors: 

884 theme = theme_table[self._theme_name] 

885 base_style = theme.as_pygments_style() 

886 tb_highlight = theme.extra_style.get(Token.TbHighlight, self.tb_highlight) 

887 style = stack_data.style_with_executing_node(base_style, tb_highlight) 

888 from pygments.formatters.terminal256 import Terminal256Formatter 

889 formatter = Terminal256Formatter(style=style) 

890 else: 

891 formatter = None 

892 options = stack_data.Options( 

893 before=before, 

894 after=after, 

895 pygments_formatter=formatter, 

896 ) 

897 

898 # Collect traceback frames and their module sizes. 

899 cf: TracebackType | None = etb 

900 tbs: list[tuple[TracebackType, int]] = [] 

901 while cf is not None: 

902 try: 

903 mod = inspect.getmodule(cf.tb_frame) 

904 if mod is not None: 

905 mod_name = mod.__name__ 

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

907 if root_name == "IPython": 

908 cf = cf.tb_next 

909 continue 

910 frame_len = get_line_number_of_frame(cf.tb_frame) 

911 if frame_len == 0: 

912 # File not found or not a .py file (e.g. <string> from 

913 # exec()). Check if source is actually available; if not, 

914 # force the fast path so that FrameInfo's "Could not get 

915 # source" fallback is rendered. 

916 try: 

917 inspect.getsourcelines(cf.tb_frame) 

918 except OSError: 

919 frame_len = FAST_THRESHOLD + 1 

920 except OSError: 

921 frame_len = FAST_THRESHOLD + 1 

922 assert cf is not None # narrowing for mypy; guarded by while condition 

923 tbs.append((cf, frame_len)) 

924 cf = cf.tb_next 

925 

926 # Group consecutive frames by fast/slow and process each group. 

927 # Consecutive slow frames must be processed together so that 

928 # stack_data can detect RepeatedFrames (recursion collapsing). 

929 FIs: list[FrameInfo] = [] 

930 i = 0 

931 while i < len(tbs): 

932 tb, frame_len = tbs[i] 

933 if frame_len > FAST_THRESHOLD: 

934 frame = tb.tb_frame 

935 lineno = frame.f_lineno 

936 code = frame.f_code 

937 filename = code.co_filename 

938 FIs.append( 

939 FrameInfo( 

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

941 ) 

942 ) 

943 i += 1 

944 else: 

945 # Collect the consecutive run of slow frames 

946 group_start = i 

947 while i < len(tbs) and tbs[i][1] <= FAST_THRESHOLD: 

948 i += 1 

949 # Build set of frame objects in this group for filtering 

950 group_frames = {tbs[j][0].tb_frame for j in range(group_start, i)} 

951 # Process via stack_data starting from the first tb in the group 

952 for sd_fi in stack_data.FrameInfo.stack_data( 

953 tbs[group_start][0], options=options 

954 ): 

955 # stack_data follows tb_next through the full chain, 

956 # including IPython frames we skipped during collection. 

957 # Filter those out, but always keep RepeatedFrames. 

958 if isinstance(sd_fi, stack_data.RepeatedFrames) or sd_fi.frame in group_frames: 

959 FIs.append(FrameInfo._from_stack_data_FrameInfo(sd_fi)) 

960 

961 return FIs 

962 

963 def structured_traceback( 

964 self, 

965 etype: type, 

966 evalue: BaseException | None, 

967 etb: TracebackType | None = None, 

968 tb_offset: int | None = None, 

969 context: int = 5, 

970 ) -> list[str]: 

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

972 from IPython.utils.PyColorize import theme_table 

973 

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

975 etype, evalue, etb, context, tb_offset 

976 ) 

977 

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

979 theme = theme_table[self._theme_name] 

980 head: str = theme.format( 

981 [ 

982 ( 

983 Token.Topline, 

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

985 ), 

986 ] 

987 ) 

988 structured_traceback_parts: list[str] = [head] 

989 chained_exceptions_tb_offset = 0 

990 lines_of_context = 3 

991 exception = self.get_parts_of_chained_exception(evalue) 

992 if exception: 

993 assert evalue is not None 

994 formatted_exceptions += self.prepare_chained_exception_message( 

995 evalue.__cause__ 

996 ) 

997 etype, evalue, etb = exception 

998 else: 

999 evalue = None 

1000 chained_exc_ids = set() 

1001 while evalue: 

1002 formatted_exceptions += self.format_exception_as_a_whole( 

1003 etype, evalue, etb, lines_of_context, chained_exceptions_tb_offset 

1004 ) 

1005 exception = self.get_parts_of_chained_exception(evalue) 

1006 

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

1008 chained_exc_ids.add( 

1009 id(exception[1]) 

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

1011 formatted_exceptions += self.prepare_chained_exception_message( 

1012 evalue.__cause__ 

1013 ) 

1014 etype, evalue, etb = exception 

1015 else: 

1016 evalue = None 

1017 

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

1019 # the first exception should be on top 

1020 for fx in reversed(formatted_exceptions): 

1021 structured_traceback_parts += fx 

1022 

1023 return structured_traceback_parts 

1024 

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

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

1027 reference. 

1028 

1029 Keywords: 

1030 

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

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

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

1034 is false. 

1035 

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

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

1038 is deleted to prevent lingering references which hamper memory 

1039 management. 

1040 

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

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

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

1044 

1045 if force or self.call_pdb: 

1046 if self.pdb is None: 

1047 self.pdb = self.debugger_cls() 

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

1049 # for pdb 

1050 display_trap = DisplayTrap(hook=sys.__displayhook__) 

1051 with display_trap: 

1052 self.pdb.reset() 

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

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

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

1056 else: 

1057 etb = self.tb = sys.last_traceback 

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

1059 assert self.tb.tb_next is not None 

1060 self.tb = self.tb.tb_next 

1061 if etb and etb.tb_next: 

1062 etb = etb.tb_next 

1063 self.pdb.botframe = etb.tb_frame 

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

1065 # please check why later and remove the getattr. 

1066 exc = ( 

1067 sys.last_value 

1068 if sys.version_info < (3, 12) 

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

1070 ) 

1071 if exc: 

1072 self.pdb.interaction(None, exc) 

1073 else: 

1074 self.pdb.interaction(None, etb) 

1075 

1076 if hasattr(self, "tb"): 

1077 del self.tb 

1078 

1079 def handler(self, info=None): 

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

1081 self.tb = etb 

1082 ostream = self.ostream 

1083 ostream.flush() 

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

1085 ostream.write("\n") 

1086 ostream.flush() 

1087 

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

1089 # out the right info on its own. 

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

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

1092 if etb is None: 

1093 self.handler() 

1094 else: 

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

1096 try: 

1097 self.debugger() 

1098 except KeyboardInterrupt: 

1099 print("\nKeyboardInterrupt") 

1100 

1101 

1102# ---------------------------------------------------------------------------- 

1103class FormattedTB(VerboseTB, ListTB): 

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

1105 

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

1107 

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

1109 

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

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

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

1113 like Python shells).""" 

1114 

1115 mode: str 

1116 

1117 def __init__( 

1118 self, 

1119 mode="Plain", 

1120 # TODO: no default 

1121 theme_name="linux", 

1122 call_pdb=False, 

1123 ostream=None, 

1124 tb_offset=0, 

1125 long_header=False, 

1126 include_vars=False, 

1127 check_cache=None, 

1128 debugger_cls=None, 

1129 ): 

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

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

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

1133 

1134 VerboseTB.__init__( 

1135 self, 

1136 theme_name=theme_name, 

1137 call_pdb=call_pdb, 

1138 ostream=ostream, 

1139 tb_offset=tb_offset, 

1140 long_header=long_header, 

1141 include_vars=include_vars, 

1142 check_cache=check_cache, 

1143 debugger_cls=debugger_cls, 

1144 ) 

1145 

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

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

1148 self._join_chars = dict( 

1149 Plain="", Context="\n", Verbose="\n", Minimal="", Docs="", Doctest="" 

1150 ) 

1151 # set_mode also sets the tb_join_char attribute 

1152 self.set_mode(mode) 

1153 

1154 def structured_traceback( 

1155 self, 

1156 etype: type, 

1157 evalue: BaseException | None, 

1158 etb: TracebackType | None = None, 

1159 tb_offset: int | None = None, 

1160 context: int = 5, 

1161 ) -> list[str]: 

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

1163 mode = self.mode 

1164 if mode in self.verbose_modes: 

1165 # Verbose modes need a full traceback 

1166 return VerboseTB.structured_traceback( 

1167 self, etype, evalue, etb, tb_offset, context 

1168 ) 

1169 elif mode == "Docs": 

1170 # return DocTB 

1171 return DocTB( 

1172 theme_name=self._theme_name, 

1173 call_pdb=self.call_pdb, 

1174 ostream=self.ostream, 

1175 tb_offset=tb_offset, 

1176 long_header=self.long_header, 

1177 include_vars=self.include_vars, 

1178 check_cache=self.check_cache, 

1179 debugger_cls=self.debugger_cls, 

1180 ).structured_traceback( 

1181 etype, evalue, etb, tb_offset, 1 

1182 ) 

1183 

1184 elif mode == "Minimal": 

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

1186 elif mode == "Doctest": 

1187 return ListTB.structured_traceback_doctest(self, etype, evalue, etb, tb_offset, context) 

1188 else: 

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

1190 # out-of-date source code. 

1191 self.check_cache() 

1192 # Now we can extract and format the exception 

1193 return ListTB.structured_traceback( 

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

1195 ) 

1196 

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

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

1199 return self.tb_join_char.join(stb) 

1200 

1201 def set_mode(self, mode: str | None = None) -> None: 

1202 """Switch to the desired mode. 

1203 

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

1205 

1206 if not mode: 

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

1208 self.mode = self.valid_modes[new_idx] 

1209 elif mode not in self.valid_modes: 

1210 raise ValueError( 

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

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

1213 ) 

1214 else: 

1215 assert isinstance(mode, str) 

1216 self.mode = mode 

1217 # include variable details only in 'Verbose' mode 

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

1219 # Set the join character for generating text tracebacks 

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

1221 

1222 # some convenient shortcuts 

1223 def plain(self) -> None: 

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

1225 

1226 def context(self) -> None: 

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

1228 

1229 def verbose(self) -> None: 

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

1231 

1232 def minimal(self) -> None: 

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

1234 

1235 

1236# ---------------------------------------------------------------------------- 

1237class AutoFormattedTB(FormattedTB): 

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

1239 

1240 It will find out about exceptions by itself. 

1241 

1242 A brief example:: 

1243 

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

1245 try: 

1246 ... 

1247 except: 

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

1249 """ 

1250 

1251 def __call__( 

1252 self, 

1253 etype: type | None = None, 

1254 evalue: BaseException | None = None, 

1255 etb: TracebackType | None = None, 

1256 out: Any = None, 

1257 tb_offset: int | None = None, 

1258 ) -> None: 

1259 """Print out a formatted exception traceback. 

1260 

1261 Optional arguments: 

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

1263 

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

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

1266 given at initialization time.""" 

1267 

1268 if out is None: 

1269 out = self.ostream 

1270 out.flush() 

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

1272 out.write("\n") 

1273 out.flush() 

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

1275 # that to the clients. 

1276 try: 

1277 self.debugger() 

1278 except KeyboardInterrupt: 

1279 print("\nKeyboardInterrupt") 

1280 

1281 def structured_traceback( 

1282 self, 

1283 etype: type, 

1284 evalue: BaseException | None, 

1285 etb: TracebackType | None = None, 

1286 tb_offset: int | None = None, 

1287 context: int = 5, 

1288 ) -> list[str]: 

1289 # tb: TracebackType or tupleof tb types ? 

1290 # etype can be None when called as structured_traceback(*sys.exc_info()) 

1291 # with no active exception; etb can be a tuple for a chained exception. 

1292 # Neither is expressible in the public signature above. 

1293 if etype is None: 

1294 etype, evalue, etb = sys.exc_info() # type: ignore[unreachable] 

1295 if isinstance(etb, tuple): 

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

1297 self.tb = etb[0] # type: ignore[unreachable] 

1298 else: 

1299 self.tb = etb 

1300 return FormattedTB.structured_traceback( 

1301 self, etype, evalue, etb, tb_offset, context 

1302 ) 

1303 

1304 

1305# --------------------------------------------------------------------------- 

1306 

1307 

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

1309class ColorTB(FormattedTB): 

1310 """Deprecated since IPython 9.0.""" 

1311 

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

1313 warnings.warn( 

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

1315 DeprecationWarning, 

1316 stacklevel=2, 

1317 ) 

1318 

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

1320 

1321 

1322class SyntaxTB(ListTB): 

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

1324 

1325 last_syntax_error: BaseException | None 

1326 

1327 def __init__(self, *, theme_name): 

1328 super().__init__(theme_name=theme_name) 

1329 self.last_syntax_error = None 

1330 

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

1332 self.last_syntax_error = value 

1333 

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

1335 

1336 def structured_traceback( 

1337 self, 

1338 etype: type, 

1339 evalue: BaseException | None, 

1340 etb: TracebackType | None = None, 

1341 tb_offset: int | None = None, 

1342 context: int = 5, 

1343 ) -> list[str]: 

1344 value = evalue 

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

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

1347 # the current value. 

1348 if ( 

1349 isinstance(value, SyntaxError) 

1350 and isinstance(value.filename, str) 

1351 and isinstance(value.lineno, int) 

1352 ): 

1353 linecache.checkcache(value.filename) 

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

1355 if newtext: 

1356 value.text = newtext 

1357 self.last_syntax_error = value 

1358 return super().structured_traceback( 

1359 etype, value, etb, tb_offset=tb_offset, context=context 

1360 ) 

1361 

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

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

1364 e = self.last_syntax_error 

1365 self.last_syntax_error = None 

1366 return e 

1367 

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

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

1370 return "".join(stb)