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

456 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 textline = py3compat.cast_unicode(value.text, "utf-8") 

340 

341 if textline is not None: 

342 i = 0 

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

344 i += 1 

345 output_list.append( 

346 theme_table[self._theme_name].format( 

347 [ 

348 (Token.Line, " "), 

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

350 (Token, "\n"), 

351 ] 

352 ) 

353 ) 

354 if value.offset is not None: 

355 s = " " 

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

357 if c.isspace(): 

358 s += c 

359 else: 

360 s += " " 

361 output_list.append( 

362 theme_table[self._theme_name].format( 

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

364 ) 

365 ) 

366 s = value.msg 

367 else: 

368 s = self._some_str(value) 

369 if s: 

370 output_list.append( 

371 theme_table[self._theme_name].format( 

372 stype_tokens 

373 + [ 

374 (Token.ExcName, ":"), 

375 (Token, " "), 

376 (Token, s), 

377 (Token, "\n"), 

378 ] 

379 ) 

380 ) 

381 else: 

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

383 

384 # PEP-678 notes 

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

386 

387 # sync with user hooks 

388 if have_filedata: 

389 ipinst = get_ipython() 

390 if ipinst is not None: 

391 assert value is not None 

392 assert hasattr(value, "lineno") 

393 assert hasattr(value, "filename") 

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

395 

396 return output_list 

397 

398 def get_exception_only(self, etype, value): 

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

400 

401 Parameters 

402 ---------- 

403 etype : exception type 

404 value : exception value 

405 """ 

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

407 

408 def show_exception_only( 

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

410 ) -> None: 

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

412 

413 Parameters 

414 ---------- 

415 etype : exception type 

416 evalue : exception value 

417 """ 

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

419 # a subclass whose signature or behavior may be different 

420 ostream = self.ostream 

421 ostream.flush() 

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

423 ostream.flush() 

424 

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

426 # Lifted from traceback.py 

427 try: 

428 return py3compat.cast_unicode(str(value)) 

429 except: 

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

431 

432 

433_sentinel = object() 

434_default = "default" 

435 

436 

437# ---------------------------------------------------------------------------- 

438class VerboseTB(TBTools): 

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

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

441 

442 Modified version which optionally strips the topmost entries from the 

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

444 would appear in the traceback).""" 

445 

446 tb_highlight = "bg:ansiyellow" 

447 tb_highlight_style = "default" 

448 

449 _mode: str 

450 

451 def __init__( 

452 self, 

453 # TODO: no default ? 

454 theme_name: str = _default, 

455 call_pdb: bool = False, 

456 ostream: Any = None, 

457 tb_offset: int = 0, 

458 long_header: bool = False, 

459 include_vars: bool = True, 

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

461 debugger_cls: type | None = None, 

462 *, 

463 color_scheme: Any = _sentinel, 

464 ): 

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

466 

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

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

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

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

471 if color_scheme is not _sentinel: 

472 assert isinstance(color_scheme, str) 

473 theme_name = color_scheme.lower() 

474 

475 warnings.warn( 

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

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

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

479 "theme.", 

480 stacklevel=2, 

481 category=DeprecationWarning, 

482 ) 

483 

484 if theme_name != _default: 

485 warnings.warn( 

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

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

488 "be ignored for the time being.", 

489 stacklevel=2, 

490 category=DeprecationWarning, 

491 ) 

492 

493 if theme_name == _default: 

494 theme_name = "linux" 

495 

496 assert isinstance(theme_name, str) 

497 super().__init__( 

498 theme_name=theme_name, 

499 call_pdb=call_pdb, 

500 ostream=ostream, 

501 debugger_cls=debugger_cls, 

502 ) 

503 self.tb_offset = tb_offset 

504 self.long_header = long_header 

505 self.include_vars = include_vars 

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

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

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

509 if check_cache is None: 

510 check_cache = linecache.checkcache 

511 self.check_cache = check_cache 

512 

513 self.skip_hidden = True 

514 

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

516 """Format a single stack frame""" 

517 assert isinstance(frame_info, FrameInfo) 

518 

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

520 return theme_table[self._theme_name].format( 

521 [ 

522 (Token, " "), 

523 ( 

524 Token.ExcName, 

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

526 ), 

527 (Token, "\n"), 

528 ] 

529 ) 

530 

531 indent: str = " " * INDENT_SIZE 

532 

533 assert isinstance(frame_info.lineno, int) 

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

535 if frame_info.executing is not None: 

536 func = frame_info.executing.code_qualname() 

537 else: 

538 func = "?" 

539 if func == "<module>": 

540 call = "" 

541 else: 

542 # Decide whether to include variable details or not 

543 var_repr = eqrepr if self.include_vars else nullrepr 

544 try: 

545 scope = inspect.formatargvalues( 

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

547 ) 

548 assert isinstance(scope, str) 

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

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

551 ) 

552 except KeyError: 

553 # This happens in situations like errors inside generator 

554 # expressions, where local variables are listed in the 

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

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

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

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

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

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

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

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

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

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

565 # test, but running a script consisting of: 

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

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

568 # disabled. 

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

570 [ 

571 (Token, "in "), 

572 (Token.VName, func), 

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

574 ] 

575 ) 

576 

577 lvals_toks: list[TokenStream] = [] 

578 if self.include_vars: 

579 try: 

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

581 # still need a workaround. 

582 fibp = frame_info.variables_in_executing_piece 

583 for var in fibp: 

584 lvals_toks.append( 

585 [ 

586 (Token, var.name), 

587 (Token, " "), 

588 (Token.ValEm, "= "), 

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

590 ] 

591 ) 

592 except Exception: 

593 lvals_toks.append( 

594 [ 

595 ( 

596 Token, 

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

598 ), 

599 ] 

600 ) 

601 

602 if frame_info._sd is None: 

603 # fast fallback if file is too long 

604 assert frame_info.filename is not None 

605 level_tokens = [ 

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

607 (Token, " "), 

608 (Token, call), 

609 (Token, "\n"), 

610 ] 

611 

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

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

614 first_line: int = frame_info.code.co_firstlineno 

615 current_line: int = frame_info.lineno 

616 raw_lines: list[str] = frame_info.raw_lines 

617 index: int = current_line - first_line 

618 assert frame_info.context is not None 

619 if index >= frame_info.context: 

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

621 stop = index + frame_info.context 

622 index = frame_info.context 

623 else: 

624 start = 0 

625 stop = index + frame_info.context 

626 raw_lines = raw_lines[start:stop] 

627 

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

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

630 

631 tb_tokens = _simple_format_traceback_lines( 

632 current_line, 

633 index, 

634 raw_color_err, 

635 lvals_toks, 

636 theme=theme_table[self._theme_name], 

637 ) 

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

639 

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

641 else: 

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

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

644 ) 

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

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

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

648 _format_traceback_lines( 

649 frame_info.lines, 

650 theme_table[self._theme_name], 

651 self.has_colors, 

652 lvals_toks, 

653 ) 

654 ) 

655 return result 

656 

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

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

659 if long_version: 

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

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

662 date = time.ctime(time.time()) 

663 theme = theme_table[self._theme_name] 

664 head = theme.format( 

665 [ 

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

667 (Token, "\n"), 

668 (Token.ExcName, etype), 

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

670 (Token, pyver), 

671 (Token, "\n"), 

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

673 ] 

674 ) 

675 head += ( 

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

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

678 ) 

679 else: 

680 # Simplified header 

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

682 [ 

683 (Token.ExcName, etype), 

684 ( 

685 Token, 

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

687 ), 

688 ] 

689 ) 

690 

691 return head 

692 

693 def format_exception(self, etype, evalue): 

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

695 try: 

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

697 except: 

698 # User exception is improperly defined. 

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

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

701 

702 # PEP-678 notes 

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

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

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

706 

707 # ... and format it 

708 return [ 

709 theme_table[self._theme_name].format( 

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

711 ), 

712 *( 

713 theme_table[self._theme_name].format( 

714 [(Token, _safe_string(py3compat.cast_unicode(n), "note"))] 

715 ) 

716 for n in notes 

717 ), 

718 ] 

719 

720 def format_exception_as_a_whole( 

721 self, 

722 etype: type, 

723 evalue: Optional[BaseException], 

724 etb: Optional[TracebackType], 

725 context: int, 

726 tb_offset: Optional[int], 

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

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

729 

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

731 (PEP 3134). 

732 """ 

733 # some locals 

734 orig_etype = etype 

735 try: 

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

737 except AttributeError: 

738 pass 

739 

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

741 assert isinstance(tb_offset, int) 

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

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

744 

745 frames = [] 

746 skipped = 0 

747 lastrecord = len(records) - 1 

748 for i, record in enumerate(records): 

749 if ( 

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

751 and self.skip_hidden 

752 ): 

753 if ( 

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

755 and i != lastrecord 

756 ): 

757 skipped += 1 

758 continue 

759 if skipped: 

760 frames.append( 

761 theme_table[self._theme_name].format( 

762 [ 

763 (Token, " "), 

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

765 (Token, "\n"), 

766 ] 

767 ) 

768 ) 

769 skipped = 0 

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

771 if skipped: 

772 frames.append( 

773 theme_table[self._theme_name].format( 

774 [ 

775 (Token, " "), 

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

777 (Token, "\n"), 

778 ] 

779 ) 

780 ) 

781 

782 formatted_exception = self.format_exception(etype, evalue) 

783 if records: 

784 frame_info = records[-1] 

785 ipinst = get_ipython() 

786 if ipinst is not None: 

787 ipinst.hooks.synchronize_with_editor( 

788 frame_info.filename, frame_info.lineno, 0 

789 ) 

790 

791 return [[head] + frames + formatted_exception] 

792 

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

794 assert etb is not None 

795 context = context - 1 

796 after = context // 2 

797 before = context - after 

798 if self.has_colors: 

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

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

801 formatter = Terminal256Formatter(style=style) 

802 else: 

803 formatter = None 

804 options = stack_data.Options( 

805 before=before, 

806 after=after, 

807 pygments_formatter=formatter, 

808 ) 

809 

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

811 cf: Optional[TracebackType] = etb 

812 max_len = 0 

813 tbs = [] 

814 while cf is not None: 

815 try: 

816 mod = inspect.getmodule(cf.tb_frame) 

817 if mod is not None: 

818 mod_name = mod.__name__ 

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

820 if root_name == "IPython": 

821 cf = cf.tb_next 

822 continue 

823 max_len = get_line_number_of_frame(cf.tb_frame) 

824 

825 except OSError: 

826 max_len = 0 

827 max_len = max(max_len, max_len) 

828 tbs.append(cf) 

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

830 

831 if max_len > FAST_THRESHOLD: 

832 FIs: list[FrameInfo] = [] 

833 for tb in tbs: 

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

835 lineno = frame.f_lineno 

836 code = frame.f_code 

837 filename = code.co_filename 

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

839 FIs.append( 

840 FrameInfo( 

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

842 ) 

843 ) 

844 return FIs 

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

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

847 return res2 

848 

849 def structured_traceback( 

850 self, 

851 etype: type, 

852 evalue: Optional[BaseException], 

853 etb: Optional[TracebackType] = None, 

854 tb_offset: Optional[int] = None, 

855 context: int = 5, 

856 ) -> list[str]: 

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

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

859 etype, evalue, etb, context, tb_offset 

860 ) 

861 

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

863 theme = theme_table[self._theme_name] 

864 head: str = theme.format( 

865 [ 

866 ( 

867 Token.Topline, 

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

869 ), 

870 ] 

871 ) 

872 structured_traceback_parts: list[str] = [head] 

873 chained_exceptions_tb_offset = 0 

874 lines_of_context = 3 

875 exception = self.get_parts_of_chained_exception(evalue) 

876 if exception: 

877 assert evalue is not None 

878 formatted_exceptions += self.prepare_chained_exception_message( 

879 evalue.__cause__ 

880 ) 

881 etype, evalue, etb = exception 

882 else: 

883 evalue = None 

884 chained_exc_ids = set() 

885 while evalue: 

886 formatted_exceptions += self.format_exception_as_a_whole( 

887 etype, evalue, etb, lines_of_context, chained_exceptions_tb_offset 

888 ) 

889 exception = self.get_parts_of_chained_exception(evalue) 

890 

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

892 chained_exc_ids.add( 

893 id(exception[1]) 

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

895 formatted_exceptions += self.prepare_chained_exception_message( 

896 evalue.__cause__ 

897 ) 

898 etype, evalue, etb = exception 

899 else: 

900 evalue = None 

901 

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

903 # the first exception should be on top 

904 for fx in reversed(formatted_exceptions): 

905 structured_traceback_parts += fx 

906 

907 return structured_traceback_parts 

908 

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

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

911 reference. 

912 

913 Keywords: 

914 

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

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

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

918 is false. 

919 

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

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

922 is deleted to prevent lingering references which hamper memory 

923 management. 

924 

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

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

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

928 

929 if force or self.call_pdb: 

930 if self.pdb is None: 

931 self.pdb = self.debugger_cls() 

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

933 # for pdb 

934 display_trap = DisplayTrap(hook=sys.__displayhook__) 

935 with display_trap: 

936 self.pdb.reset() 

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

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

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

940 else: 

941 etb = self.tb = sys.last_traceback 

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

943 assert self.tb.tb_next is not None 

944 self.tb = self.tb.tb_next 

945 if etb and etb.tb_next: 

946 etb = etb.tb_next 

947 self.pdb.botframe = etb.tb_frame 

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

949 # please check why later and remove the getattr. 

950 exc = ( 

951 sys.last_value 

952 if sys.version_info < (3, 12) 

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

954 ) # type: ignore[attr-defined] 

955 if exc: 

956 self.pdb.interaction(None, exc) 

957 else: 

958 self.pdb.interaction(None, etb) 

959 

960 if hasattr(self, "tb"): 

961 del self.tb 

962 

963 def handler(self, info=None): 

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

965 self.tb = etb 

966 ostream = self.ostream 

967 ostream.flush() 

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

969 ostream.write("\n") 

970 ostream.flush() 

971 

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

973 # out the right info on its own. 

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

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

976 if etb is None: 

977 self.handler() 

978 else: 

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

980 try: 

981 self.debugger() 

982 except KeyboardInterrupt: 

983 print("\nKeyboardInterrupt") 

984 

985 

986# ---------------------------------------------------------------------------- 

987class FormattedTB(VerboseTB, ListTB): 

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

989 

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

991 

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

993 

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

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

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

997 like Python shells).""" 

998 

999 mode: str 

1000 

1001 def __init__( 

1002 self, 

1003 mode="Plain", 

1004 # TODO: no default 

1005 theme_name="linux", 

1006 call_pdb=False, 

1007 ostream=None, 

1008 tb_offset=0, 

1009 long_header=False, 

1010 include_vars=False, 

1011 check_cache=None, 

1012 debugger_cls=None, 

1013 ): 

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

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

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

1017 

1018 VerboseTB.__init__( 

1019 self, 

1020 theme_name=theme_name, 

1021 call_pdb=call_pdb, 

1022 ostream=ostream, 

1023 tb_offset=tb_offset, 

1024 long_header=long_header, 

1025 include_vars=include_vars, 

1026 check_cache=check_cache, 

1027 debugger_cls=debugger_cls, 

1028 ) 

1029 

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

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

1032 self._join_chars = dict( 

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

1034 ) 

1035 # set_mode also sets the tb_join_char attribute 

1036 self.set_mode(mode) 

1037 

1038 def structured_traceback( 

1039 self, 

1040 etype: type, 

1041 evalue: BaseException | None, 

1042 etb: TracebackType | None = None, 

1043 tb_offset: int | None = None, 

1044 context: int = 5, 

1045 ) -> list[str]: 

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

1047 mode = self.mode 

1048 if mode in self.verbose_modes: 

1049 # Verbose modes need a full traceback 

1050 return VerboseTB.structured_traceback( 

1051 self, etype, evalue, etb, tb_offset, context 

1052 ) 

1053 elif mode == "Docs": 

1054 # return DocTB 

1055 return DocTB( 

1056 theme_name=self._theme_name, 

1057 call_pdb=self.call_pdb, 

1058 ostream=self.ostream, 

1059 tb_offset=tb_offset, 

1060 long_header=self.long_header, 

1061 include_vars=self.include_vars, 

1062 check_cache=self.check_cache, 

1063 debugger_cls=self.debugger_cls, 

1064 ).structured_traceback( 

1065 etype, evalue, etb, tb_offset, 1 

1066 ) # type: ignore[arg-type] 

1067 

1068 elif mode == "Minimal": 

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

1070 else: 

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

1072 # out-of-date source code. 

1073 self.check_cache() 

1074 # Now we can extract and format the exception 

1075 return ListTB.structured_traceback( 

1076 self, etype, evalue, etb, tb_offset, context 

1077 ) 

1078 

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

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

1081 return self.tb_join_char.join(stb) 

1082 

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

1084 """Switch to the desired mode. 

1085 

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

1087 

1088 if not mode: 

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

1090 self.mode = self.valid_modes[new_idx] 

1091 elif mode not in self.valid_modes: 

1092 raise ValueError( 

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

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

1095 ) 

1096 else: 

1097 assert isinstance(mode, str) 

1098 self.mode = mode 

1099 # include variable details only in 'Verbose' mode 

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

1101 # Set the join character for generating text tracebacks 

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

1103 

1104 # some convenient shortcuts 

1105 def plain(self) -> None: 

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

1107 

1108 def context(self) -> None: 

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

1110 

1111 def verbose(self) -> None: 

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

1113 

1114 def minimal(self) -> None: 

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

1116 

1117 

1118# ---------------------------------------------------------------------------- 

1119class AutoFormattedTB(FormattedTB): 

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

1121 

1122 It will find out about exceptions by itself. 

1123 

1124 A brief example:: 

1125 

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

1127 try: 

1128 ... 

1129 except: 

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

1131 """ 

1132 

1133 def __call__( 

1134 self, 

1135 etype: type | None = None, 

1136 evalue: BaseException | None = None, 

1137 etb: TracebackType | None = None, 

1138 out: Any = None, 

1139 tb_offset: int | None = None, 

1140 ) -> None: 

1141 """Print out a formatted exception traceback. 

1142 

1143 Optional arguments: 

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

1145 

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

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

1148 given at initialization time.""" 

1149 

1150 if out is None: 

1151 out = self.ostream 

1152 out.flush() 

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

1154 out.write("\n") 

1155 out.flush() 

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

1157 # that to the clients. 

1158 try: 

1159 self.debugger() 

1160 except KeyboardInterrupt: 

1161 print("\nKeyboardInterrupt") 

1162 

1163 def structured_traceback( 

1164 self, 

1165 etype: type, 

1166 evalue: Optional[BaseException], 

1167 etb: Optional[TracebackType] = None, 

1168 tb_offset: Optional[int] = None, 

1169 context: int = 5, 

1170 ) -> list[str]: 

1171 # tb: TracebackType or tupleof tb types ? 

1172 if etype is None: 

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

1174 if isinstance(etb, tuple): 

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

1176 self.tb = etb[0] 

1177 else: 

1178 self.tb = etb 

1179 return FormattedTB.structured_traceback( 

1180 self, etype, evalue, etb, tb_offset, context 

1181 ) 

1182 

1183 

1184# --------------------------------------------------------------------------- 

1185 

1186 

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

1188class ColorTB(FormattedTB): 

1189 """Deprecated since IPython 9.0.""" 

1190 

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

1192 warnings.warn( 

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

1194 DeprecationWarning, 

1195 stacklevel=2, 

1196 ) 

1197 

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

1199 

1200 

1201class SyntaxTB(ListTB): 

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

1203 

1204 last_syntax_error: BaseException | None 

1205 

1206 def __init__(self, *, theme_name): 

1207 super().__init__(theme_name=theme_name) 

1208 self.last_syntax_error = None 

1209 

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

1211 self.last_syntax_error = value 

1212 

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

1214 

1215 def structured_traceback( 

1216 self, 

1217 etype: type, 

1218 evalue: BaseException | None, 

1219 etb: TracebackType | None = None, 

1220 tb_offset: int | None = None, 

1221 context: int = 5, 

1222 ) -> list[str]: 

1223 value = evalue 

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

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

1226 # the current value. 

1227 if ( 

1228 isinstance(value, SyntaxError) 

1229 and isinstance(value.filename, str) 

1230 and isinstance(value.lineno, int) 

1231 ): 

1232 linecache.checkcache(value.filename) 

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

1234 if newtext: 

1235 value.text = newtext 

1236 self.last_syntax_error = value 

1237 return super(SyntaxTB, self).structured_traceback( 

1238 etype, value, etb, tb_offset=tb_offset, context=context 

1239 ) 

1240 

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

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

1243 e = self.last_syntax_error 

1244 self.last_syntax_error = None 

1245 return e 

1246 

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

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

1249 return "".join(stb)