Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/IPython/core/ultratb.py: 17%

637 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:05 +0000

1# -*- coding: utf-8 -*- 

2""" 

3Verbose and colourful traceback formatting. 

4 

5**ColorTB** 

6 

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

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

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

10text editor. 

11 

12Installation instructions for ColorTB:: 

13 

14 import sys,ultratb 

15 sys.excepthook = ultratb.ColorTB() 

16 

17**VerboseTB** 

18 

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

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

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

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

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

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

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

26 

27.. note:: 

28 

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

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

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

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

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

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

35 

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

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

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

39 Verbose). 

40 

41.. note:: 

42 

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

44 potentially leak sensitive information like access keys, or unencrypted 

45 password. 

46 

47Installation instructions for VerboseTB:: 

48 

49 import sys,ultratb 

50 sys.excepthook = ultratb.VerboseTB() 

51 

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

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

54 

55Color schemes 

56------------- 

57 

58The colors are defined in the class TBTools through the use of the 

59ColorSchemeTable class. Currently the following exist: 

60 

61 - NoColor: allows all of this module to be used in any terminal (the color 

62 escapes are just dummy blank strings). 

63 

64 - Linux: is meant to look good in a terminal like the Linux console (black 

65 or very dark background). 

66 

67 - LightBG: similar to Linux but swaps dark/light colors to be more readable 

68 in light background terminals. 

69 

70 - Neutral: a neutral color scheme that should be readable on both light and 

71 dark background 

72 

73You can implement other color schemes easily, the syntax is fairly 

74self-explanatory. Please send back new schemes you develop to the author for 

75possible inclusion in future releases. 

76 

77Inheritance diagram: 

78 

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

80 :parts: 3 

81""" 

82 

83#***************************************************************************** 

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

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

86# 

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

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

89#***************************************************************************** 

90 

91 

92from collections.abc import Sequence 

93import functools 

94import inspect 

95import linecache 

96import pydoc 

97import sys 

98import time 

99import traceback 

100import types 

101from types import TracebackType 

102from typing import Any, List, Optional, Tuple 

103 

104import stack_data 

105from pygments.formatters.terminal256 import Terminal256Formatter 

106from pygments.styles import get_style_by_name 

107 

108import IPython.utils.colorable as colorable 

109# IPython's own modules 

110from IPython import get_ipython 

111from IPython.core import debugger 

112from IPython.core.display_trap import DisplayTrap 

113from IPython.core.excolors import exception_colors 

114from IPython.utils import PyColorize 

115from IPython.utils import path as util_path 

116from IPython.utils import py3compat 

117from IPython.utils.terminal import get_terminal_size 

118 

119# Globals 

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

121INDENT_SIZE = 8 

122 

123# Default color scheme. This is used, for example, by the traceback 

124# formatter. When running in an actual IPython instance, the user's rc.colors 

125# value is used, but having a module global makes this functionality available 

126# to users of ultratb who are NOT running inside ipython. 

127DEFAULT_SCHEME = 'NoColor' 

128FAST_THRESHOLD = 10_000 

129 

130# --------------------------------------------------------------------------- 

131# Code begins 

132 

133# Helper function -- largely belongs to VerboseTB, but we need the same 

134# functionality to produce a pseudo verbose TB for SyntaxErrors, so that they 

135# can be recognized properly by ipython.el's py-traceback-line-re 

136# (SyntaxErrors have to be treated specially because they have no traceback) 

137 

138 

139@functools.lru_cache() 

140def count_lines_in_py_file(filename: str) -> int: 

141 """ 

142 Given a filename, returns the number of lines in the file 

143 if it ends with the extension ".py". Otherwise, returns 0. 

144 """ 

145 if not filename.endswith(".py"): 

146 return 0 

147 else: 

148 try: 

149 with open(filename, "r") as file: 

150 s = sum(1 for line in file) 

151 except UnicodeError: 

152 return 0 

153 return s 

154 

155 """ 

156 Given a frame object, returns the total number of lines in the file 

157 if the filename ends with the extension ".py". Otherwise, returns 0. 

158 """ 

159 

160 

161def get_line_number_of_frame(frame: types.FrameType) -> int: 

162 """ 

163 Given a frame object, returns the total number of lines in the file 

164 containing the frame's code object, or the number of lines in the 

165 frame's source code if the file is not available. 

166 

167 Parameters 

168 ---------- 

169 frame : FrameType 

170 The frame object whose line number is to be determined. 

171 

172 Returns 

173 ------- 

174 int 

175 The total number of lines in the file containing the frame's 

176 code object, or the number of lines in the frame's source code 

177 if the file is not available. 

178 """ 

179 filename = frame.f_code.co_filename 

180 if filename is None: 

181 print("No file....") 

182 lines, first = inspect.getsourcelines(frame) 

183 return first + len(lines) 

184 return count_lines_in_py_file(filename) 

185 

186 

187def _safe_string(value, what, func=str): 

188 # Copied from cpython/Lib/traceback.py 

189 try: 

190 return func(value) 

191 except: 

192 return f"<{what} {func.__name__}() failed>" 

193 

194 

195def _format_traceback_lines(lines, Colors, has_colors: bool, lvals): 

196 """ 

197 Format tracebacks lines with pointing arrow, leading numbers... 

198 

199 Parameters 

200 ---------- 

201 lines : list[Line] 

202 Colors 

203 ColorScheme used. 

204 lvals : str 

205 Values of local variables, already colored, to inject just after the error line. 

206 """ 

207 numbers_width = INDENT_SIZE - 1 

208 res = [] 

209 

210 for stack_line in lines: 

211 if stack_line is stack_data.LINE_GAP: 

212 res.append('%s (...)%s\n' % (Colors.linenoEm, Colors.Normal)) 

213 continue 

214 

215 line = stack_line.render(pygmented=has_colors).rstrip('\n') + '\n' 

216 lineno = stack_line.lineno 

217 if stack_line.is_current: 

218 # This is the line with the error 

219 pad = numbers_width - len(str(lineno)) 

220 num = '%s%s' % (debugger.make_arrow(pad), str(lineno)) 

221 start_color = Colors.linenoEm 

222 else: 

223 num = '%*s' % (numbers_width, lineno) 

224 start_color = Colors.lineno 

225 

226 line = '%s%s%s %s' % (start_color, num, Colors.Normal, line) 

227 

228 res.append(line) 

229 if lvals and stack_line.is_current: 

230 res.append(lvals + '\n') 

231 return res 

232 

233def _simple_format_traceback_lines(lnum, index, lines, Colors, lvals, _line_format): 

234 """ 

235 Format tracebacks lines with pointing arrow, leading numbers... 

236 

237 Parameters 

238 ========== 

239 

240 lnum: int 

241 number of the target line of code. 

242 index: int 

243 which line in the list should be highlighted. 

244 lines: list[string] 

245 Colors: 

246 ColorScheme used. 

247 lvals: bytes 

248 Values of local variables, already colored, to inject just after the error line. 

249 _line_format: f (str) -> (str, bool) 

250 return (colorized version of str, failure to do so) 

251 """ 

252 numbers_width = INDENT_SIZE - 1 

253 res = [] 

254 for i, line in enumerate(lines, lnum - index): 

255 # assert isinstance(line, str) 

256 line = py3compat.cast_unicode(line) 

257 

258 new_line, err = _line_format(line, "str") 

259 if not err: 

260 line = new_line 

261 

262 if i == lnum: 

263 # This is the line with the error 

264 pad = numbers_width - len(str(i)) 

265 num = "%s%s" % (debugger.make_arrow(pad), str(lnum)) 

266 line = "%s%s%s %s%s" % ( 

267 Colors.linenoEm, 

268 num, 

269 Colors.line, 

270 line, 

271 Colors.Normal, 

272 ) 

273 else: 

274 num = "%*s" % (numbers_width, i) 

275 line = "%s%s%s %s" % (Colors.lineno, num, Colors.Normal, line) 

276 

277 res.append(line) 

278 if lvals and i == lnum: 

279 res.append(lvals + "\n") 

280 return res 

281 

282 

283def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None): 

284 """ 

285 Format filename lines with custom formatting from caching compiler or `File *.py` by default 

286 

287 Parameters 

288 ---------- 

289 file : str 

290 ColorFilename 

291 ColorScheme's filename coloring to be used. 

292 ColorNormal 

293 ColorScheme's normal coloring to be used. 

294 """ 

295 ipinst = get_ipython() 

296 if ( 

297 ipinst is not None 

298 and (data := ipinst.compile.format_code_name(file)) is not None 

299 ): 

300 label, name = data 

301 if lineno is None: 

302 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}" 

303 else: 

304 tpl_link = ( 

305 f"{{label}} {ColorFilename}{{name}}, line {{lineno}}{ColorNormal}" 

306 ) 

307 else: 

308 label = "File" 

309 name = util_path.compress_user( 

310 py3compat.cast_unicode(file, util_path.fs_encoding) 

311 ) 

312 if lineno is None: 

313 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}" 

314 else: 

315 # can we make this the more friendly ", line {{lineno}}", or do we need to preserve the formatting with the colon? 

316 tpl_link = f"{{label}} {ColorFilename}{{name}}:{{lineno}}{ColorNormal}" 

317 

318 return tpl_link.format(label=label, name=name, lineno=lineno) 

319 

320#--------------------------------------------------------------------------- 

321# Module classes 

322class TBTools(colorable.Colorable): 

323 """Basic tools used by all traceback printer classes.""" 

324 

325 # Number of frames to skip when reporting tracebacks 

326 tb_offset = 0 

327 

328 def __init__( 

329 self, 

330 color_scheme="NoColor", 

331 call_pdb=False, 

332 ostream=None, 

333 parent=None, 

334 config=None, 

335 *, 

336 debugger_cls=None, 

337 ): 

338 # Whether to call the interactive pdb debugger after printing 

339 # tracebacks or not 

340 super(TBTools, self).__init__(parent=parent, config=config) 

341 self.call_pdb = call_pdb 

342 

343 # Output stream to write to. Note that we store the original value in 

344 # a private attribute and then make the public ostream a property, so 

345 # that we can delay accessing sys.stdout until runtime. The way 

346 # things are written now, the sys.stdout object is dynamically managed 

347 # so a reference to it should NEVER be stored statically. This 

348 # property approach confines this detail to a single location, and all 

349 # subclasses can simply access self.ostream for writing. 

350 self._ostream = ostream 

351 

352 # Create color table 

353 self.color_scheme_table = exception_colors() 

354 

355 self.set_colors(color_scheme) 

356 self.old_scheme = color_scheme # save initial value for toggles 

357 self.debugger_cls = debugger_cls or debugger.Pdb 

358 

359 if call_pdb: 

360 self.pdb = self.debugger_cls() 

361 else: 

362 self.pdb = None 

363 

364 def _get_ostream(self): 

365 """Output stream that exceptions are written to. 

366 

367 Valid values are: 

368 

369 - None: the default, which means that IPython will dynamically resolve 

370 to sys.stdout. This ensures compatibility with most tools, including 

371 Windows (where plain stdout doesn't recognize ANSI escapes). 

372 

373 - Any object with 'write' and 'flush' attributes. 

374 """ 

375 return sys.stdout if self._ostream is None else self._ostream 

376 

377 def _set_ostream(self, val): 

378 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush')) 

379 self._ostream = val 

380 

381 ostream = property(_get_ostream, _set_ostream) 

382 

383 @staticmethod 

384 def _get_chained_exception(exception_value): 

385 cause = getattr(exception_value, "__cause__", None) 

386 if cause: 

387 return cause 

388 if getattr(exception_value, "__suppress_context__", False): 

389 return None 

390 return getattr(exception_value, "__context__", None) 

391 

392 def get_parts_of_chained_exception( 

393 self, evalue 

394 ) -> Optional[Tuple[type, BaseException, TracebackType]]: 

395 chained_evalue = self._get_chained_exception(evalue) 

396 

397 if chained_evalue: 

398 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__ 

399 return None 

400 

401 def prepare_chained_exception_message(self, cause) -> List[Any]: 

402 direct_cause = "\nThe above exception was the direct cause of the following exception:\n" 

403 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n" 

404 

405 if cause: 

406 message = [[direct_cause]] 

407 else: 

408 message = [[exception_during_handling]] 

409 return message 

410 

411 @property 

412 def has_colors(self) -> bool: 

413 return self.color_scheme_table.active_scheme_name.lower() != "nocolor" 

414 

415 def set_colors(self, *args, **kw): 

416 """Shorthand access to the color table scheme selector method.""" 

417 

418 # Set own color table 

419 self.color_scheme_table.set_active_scheme(*args, **kw) 

420 # for convenience, set Colors to the active scheme 

421 self.Colors = self.color_scheme_table.active_colors 

422 # Also set colors of debugger 

423 if hasattr(self, 'pdb') and self.pdb is not None: 

424 self.pdb.set_colors(*args, **kw) 

425 

426 def color_toggle(self): 

427 """Toggle between the currently active color scheme and NoColor.""" 

428 

429 if self.color_scheme_table.active_scheme_name == 'NoColor': 

430 self.color_scheme_table.set_active_scheme(self.old_scheme) 

431 self.Colors = self.color_scheme_table.active_colors 

432 else: 

433 self.old_scheme = self.color_scheme_table.active_scheme_name 

434 self.color_scheme_table.set_active_scheme('NoColor') 

435 self.Colors = self.color_scheme_table.active_colors 

436 

437 def stb2text(self, stb): 

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

439 return '\n'.join(stb) 

440 

441 def text(self, etype, value, tb, tb_offset: Optional[int] = None, context=5): 

442 """Return formatted traceback. 

443 

444 Subclasses may override this if they add extra arguments. 

445 """ 

446 tb_list = self.structured_traceback(etype, value, tb, 

447 tb_offset, context) 

448 return self.stb2text(tb_list) 

449 

450 def structured_traceback( 

451 self, 

452 etype: type, 

453 evalue: Optional[BaseException], 

454 etb: Optional[TracebackType] = None, 

455 tb_offset: Optional[int] = None, 

456 number_of_lines_of_context: int = 5, 

457 ): 

458 """Return a list of traceback frames. 

459 

460 Must be implemented by each class. 

461 """ 

462 raise NotImplementedError() 

463 

464 

465#--------------------------------------------------------------------------- 

466class ListTB(TBTools): 

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

468 

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

470 as would be obtained by:: 

471 

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

473 if tb: 

474 elist = traceback.extract_tb(tb) 

475 else: 

476 elist = None 

477 

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

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

480 standard library). 

481 

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

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

484 

485 

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

487 self.ostream.flush() 

488 self.ostream.write(self.text(etype, value, elist)) 

489 self.ostream.write('\n') 

490 

491 def _extract_tb(self, tb): 

492 if tb: 

493 return traceback.extract_tb(tb) 

494 else: 

495 return None 

496 

497 def structured_traceback( 

498 self, 

499 etype: type, 

500 evalue: Optional[BaseException], 

501 etb: Optional[TracebackType] = None, 

502 tb_offset: Optional[int] = None, 

503 context=5, 

504 ): 

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

506 

507 Parameters 

508 ---------- 

509 etype : exception type 

510 Type of the exception raised. 

511 evalue : object 

512 Data stored in the exception 

513 etb : list | TracebackType | None 

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

515 If Traceback: Traceback of the exception. 

516 tb_offset : int, optional 

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

518 instance evalue is used (set in constructor). 

519 context : int, optional 

520 Number of lines of context information to print. 

521 

522 Returns 

523 ------- 

524 String with formatted exception. 

525 """ 

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

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

528 if isinstance(etb, tuple): 

529 etb, chained_exc_ids = etb 

530 else: 

531 chained_exc_ids = set() 

532 

533 if isinstance(etb, list): 

534 elist = etb 

535 elif etb is not None: 

536 elist = self._extract_tb(etb) 

537 else: 

538 elist = [] 

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

540 assert isinstance(tb_offset, int) 

541 Colors = self.Colors 

542 out_list = [] 

543 if elist: 

544 

545 if tb_offset and len(elist) > tb_offset: 

546 elist = elist[tb_offset:] 

547 

548 out_list.append('Traceback %s(most recent call last)%s:' % 

549 (Colors.normalEm, Colors.Normal) + '\n') 

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

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

552 lines = ''.join(self._format_exception_only(etype, evalue)) 

553 out_list.append(lines) 

554 

555 exception = self.get_parts_of_chained_exception(evalue) 

556 

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

558 chained_exception_message = ( 

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

560 if evalue is not None 

561 else "" 

562 ) 

563 etype, evalue, etb = exception 

564 # Trace exception to avoid infinite 'cause' loop 

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

566 chained_exceptions_tb_offset = 0 

567 out_list = ( 

568 self.structured_traceback( 

569 etype, 

570 evalue, 

571 (etb, chained_exc_ids), # type: ignore 

572 chained_exceptions_tb_offset, 

573 context, 

574 ) 

575 + chained_exception_message 

576 + out_list) 

577 

578 return out_list 

579 

580 def _format_list(self, extracted_list): 

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

582 

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

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

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

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

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

588 whose source text line is not None. 

589 

590 Lifted almost verbatim from traceback.py 

591 """ 

592 

593 Colors = self.Colors 

594 output_list = [] 

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

596 normalCol, nameCol, fileCol, lineCol = ( 

597 # Emphasize the last entry 

598 (Colors.normalEm, Colors.nameEm, Colors.filenameEm, Colors.line) 

599 if ind == len(extracted_list) - 1 

600 else (Colors.Normal, Colors.name, Colors.filename, "") 

601 ) 

602 

603 fns = _format_filename(filename, fileCol, normalCol, lineno=lineno) 

604 item = f"{normalCol} {fns}" 

605 

606 if name != "<module>": 

607 item += f" in {nameCol}{name}{normalCol}\n" 

608 else: 

609 item += "\n" 

610 if line: 

611 item += f"{lineCol} {line.strip()}{normalCol}\n" 

612 output_list.append(item) 

613 

614 return output_list 

615 

616 def _format_exception_only(self, etype, value): 

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

618 

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

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

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

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

623 printed) display detailed information about where the syntax error 

624 occurred. The message indicating which exception occurred is the 

625 always last string in the list. 

626 

627 Also lifted nearly verbatim from traceback.py 

628 """ 

629 have_filedata = False 

630 Colors = self.Colors 

631 output_list = [] 

632 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal) 

633 if value is None: 

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

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

636 else: 

637 if issubclass(etype, SyntaxError): 

638 have_filedata = True 

639 if not value.filename: value.filename = "<string>" 

640 if value.lineno: 

641 lineno = value.lineno 

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

643 else: 

644 lineno = "unknown" 

645 textline = "" 

646 output_list.append( 

647 "%s %s%s\n" 

648 % ( 

649 Colors.normalEm, 

650 _format_filename( 

651 value.filename, 

652 Colors.filenameEm, 

653 Colors.normalEm, 

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

655 ), 

656 Colors.Normal, 

657 ) 

658 ) 

659 if textline == "": 

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

661 

662 if textline is not None: 

663 i = 0 

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

665 i += 1 

666 output_list.append( 

667 "%s %s%s\n" % (Colors.line, textline.strip(), Colors.Normal) 

668 ) 

669 if value.offset is not None: 

670 s = ' ' 

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

672 if c.isspace(): 

673 s += c 

674 else: 

675 s += " " 

676 output_list.append( 

677 "%s%s^%s\n" % (Colors.caret, s, Colors.Normal) 

678 ) 

679 

680 try: 

681 s = value.msg 

682 except Exception: 

683 s = self._some_str(value) 

684 if s: 

685 output_list.append( 

686 "%s%s:%s %s\n" % (stype, Colors.excName, Colors.Normal, s) 

687 ) 

688 else: 

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

690 

691 # PEP-678 notes 

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

693 

694 # sync with user hooks 

695 if have_filedata: 

696 ipinst = get_ipython() 

697 if ipinst is not None: 

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

699 

700 return output_list 

701 

702 def get_exception_only(self, etype, value): 

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

704 

705 Parameters 

706 ---------- 

707 etype : exception type 

708 value : exception value 

709 """ 

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

711 

712 def show_exception_only(self, etype, evalue): 

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

714 

715 Parameters 

716 ---------- 

717 etype : exception type 

718 evalue : exception value 

719 """ 

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

721 # a subclass whose signature or behavior may be different 

722 ostream = self.ostream 

723 ostream.flush() 

724 ostream.write('\n'.join(self.get_exception_only(etype, evalue))) 

725 ostream.flush() 

726 

727 def _some_str(self, value): 

728 # Lifted from traceback.py 

729 try: 

730 return py3compat.cast_unicode(str(value)) 

731 except: 

732 return u'<unprintable %s object>' % type(value).__name__ 

733 

734 

735class FrameInfo: 

736 """ 

737 Mirror of stack data's FrameInfo, but so that we can bypass highlighting on 

738 really long frames. 

739 """ 

740 

741 description: Optional[str] 

742 filename: Optional[str] 

743 lineno: Tuple[int] 

744 # number of context lines to use 

745 context: Optional[int] 

746 

747 @classmethod 

748 def _from_stack_data_FrameInfo(cls, frame_info): 

749 return cls( 

750 getattr(frame_info, "description", None), 

751 getattr(frame_info, "filename", None), # type: ignore[arg-type] 

752 getattr(frame_info, "lineno", None), # type: ignore[arg-type] 

753 getattr(frame_info, "frame", None), 

754 getattr(frame_info, "code", None), 

755 sd=frame_info, 

756 context=None, 

757 ) 

758 

759 def __init__( 

760 self, 

761 description: Optional[str], 

762 filename: str, 

763 lineno: Tuple[int], 

764 frame, 

765 code, 

766 *, 

767 sd=None, 

768 context=None, 

769 ): 

770 self.description = description 

771 self.filename = filename 

772 self.lineno = lineno 

773 self.frame = frame 

774 self.code = code 

775 self._sd = sd 

776 self.context = context 

777 

778 # self.lines = [] 

779 if sd is None: 

780 ix = inspect.getsourcelines(frame) 

781 self.raw_lines = ix[0] 

782 

783 @property 

784 def variables_in_executing_piece(self): 

785 if self._sd: 

786 return self._sd.variables_in_executing_piece 

787 else: 

788 return [] 

789 

790 @property 

791 def lines(self): 

792 return self._sd.lines 

793 

794 @property 

795 def executing(self): 

796 if self._sd: 

797 return self._sd.executing 

798 else: 

799 return None 

800 

801 

802# ---------------------------------------------------------------------------- 

803class VerboseTB(TBTools): 

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

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

806 

807 Modified version which optionally strips the topmost entries from the 

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

809 would appear in the traceback).""" 

810 

811 _tb_highlight = "bg:ansiyellow" 

812 _tb_highlight_style = "default" 

813 

814 def __init__( 

815 self, 

816 color_scheme: str = "Linux", 

817 call_pdb: bool = False, 

818 ostream=None, 

819 tb_offset: int = 0, 

820 long_header: bool = False, 

821 include_vars: bool = True, 

822 check_cache=None, 

823 debugger_cls=None, 

824 parent=None, 

825 config=None, 

826 ): 

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

828 

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

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

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

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

833 TBTools.__init__( 

834 self, 

835 color_scheme=color_scheme, 

836 call_pdb=call_pdb, 

837 ostream=ostream, 

838 parent=parent, 

839 config=config, 

840 debugger_cls=debugger_cls, 

841 ) 

842 self.tb_offset = tb_offset 

843 self.long_header = long_header 

844 self.include_vars = include_vars 

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

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

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

848 if check_cache is None: 

849 check_cache = linecache.checkcache 

850 self.check_cache = check_cache 

851 

852 self.skip_hidden = True 

853 

854 def format_record(self, frame_info: FrameInfo): 

855 """Format a single stack frame""" 

856 assert isinstance(frame_info, FrameInfo) 

857 Colors = self.Colors # just a shorthand + quicker name lookup 

858 ColorsNormal = Colors.Normal # used a lot 

859 

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

861 return ' %s[... skipping similar frames: %s]%s\n' % ( 

862 Colors.excName, frame_info.description, ColorsNormal) 

863 

864 indent = " " * INDENT_SIZE 

865 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal) 

866 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}" 

867 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % ( 

868 Colors.vName, 

869 Colors.valEm, 

870 ColorsNormal, 

871 ) 

872 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal) 

873 

874 link = _format_filename( 

875 frame_info.filename, 

876 Colors.filenameEm, 

877 ColorsNormal, 

878 lineno=frame_info.lineno, 

879 ) 

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

881 if frame_info.executing is not None: 

882 func = frame_info.executing.code_qualname() 

883 else: 

884 func = "?" 

885 if func == "<module>": 

886 call = "" 

887 else: 

888 # Decide whether to include variable details or not 

889 var_repr = eqrepr if self.include_vars else nullrepr 

890 try: 

891 scope = inspect.formatargvalues( 

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

893 ) 

894 call = tpl_call.format(file=func, scope=scope) 

895 except KeyError: 

896 # This happens in situations like errors inside generator 

897 # expressions, where local variables are listed in the 

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

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

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

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

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

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

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

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

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

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

908 # test, but running a script consisting of: 

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

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

911 # disabled. 

912 call = tpl_call_fail % func 

913 

914 lvals = '' 

915 lvals_list = [] 

916 if self.include_vars: 

917 try: 

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

919 # still need a workaround. 

920 fibp = frame_info.variables_in_executing_piece 

921 for var in fibp: 

922 lvals_list.append(tpl_name_val % (var.name, repr(var.value))) 

923 except Exception: 

924 lvals_list.append( 

925 "Exception trying to inspect frame. No more locals available." 

926 ) 

927 if lvals_list: 

928 lvals = '%s%s' % (indent, em_normal.join(lvals_list)) 

929 

930 result = f'{link}{", " if call else ""}{call}\n' 

931 if frame_info._sd is None: 

932 # fast fallback if file is too long 

933 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal) 

934 link = tpl_link % util_path.compress_user(frame_info.filename) 

935 level = "%s %s\n" % (link, call) 

936 _line_format = PyColorize.Parser( 

937 style=self.color_scheme_table.active_scheme_name, parent=self 

938 ).format2 

939 first_line = frame_info.code.co_firstlineno 

940 current_line = frame_info.lineno[0] 

941 raw_lines = frame_info.raw_lines 

942 index = current_line - first_line 

943 

944 if index >= frame_info.context: 

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

946 stop = index + frame_info.context 

947 index = frame_info.context 

948 else: 

949 start = 0 

950 stop = index + frame_info.context 

951 raw_lines = raw_lines[start:stop] 

952 

953 return "%s%s" % ( 

954 level, 

955 "".join( 

956 _simple_format_traceback_lines( 

957 current_line, 

958 index, 

959 raw_lines, 

960 Colors, 

961 lvals, 

962 _line_format, 

963 ) 

964 ), 

965 ) 

966 # result += "\n".join(frame_info.raw_lines) 

967 else: 

968 result += "".join( 

969 _format_traceback_lines( 

970 frame_info.lines, Colors, self.has_colors, lvals 

971 ) 

972 ) 

973 return result 

974 

975 def prepare_header(self, etype: str, long_version: bool = False): 

976 colors = self.Colors # just a shorthand + quicker name lookup 

977 colorsnormal = colors.Normal # used a lot 

978 exc = '%s%s%s' % (colors.excName, etype, colorsnormal) 

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

980 if long_version: 

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

982 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable 

983 date = time.ctime(time.time()) 

984 

985 head = "%s%s%s\n%s%s%s\n%s" % ( 

986 colors.topline, 

987 "-" * width, 

988 colorsnormal, 

989 exc, 

990 " " * (width - len(etype) - len(pyver)), 

991 pyver, 

992 date.rjust(width), 

993 ) 

994 head += ( 

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

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

997 ) 

998 else: 

999 # Simplified header 

1000 head = "%s%s" % ( 

1001 exc, 

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

1003 ) 

1004 

1005 return head 

1006 

1007 def format_exception(self, etype, evalue): 

1008 colors = self.Colors # just a shorthand + quicker name lookup 

1009 colorsnormal = colors.Normal # used a lot 

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

1011 try: 

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

1013 except: 

1014 # User exception is improperly defined. 

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

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

1017 

1018 # PEP-678 notes 

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

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

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

1022 

1023 # ... and format it 

1024 return [ 

1025 "{}{}{}: {}".format( 

1026 colors.excName, 

1027 etype_str, 

1028 colorsnormal, 

1029 py3compat.cast_unicode(evalue_str), 

1030 ), 

1031 *( 

1032 "{}{}".format( 

1033 colorsnormal, _safe_string(py3compat.cast_unicode(n), "note") 

1034 ) 

1035 for n in notes 

1036 ), 

1037 ] 

1038 

1039 def format_exception_as_a_whole( 

1040 self, 

1041 etype: type, 

1042 evalue: Optional[BaseException], 

1043 etb: Optional[TracebackType], 

1044 number_of_lines_of_context, 

1045 tb_offset: Optional[int], 

1046 ): 

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

1048 

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

1050 (PEP 3134). 

1051 """ 

1052 # some locals 

1053 orig_etype = etype 

1054 try: 

1055 etype = etype.__name__ # type: ignore 

1056 except AttributeError: 

1057 pass 

1058 

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

1060 assert isinstance(tb_offset, int) 

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

1062 records = ( 

1063 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else [] 

1064 ) 

1065 

1066 frames = [] 

1067 skipped = 0 

1068 lastrecord = len(records) - 1 

1069 for i, record in enumerate(records): 

1070 if ( 

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

1072 and self.skip_hidden 

1073 ): 

1074 if ( 

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

1076 and i != lastrecord 

1077 ): 

1078 skipped += 1 

1079 continue 

1080 if skipped: 

1081 Colors = self.Colors # just a shorthand + quicker name lookup 

1082 ColorsNormal = Colors.Normal # used a lot 

1083 frames.append( 

1084 " %s[... skipping hidden %s frame]%s\n" 

1085 % (Colors.excName, skipped, ColorsNormal) 

1086 ) 

1087 skipped = 0 

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

1089 if skipped: 

1090 Colors = self.Colors # just a shorthand + quicker name lookup 

1091 ColorsNormal = Colors.Normal # used a lot 

1092 frames.append( 

1093 " %s[... skipping hidden %s frame]%s\n" 

1094 % (Colors.excName, skipped, ColorsNormal) 

1095 ) 

1096 

1097 formatted_exception = self.format_exception(etype, evalue) 

1098 if records: 

1099 frame_info = records[-1] 

1100 ipinst = get_ipython() 

1101 if ipinst is not None: 

1102 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0) 

1103 

1104 return [[head] + frames + formatted_exception] 

1105 

1106 def get_records( 

1107 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int 

1108 ): 

1109 assert etb is not None 

1110 context = number_of_lines_of_context - 1 

1111 after = context // 2 

1112 before = context - after 

1113 if self.has_colors: 

1114 style = get_style_by_name(self._tb_highlight_style) 

1115 style = stack_data.style_with_executing_node(style, self._tb_highlight) 

1116 formatter = Terminal256Formatter(style=style) 

1117 else: 

1118 formatter = None 

1119 options = stack_data.Options( 

1120 before=before, 

1121 after=after, 

1122 pygments_formatter=formatter, 

1123 ) 

1124 

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

1126 cf: Optional[TracebackType] = etb 

1127 max_len = 0 

1128 tbs = [] 

1129 while cf is not None: 

1130 try: 

1131 mod = inspect.getmodule(cf.tb_frame) 

1132 if mod is not None: 

1133 mod_name = mod.__name__ 

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

1135 if root_name == "IPython": 

1136 cf = cf.tb_next 

1137 continue 

1138 max_len = get_line_number_of_frame(cf.tb_frame) 

1139 

1140 except OSError: 

1141 max_len = 0 

1142 max_len = max(max_len, max_len) 

1143 tbs.append(cf) 

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

1145 

1146 if max_len > FAST_THRESHOLD: 

1147 FIs = [] 

1148 for tb in tbs: 

1149 frame = tb.tb_frame # type: ignore 

1150 lineno = (frame.f_lineno,) 

1151 code = frame.f_code 

1152 filename = code.co_filename 

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

1154 FIs.append( 

1155 FrameInfo( 

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

1157 ) 

1158 ) 

1159 return FIs 

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

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

1162 return res 

1163 

1164 def structured_traceback( 

1165 self, 

1166 etype: type, 

1167 evalue: Optional[BaseException], 

1168 etb: Optional[TracebackType] = None, 

1169 tb_offset: Optional[int] = None, 

1170 number_of_lines_of_context: int = 5, 

1171 ): 

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

1173 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context, 

1174 tb_offset) 

1175 

1176 colors = self.Colors # just a shorthand + quicker name lookup 

1177 colorsnormal = colors.Normal # used a lot 

1178 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal) 

1179 structured_traceback_parts = [head] 

1180 chained_exceptions_tb_offset = 0 

1181 lines_of_context = 3 

1182 formatted_exceptions = formatted_exception 

1183 exception = self.get_parts_of_chained_exception(evalue) 

1184 if exception: 

1185 assert evalue is not None 

1186 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__) 

1187 etype, evalue, etb = exception 

1188 else: 

1189 evalue = None 

1190 chained_exc_ids = set() 

1191 while evalue: 

1192 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context, 

1193 chained_exceptions_tb_offset) 

1194 exception = self.get_parts_of_chained_exception(evalue) 

1195 

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

1197 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop 

1198 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__) 

1199 etype, evalue, etb = exception 

1200 else: 

1201 evalue = None 

1202 

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

1204 # the first exception should be on top 

1205 for formatted_exception in reversed(formatted_exceptions): 

1206 structured_traceback_parts += formatted_exception 

1207 

1208 return structured_traceback_parts 

1209 

1210 def debugger(self, force: bool = False): 

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

1212 reference. 

1213 

1214 Keywords: 

1215 

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

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

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

1219 is false. 

1220 

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

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

1223 is deleted to prevent lingering references which hamper memory 

1224 management. 

1225 

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

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

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

1229 

1230 if force or self.call_pdb: 

1231 if self.pdb is None: 

1232 self.pdb = self.debugger_cls() 

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

1234 # for pdb 

1235 display_trap = DisplayTrap(hook=sys.__displayhook__) 

1236 with display_trap: 

1237 self.pdb.reset() 

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

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

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

1241 else: 

1242 etb = self.tb = sys.last_traceback 

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

1244 assert self.tb.tb_next is not None 

1245 self.tb = self.tb.tb_next 

1246 if etb and etb.tb_next: 

1247 etb = etb.tb_next 

1248 self.pdb.botframe = etb.tb_frame 

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

1250 # please check why later and remove the getattr. 

1251 exc = sys.last_value if sys.version_info < (3, 12) else getattr(sys, "last_exc", sys.last_value) # type: ignore[attr-defined] 

1252 if exc: 

1253 self.pdb.interaction(None, exc) 

1254 else: 

1255 self.pdb.interaction(None, etb) 

1256 

1257 if hasattr(self, 'tb'): 

1258 del self.tb 

1259 

1260 def handler(self, info=None): 

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

1262 self.tb = etb 

1263 ostream = self.ostream 

1264 ostream.flush() 

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

1266 ostream.write('\n') 

1267 ostream.flush() 

1268 

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

1270 # out the right info on its own. 

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

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

1273 if etb is None: 

1274 self.handler() 

1275 else: 

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

1277 try: 

1278 self.debugger() 

1279 except KeyboardInterrupt: 

1280 print("\nKeyboardInterrupt") 

1281 

1282 

1283#---------------------------------------------------------------------------- 

1284class FormattedTB(VerboseTB, ListTB): 

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

1286 

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

1288 

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

1290 

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

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

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

1294 like Python shells). """ 

1295 

1296 mode: str 

1297 

1298 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False, 

1299 ostream=None, 

1300 tb_offset=0, long_header=False, include_vars=False, 

1301 check_cache=None, debugger_cls=None, 

1302 parent=None, config=None): 

1303 

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

1305 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal'] 

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

1307 

1308 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb, 

1309 ostream=ostream, tb_offset=tb_offset, 

1310 long_header=long_header, include_vars=include_vars, 

1311 check_cache=check_cache, debugger_cls=debugger_cls, 

1312 parent=parent, config=config) 

1313 

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

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

1316 self._join_chars = dict(Plain='', Context='\n', Verbose='\n', 

1317 Minimal='') 

1318 # set_mode also sets the tb_join_char attribute 

1319 self.set_mode(mode) 

1320 

1321 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5): 

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

1323 mode = self.mode 

1324 if mode in self.verbose_modes: 

1325 # Verbose modes need a full traceback 

1326 return VerboseTB.structured_traceback( 

1327 self, etype, value, tb, tb_offset, number_of_lines_of_context 

1328 ) 

1329 elif mode == 'Minimal': 

1330 return ListTB.get_exception_only(self, etype, value) 

1331 else: 

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

1333 # out-of-date source code. 

1334 self.check_cache() 

1335 # Now we can extract and format the exception 

1336 return ListTB.structured_traceback( 

1337 self, etype, value, tb, tb_offset, number_of_lines_of_context 

1338 ) 

1339 

1340 def stb2text(self, stb): 

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

1342 return self.tb_join_char.join(stb) 

1343 

1344 def set_mode(self, mode: Optional[str] = None): 

1345 """Switch to the desired mode. 

1346 

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

1348 

1349 if not mode: 

1350 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \ 

1351 len(self.valid_modes) 

1352 self.mode = self.valid_modes[new_idx] 

1353 elif mode not in self.valid_modes: 

1354 raise ValueError( 

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

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

1357 ) 

1358 else: 

1359 assert isinstance(mode, str) 

1360 self.mode = mode 

1361 # include variable details only in 'Verbose' mode 

1362 self.include_vars = (self.mode == self.valid_modes[2]) 

1363 # Set the join character for generating text tracebacks 

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

1365 

1366 # some convenient shortcuts 

1367 def plain(self): 

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

1369 

1370 def context(self): 

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

1372 

1373 def verbose(self): 

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

1375 

1376 def minimal(self): 

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

1378 

1379 

1380#---------------------------------------------------------------------------- 

1381class AutoFormattedTB(FormattedTB): 

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

1383 

1384 It will find out about exceptions by itself. 

1385 

1386 A brief example:: 

1387 

1388 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux') 

1389 try: 

1390 ... 

1391 except: 

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

1393 """ 

1394 

1395 def __call__(self, etype=None, evalue=None, etb=None, 

1396 out=None, tb_offset=None): 

1397 """Print out a formatted exception traceback. 

1398 

1399 Optional arguments: 

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

1401 

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

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

1404 given at initialization time.""" 

1405 

1406 if out is None: 

1407 out = self.ostream 

1408 out.flush() 

1409 out.write(self.text(etype, evalue, etb, tb_offset)) 

1410 out.write('\n') 

1411 out.flush() 

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

1413 # that to the clients. 

1414 try: 

1415 self.debugger() 

1416 except KeyboardInterrupt: 

1417 print("\nKeyboardInterrupt") 

1418 

1419 def structured_traceback( 

1420 self, 

1421 etype: type, 

1422 evalue: Optional[BaseException], 

1423 etb: Optional[TracebackType] = None, 

1424 tb_offset: Optional[int] = None, 

1425 number_of_lines_of_context: int = 5, 

1426 ): 

1427 # tb: TracebackType or tupleof tb types ? 

1428 if etype is None: 

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

1430 if isinstance(etb, tuple): 

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

1432 self.tb = etb[0] 

1433 else: 

1434 self.tb = etb 

1435 return FormattedTB.structured_traceback( 

1436 self, etype, evalue, etb, tb_offset, number_of_lines_of_context 

1437 ) 

1438 

1439 

1440#--------------------------------------------------------------------------- 

1441 

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

1443class ColorTB(FormattedTB): 

1444 """Shorthand to initialize a FormattedTB in Linux colors mode.""" 

1445 

1446 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs): 

1447 FormattedTB.__init__(self, color_scheme=color_scheme, 

1448 call_pdb=call_pdb, **kwargs) 

1449 

1450 

1451class SyntaxTB(ListTB): 

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

1453 

1454 def __init__(self, color_scheme='NoColor', parent=None, config=None): 

1455 ListTB.__init__(self, color_scheme, parent=parent, config=config) 

1456 self.last_syntax_error = None 

1457 

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

1459 self.last_syntax_error = value 

1460 

1461 ListTB.__call__(self, etype, value, elist) 

1462 

1463 def structured_traceback(self, etype, value, elist, tb_offset=None, 

1464 context=5): 

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

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

1467 # the current value. 

1468 if isinstance(value, SyntaxError) \ 

1469 and isinstance(value.filename, str) \ 

1470 and isinstance(value.lineno, int): 

1471 linecache.checkcache(value.filename) 

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

1473 if newtext: 

1474 value.text = newtext 

1475 self.last_syntax_error = value 

1476 return super(SyntaxTB, self).structured_traceback(etype, value, elist, 

1477 tb_offset=tb_offset, context=context) 

1478 

1479 def clear_err_state(self): 

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

1481 e = self.last_syntax_error 

1482 self.last_syntax_error = None 

1483 return e 

1484 

1485 def stb2text(self, stb): 

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

1487 return ''.join(stb) 

1488 

1489 

1490# some internal-use functions 

1491def text_repr(value): 

1492 """Hopefully pretty robust repr equivalent.""" 

1493 # this is pretty horrible but should always return *something* 

1494 try: 

1495 return pydoc.text.repr(value) # type: ignore[call-arg] 

1496 except KeyboardInterrupt: 

1497 raise 

1498 except: 

1499 try: 

1500 return repr(value) 

1501 except KeyboardInterrupt: 

1502 raise 

1503 except: 

1504 try: 

1505 # all still in an except block so we catch 

1506 # getattr raising 

1507 name = getattr(value, '__name__', None) 

1508 if name: 

1509 # ick, recursion 

1510 return text_repr(name) 

1511 klass = getattr(value, '__class__', None) 

1512 if klass: 

1513 return '%s instance' % text_repr(klass) 

1514 except KeyboardInterrupt: 

1515 raise 

1516 except: 

1517 return 'UNRECOVERABLE REPR FAILURE' 

1518 

1519 

1520def eqrepr(value, repr=text_repr): 

1521 return '=%s' % repr(value) 

1522 

1523 

1524def nullrepr(value, repr=text_repr): 

1525 return ''