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

460 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 assert isinstance(value.text, str) 

342 textline = value.text 

343 

344 if textline is not None: 

345 i = 0 

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

347 i += 1 

348 output_list.append( 

349 theme_table[self._theme_name].format( 

350 [ 

351 (Token.Line, " "), 

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

353 (Token, "\n"), 

354 ] 

355 ) 

356 ) 

357 if value.offset is not None: 

358 s = " " 

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

360 if c.isspace(): 

361 s += c 

362 else: 

363 s += " " 

364 output_list.append( 

365 theme_table[self._theme_name].format( 

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

367 ) 

368 ) 

369 s = value.msg 

370 else: 

371 s = self._some_str(value) 

372 if s: 

373 output_list.append( 

374 theme_table[self._theme_name].format( 

375 stype_tokens 

376 + [ 

377 (Token.ExcName, ":"), 

378 (Token, " "), 

379 (Token, s), 

380 (Token, "\n"), 

381 ] 

382 ) 

383 ) 

384 else: 

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

386 

387 # PEP-678 notes 

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

389 

390 # sync with user hooks 

391 if have_filedata: 

392 ipinst = get_ipython() 

393 if ipinst is not None: 

394 assert value is not None 

395 assert hasattr(value, "lineno") 

396 assert hasattr(value, "filename") 

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

398 

399 return output_list 

400 

401 def get_exception_only(self, etype, value): 

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

403 

404 Parameters 

405 ---------- 

406 etype : exception type 

407 value : exception value 

408 """ 

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

410 

411 def show_exception_only( 

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

413 ) -> None: 

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

415 

416 Parameters 

417 ---------- 

418 etype : exception type 

419 evalue : exception value 

420 """ 

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

422 # a subclass whose signature or behavior may be different 

423 ostream = self.ostream 

424 ostream.flush() 

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

426 ostream.flush() 

427 

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

429 # Lifted from traceback.py 

430 try: 

431 return str(value) 

432 except: 

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

434 

435 

436_sentinel = object() 

437_default = "default" 

438 

439 

440# ---------------------------------------------------------------------------- 

441class VerboseTB(TBTools): 

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

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

444 

445 Modified version which optionally strips the topmost entries from the 

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

447 would appear in the traceback).""" 

448 

449 tb_highlight = "bg:ansiyellow" 

450 tb_highlight_style = "default" 

451 

452 _mode: str 

453 

454 def __init__( 

455 self, 

456 # TODO: no default ? 

457 theme_name: str = _default, 

458 call_pdb: bool = False, 

459 ostream: Any = None, 

460 tb_offset: int = 0, 

461 long_header: bool = False, 

462 include_vars: bool = True, 

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

464 debugger_cls: type | None = None, 

465 *, 

466 color_scheme: Any = _sentinel, 

467 ): 

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

469 

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

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

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

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

474 if color_scheme is not _sentinel: 

475 assert isinstance(color_scheme, str) 

476 theme_name = color_scheme.lower() 

477 

478 warnings.warn( 

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

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

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

482 "theme.", 

483 stacklevel=2, 

484 category=DeprecationWarning, 

485 ) 

486 

487 if theme_name != _default: 

488 warnings.warn( 

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

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

491 "be ignored for the time being.", 

492 stacklevel=2, 

493 category=DeprecationWarning, 

494 ) 

495 

496 if theme_name == _default: 

497 theme_name = "linux" 

498 

499 assert isinstance(theme_name, str) 

500 super().__init__( 

501 theme_name=theme_name, 

502 call_pdb=call_pdb, 

503 ostream=ostream, 

504 debugger_cls=debugger_cls, 

505 ) 

506 self.tb_offset = tb_offset 

507 self.long_header = long_header 

508 self.include_vars = include_vars 

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

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

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

512 if check_cache is None: 

513 check_cache = linecache.checkcache 

514 self.check_cache = check_cache 

515 

516 self.skip_hidden = True 

517 

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

519 """Format a single stack frame""" 

520 assert isinstance(frame_info, FrameInfo) 

521 

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

523 return theme_table[self._theme_name].format( 

524 [ 

525 (Token, " "), 

526 ( 

527 Token.ExcName, 

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

529 ), 

530 (Token, "\n"), 

531 ] 

532 ) 

533 

534 indent: str = " " * INDENT_SIZE 

535 

536 assert isinstance(frame_info.lineno, int) 

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

538 if frame_info.executing is not None: 

539 func = frame_info.executing.code_qualname() 

540 else: 

541 func = "?" 

542 if func == "<module>": 

543 call = "" 

544 else: 

545 # Decide whether to include variable details or not 

546 var_repr = eqrepr if self.include_vars else nullrepr 

547 try: 

548 scope = inspect.formatargvalues( 

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

550 ) 

551 assert isinstance(scope, str) 

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

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

554 ) 

555 except KeyError: 

556 # This happens in situations like errors inside generator 

557 # expressions, where local variables are listed in the 

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

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

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

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

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

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

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

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

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

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

568 # test, but running a script consisting of: 

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

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

571 # disabled. 

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

573 [ 

574 (Token, "in "), 

575 (Token.VName, func), 

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

577 ] 

578 ) 

579 

580 lvals_toks: list[TokenStream] = [] 

581 if self.include_vars: 

582 try: 

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

584 # still need a workaround. 

585 fibp = frame_info.variables_in_executing_piece 

586 for var in fibp: 

587 lvals_toks.append( 

588 [ 

589 (Token, var.name), 

590 (Token, " "), 

591 (Token.ValEm, "= "), 

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

593 ] 

594 ) 

595 except Exception: 

596 lvals_toks.append( 

597 [ 

598 ( 

599 Token, 

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

601 ), 

602 ] 

603 ) 

604 

605 if frame_info._sd is None: 

606 # fast fallback if file is too long 

607 assert frame_info.filename is not None 

608 level_tokens = [ 

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

610 (Token, " "), 

611 (Token, call), 

612 (Token, "\n"), 

613 ] 

614 

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

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

617 first_line: int = frame_info.code.co_firstlineno 

618 current_line: int = frame_info.lineno 

619 raw_lines: list[str] = frame_info.raw_lines 

620 index: int = current_line - first_line 

621 assert frame_info.context is not None 

622 if index >= frame_info.context: 

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

624 stop = index + frame_info.context 

625 index = frame_info.context 

626 else: 

627 start = 0 

628 stop = index + frame_info.context 

629 raw_lines = raw_lines[start:stop] 

630 

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

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

633 

634 tb_tokens = _simple_format_traceback_lines( 

635 current_line, 

636 index, 

637 raw_color_err, 

638 lvals_toks, 

639 theme=theme_table[self._theme_name], 

640 ) 

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

642 

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

644 else: 

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

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

647 ) 

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

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

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

651 _format_traceback_lines( 

652 frame_info.lines, 

653 theme_table[self._theme_name], 

654 self.has_colors, 

655 lvals_toks, 

656 ) 

657 ) 

658 return result 

659 

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

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

662 if long_version: 

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

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

665 date = time.ctime(time.time()) 

666 theme = theme_table[self._theme_name] 

667 head = theme.format( 

668 [ 

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

670 (Token, "\n"), 

671 (Token.ExcName, etype), 

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

673 (Token, pyver), 

674 (Token, "\n"), 

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

676 ] 

677 ) 

678 head += ( 

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

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

681 ) 

682 else: 

683 # Simplified header 

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

685 [ 

686 (Token.ExcName, etype), 

687 ( 

688 Token, 

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

690 ), 

691 ] 

692 ) 

693 

694 return head 

695 

696 def format_exception(self, etype, evalue): 

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

698 try: 

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

700 except: 

701 # User exception is improperly defined. 

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

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

704 

705 # PEP-678 notes 

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

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

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

709 

710 for note in notes: 

711 assert isinstance(note, str) 

712 

713 str_notes: Sequence[str] = notes 

714 

715 # ... and format it 

716 return [ 

717 theme_table[self._theme_name].format( 

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

719 ), 

720 *( 

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

722 for note in str_notes 

723 ), 

724 ] 

725 

726 def format_exception_as_a_whole( 

727 self, 

728 etype: type, 

729 evalue: Optional[BaseException], 

730 etb: Optional[TracebackType], 

731 context: int, 

732 tb_offset: Optional[int], 

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

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

735 

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

737 (PEP 3134). 

738 """ 

739 # some locals 

740 orig_etype = etype 

741 try: 

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

743 except AttributeError: 

744 pass 

745 

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

747 assert isinstance(tb_offset, int) 

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

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

750 

751 frames = [] 

752 skipped = 0 

753 lastrecord = len(records) - 1 

754 for i, record in enumerate(records): 

755 if ( 

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

757 and self.skip_hidden 

758 ): 

759 if ( 

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

761 and i != lastrecord 

762 ): 

763 skipped += 1 

764 continue 

765 if skipped: 

766 frames.append( 

767 theme_table[self._theme_name].format( 

768 [ 

769 (Token, " "), 

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

771 (Token, "\n"), 

772 ] 

773 ) 

774 ) 

775 skipped = 0 

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

777 if skipped: 

778 frames.append( 

779 theme_table[self._theme_name].format( 

780 [ 

781 (Token, " "), 

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

783 (Token, "\n"), 

784 ] 

785 ) 

786 ) 

787 

788 formatted_exception = self.format_exception(etype, evalue) 

789 if records: 

790 frame_info = records[-1] 

791 ipinst = get_ipython() 

792 if ipinst is not None: 

793 ipinst.hooks.synchronize_with_editor( 

794 frame_info.filename, frame_info.lineno, 0 

795 ) 

796 

797 return [[head] + frames + formatted_exception] 

798 

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

800 assert etb is not None 

801 context = context - 1 

802 after = context // 2 

803 before = context - after 

804 if self.has_colors: 

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

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

807 formatter = Terminal256Formatter(style=style) 

808 else: 

809 formatter = None 

810 options = stack_data.Options( 

811 before=before, 

812 after=after, 

813 pygments_formatter=formatter, 

814 ) 

815 

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

817 cf: Optional[TracebackType] = etb 

818 max_len = 0 

819 tbs = [] 

820 while cf is not None: 

821 try: 

822 mod = inspect.getmodule(cf.tb_frame) 

823 if mod is not None: 

824 mod_name = mod.__name__ 

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

826 if root_name == "IPython": 

827 cf = cf.tb_next 

828 continue 

829 max_len = get_line_number_of_frame(cf.tb_frame) 

830 

831 except OSError: 

832 max_len = 0 

833 max_len = max(max_len, max_len) 

834 tbs.append(cf) 

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

836 

837 if max_len > FAST_THRESHOLD: 

838 FIs: list[FrameInfo] = [] 

839 for tb in tbs: 

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

841 lineno = frame.f_lineno 

842 code = frame.f_code 

843 filename = code.co_filename 

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

845 FIs.append( 

846 FrameInfo( 

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

848 ) 

849 ) 

850 return FIs 

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

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

853 return res2 

854 

855 def structured_traceback( 

856 self, 

857 etype: type, 

858 evalue: Optional[BaseException], 

859 etb: Optional[TracebackType] = None, 

860 tb_offset: Optional[int] = None, 

861 context: int = 5, 

862 ) -> list[str]: 

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

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

865 etype, evalue, etb, context, tb_offset 

866 ) 

867 

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

869 theme = theme_table[self._theme_name] 

870 head: str = theme.format( 

871 [ 

872 ( 

873 Token.Topline, 

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

875 ), 

876 ] 

877 ) 

878 structured_traceback_parts: list[str] = [head] 

879 chained_exceptions_tb_offset = 0 

880 lines_of_context = 3 

881 exception = self.get_parts_of_chained_exception(evalue) 

882 if exception: 

883 assert evalue is not None 

884 formatted_exceptions += self.prepare_chained_exception_message( 

885 evalue.__cause__ 

886 ) 

887 etype, evalue, etb = exception 

888 else: 

889 evalue = None 

890 chained_exc_ids = set() 

891 while evalue: 

892 formatted_exceptions += self.format_exception_as_a_whole( 

893 etype, evalue, etb, lines_of_context, chained_exceptions_tb_offset 

894 ) 

895 exception = self.get_parts_of_chained_exception(evalue) 

896 

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

898 chained_exc_ids.add( 

899 id(exception[1]) 

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

901 formatted_exceptions += self.prepare_chained_exception_message( 

902 evalue.__cause__ 

903 ) 

904 etype, evalue, etb = exception 

905 else: 

906 evalue = None 

907 

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

909 # the first exception should be on top 

910 for fx in reversed(formatted_exceptions): 

911 structured_traceback_parts += fx 

912 

913 return structured_traceback_parts 

914 

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

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

917 reference. 

918 

919 Keywords: 

920 

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

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

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

924 is false. 

925 

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

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

928 is deleted to prevent lingering references which hamper memory 

929 management. 

930 

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

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

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

934 

935 if force or self.call_pdb: 

936 if self.pdb is None: 

937 self.pdb = self.debugger_cls() 

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

939 # for pdb 

940 display_trap = DisplayTrap(hook=sys.__displayhook__) 

941 with display_trap: 

942 self.pdb.reset() 

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

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

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

946 else: 

947 etb = self.tb = sys.last_traceback 

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

949 assert self.tb.tb_next is not None 

950 self.tb = self.tb.tb_next 

951 if etb and etb.tb_next: 

952 etb = etb.tb_next 

953 self.pdb.botframe = etb.tb_frame 

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

955 # please check why later and remove the getattr. 

956 exc = ( 

957 sys.last_value 

958 if sys.version_info < (3, 12) 

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

960 ) # type: ignore[attr-defined] 

961 if exc: 

962 self.pdb.interaction(None, exc) 

963 else: 

964 self.pdb.interaction(None, etb) 

965 

966 if hasattr(self, "tb"): 

967 del self.tb 

968 

969 def handler(self, info=None): 

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

971 self.tb = etb 

972 ostream = self.ostream 

973 ostream.flush() 

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

975 ostream.write("\n") 

976 ostream.flush() 

977 

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

979 # out the right info on its own. 

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

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

982 if etb is None: 

983 self.handler() 

984 else: 

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

986 try: 

987 self.debugger() 

988 except KeyboardInterrupt: 

989 print("\nKeyboardInterrupt") 

990 

991 

992# ---------------------------------------------------------------------------- 

993class FormattedTB(VerboseTB, ListTB): 

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

995 

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

997 

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

999 

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

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

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

1003 like Python shells).""" 

1004 

1005 mode: str 

1006 

1007 def __init__( 

1008 self, 

1009 mode="Plain", 

1010 # TODO: no default 

1011 theme_name="linux", 

1012 call_pdb=False, 

1013 ostream=None, 

1014 tb_offset=0, 

1015 long_header=False, 

1016 include_vars=False, 

1017 check_cache=None, 

1018 debugger_cls=None, 

1019 ): 

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

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

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

1023 

1024 VerboseTB.__init__( 

1025 self, 

1026 theme_name=theme_name, 

1027 call_pdb=call_pdb, 

1028 ostream=ostream, 

1029 tb_offset=tb_offset, 

1030 long_header=long_header, 

1031 include_vars=include_vars, 

1032 check_cache=check_cache, 

1033 debugger_cls=debugger_cls, 

1034 ) 

1035 

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

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

1038 self._join_chars = dict( 

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

1040 ) 

1041 # set_mode also sets the tb_join_char attribute 

1042 self.set_mode(mode) 

1043 

1044 def structured_traceback( 

1045 self, 

1046 etype: type, 

1047 evalue: BaseException | None, 

1048 etb: TracebackType | None = None, 

1049 tb_offset: int | None = None, 

1050 context: int = 5, 

1051 ) -> list[str]: 

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

1053 mode = self.mode 

1054 if mode in self.verbose_modes: 

1055 # Verbose modes need a full traceback 

1056 return VerboseTB.structured_traceback( 

1057 self, etype, evalue, etb, tb_offset, context 

1058 ) 

1059 elif mode == "Docs": 

1060 # return DocTB 

1061 return DocTB( 

1062 theme_name=self._theme_name, 

1063 call_pdb=self.call_pdb, 

1064 ostream=self.ostream, 

1065 tb_offset=tb_offset, 

1066 long_header=self.long_header, 

1067 include_vars=self.include_vars, 

1068 check_cache=self.check_cache, 

1069 debugger_cls=self.debugger_cls, 

1070 ).structured_traceback( 

1071 etype, evalue, etb, tb_offset, 1 

1072 ) # type: ignore[arg-type] 

1073 

1074 elif mode == "Minimal": 

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

1076 else: 

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

1078 # out-of-date source code. 

1079 self.check_cache() 

1080 # Now we can extract and format the exception 

1081 return ListTB.structured_traceback( 

1082 self, etype, evalue, etb, tb_offset, context 

1083 ) 

1084 

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

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

1087 return self.tb_join_char.join(stb) 

1088 

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

1090 """Switch to the desired mode. 

1091 

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

1093 

1094 if not mode: 

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

1096 self.mode = self.valid_modes[new_idx] 

1097 elif mode not in self.valid_modes: 

1098 raise ValueError( 

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

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

1101 ) 

1102 else: 

1103 assert isinstance(mode, str) 

1104 self.mode = mode 

1105 # include variable details only in 'Verbose' mode 

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

1107 # Set the join character for generating text tracebacks 

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

1109 

1110 # some convenient shortcuts 

1111 def plain(self) -> None: 

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

1113 

1114 def context(self) -> None: 

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

1116 

1117 def verbose(self) -> None: 

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

1119 

1120 def minimal(self) -> None: 

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

1122 

1123 

1124# ---------------------------------------------------------------------------- 

1125class AutoFormattedTB(FormattedTB): 

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

1127 

1128 It will find out about exceptions by itself. 

1129 

1130 A brief example:: 

1131 

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

1133 try: 

1134 ... 

1135 except: 

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

1137 """ 

1138 

1139 def __call__( 

1140 self, 

1141 etype: type | None = None, 

1142 evalue: BaseException | None = None, 

1143 etb: TracebackType | None = None, 

1144 out: Any = None, 

1145 tb_offset: int | None = None, 

1146 ) -> None: 

1147 """Print out a formatted exception traceback. 

1148 

1149 Optional arguments: 

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

1151 

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

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

1154 given at initialization time.""" 

1155 

1156 if out is None: 

1157 out = self.ostream 

1158 out.flush() 

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

1160 out.write("\n") 

1161 out.flush() 

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

1163 # that to the clients. 

1164 try: 

1165 self.debugger() 

1166 except KeyboardInterrupt: 

1167 print("\nKeyboardInterrupt") 

1168 

1169 def structured_traceback( 

1170 self, 

1171 etype: type, 

1172 evalue: Optional[BaseException], 

1173 etb: Optional[TracebackType] = None, 

1174 tb_offset: Optional[int] = None, 

1175 context: int = 5, 

1176 ) -> list[str]: 

1177 # tb: TracebackType or tupleof tb types ? 

1178 if etype is None: 

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

1180 if isinstance(etb, tuple): 

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

1182 self.tb = etb[0] 

1183 else: 

1184 self.tb = etb 

1185 return FormattedTB.structured_traceback( 

1186 self, etype, evalue, etb, tb_offset, context 

1187 ) 

1188 

1189 

1190# --------------------------------------------------------------------------- 

1191 

1192 

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

1194class ColorTB(FormattedTB): 

1195 """Deprecated since IPython 9.0.""" 

1196 

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

1198 warnings.warn( 

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

1200 DeprecationWarning, 

1201 stacklevel=2, 

1202 ) 

1203 

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

1205 

1206 

1207class SyntaxTB(ListTB): 

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

1209 

1210 last_syntax_error: BaseException | None 

1211 

1212 def __init__(self, *, theme_name): 

1213 super().__init__(theme_name=theme_name) 

1214 self.last_syntax_error = None 

1215 

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

1217 self.last_syntax_error = value 

1218 

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

1220 

1221 def structured_traceback( 

1222 self, 

1223 etype: type, 

1224 evalue: BaseException | None, 

1225 etb: TracebackType | None = None, 

1226 tb_offset: int | None = None, 

1227 context: int = 5, 

1228 ) -> list[str]: 

1229 value = evalue 

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

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

1232 # the current value. 

1233 if ( 

1234 isinstance(value, SyntaxError) 

1235 and isinstance(value.filename, str) 

1236 and isinstance(value.lineno, int) 

1237 ): 

1238 linecache.checkcache(value.filename) 

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

1240 if newtext: 

1241 value.text = newtext 

1242 self.last_syntax_error = value 

1243 return super(SyntaxTB, self).structured_traceback( 

1244 etype, value, etb, tb_offset=tb_offset, context=context 

1245 ) 

1246 

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

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

1249 e = self.last_syntax_error 

1250 self.last_syntax_error = None 

1251 return e 

1252 

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

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

1255 return "".join(stb)