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

623 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +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 

92import functools 

93import inspect 

94import linecache 

95import pydoc 

96import sys 

97import time 

98import traceback 

99import types 

100from types import TracebackType 

101from typing import Any, List, Optional, Tuple 

102 

103import stack_data 

104from pygments.formatters.terminal256 import Terminal256Formatter 

105from pygments.styles import get_style_by_name 

106 

107import IPython.utils.colorable as colorable 

108# IPython's own modules 

109from IPython import get_ipython 

110from IPython.core import debugger 

111from IPython.core.display_trap import DisplayTrap 

112from IPython.core.excolors import exception_colors 

113from IPython.utils import PyColorize 

114from IPython.utils import path as util_path 

115from IPython.utils import py3compat 

116from IPython.utils.terminal import get_terminal_size 

117 

118# Globals 

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

120INDENT_SIZE = 8 

121 

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

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

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

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

126DEFAULT_SCHEME = 'NoColor' 

127FAST_THRESHOLD = 10_000 

128 

129# --------------------------------------------------------------------------- 

130# Code begins 

131 

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

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

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

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

136 

137 

138@functools.lru_cache() 

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

140 """ 

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

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

143 """ 

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

145 return 0 

146 else: 

147 try: 

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

149 s = sum(1 for line in file) 

150 except UnicodeError: 

151 return 0 

152 return s 

153 

154 """ 

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

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

157 """ 

158 

159 

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

161 """ 

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

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

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

165 

166 Parameters 

167 ---------- 

168 frame : FrameType 

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

170 

171 Returns 

172 ------- 

173 int 

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

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

176 if the file is not available. 

177 """ 

178 filename = frame.f_code.co_filename 

179 if filename is None: 

180 print("No file....") 

181 lines, first = inspect.getsourcelines(frame) 

182 return first + len(lines) 

183 return count_lines_in_py_file(filename) 

184 

185 

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

187 """ 

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

189 

190 Parameters 

191 ---------- 

192 lines : list[Line] 

193 Colors 

194 ColorScheme used. 

195 lvals : str 

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

197 """ 

198 numbers_width = INDENT_SIZE - 1 

199 res = [] 

200 

201 for stack_line in lines: 

202 if stack_line is stack_data.LINE_GAP: 

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

204 continue 

205 

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

207 lineno = stack_line.lineno 

208 if stack_line.is_current: 

209 # This is the line with the error 

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

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

212 start_color = Colors.linenoEm 

213 else: 

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

215 start_color = Colors.lineno 

216 

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

218 

219 res.append(line) 

220 if lvals and stack_line.is_current: 

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

222 return res 

223 

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

225 """ 

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

227 

228 Parameters 

229 ========== 

230 

231 lnum: int 

232 number of the target line of code. 

233 index: int 

234 which line in the list should be highlighted. 

235 lines: list[string] 

236 Colors: 

237 ColorScheme used. 

238 lvals: bytes 

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

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

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

242 """ 

243 numbers_width = INDENT_SIZE - 1 

244 res = [] 

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

246 # assert isinstance(line, str) 

247 line = py3compat.cast_unicode(line) 

248 

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

250 if not err: 

251 line = new_line 

252 

253 if i == lnum: 

254 # This is the line with the error 

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

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

257 line = "%s%s%s %s%s" % ( 

258 Colors.linenoEm, 

259 num, 

260 Colors.line, 

261 line, 

262 Colors.Normal, 

263 ) 

264 else: 

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

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

267 

268 res.append(line) 

269 if lvals and i == lnum: 

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

271 return res 

272 

273 

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

275 """ 

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

277 

278 Parameters 

279 ---------- 

280 file : str 

281 ColorFilename 

282 ColorScheme's filename coloring to be used. 

283 ColorNormal 

284 ColorScheme's normal coloring to be used. 

285 """ 

286 ipinst = get_ipython() 

287 if ( 

288 ipinst is not None 

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

290 ): 

291 label, name = data 

292 if lineno is None: 

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

294 else: 

295 tpl_link = ( 

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

297 ) 

298 else: 

299 label = "File" 

300 name = util_path.compress_user( 

301 py3compat.cast_unicode(file, util_path.fs_encoding) 

302 ) 

303 if lineno is None: 

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

305 else: 

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

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

308 

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

310 

311#--------------------------------------------------------------------------- 

312# Module classes 

313class TBTools(colorable.Colorable): 

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

315 

316 # Number of frames to skip when reporting tracebacks 

317 tb_offset = 0 

318 

319 def __init__( 

320 self, 

321 color_scheme="NoColor", 

322 call_pdb=False, 

323 ostream=None, 

324 parent=None, 

325 config=None, 

326 *, 

327 debugger_cls=None, 

328 ): 

329 # Whether to call the interactive pdb debugger after printing 

330 # tracebacks or not 

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

332 self.call_pdb = call_pdb 

333 

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

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

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

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

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

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

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

341 self._ostream = ostream 

342 

343 # Create color table 

344 self.color_scheme_table = exception_colors() 

345 

346 self.set_colors(color_scheme) 

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

348 self.debugger_cls = debugger_cls or debugger.Pdb 

349 

350 if call_pdb: 

351 self.pdb = self.debugger_cls() 

352 else: 

353 self.pdb = None 

354 

355 def _get_ostream(self): 

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

357 

358 Valid values are: 

359 

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

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

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

363 

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

365 """ 

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

367 

368 def _set_ostream(self, val): 

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

370 self._ostream = val 

371 

372 ostream = property(_get_ostream, _set_ostream) 

373 

374 @staticmethod 

375 def _get_chained_exception(exception_value): 

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

377 if cause: 

378 return cause 

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

380 return None 

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

382 

383 def get_parts_of_chained_exception( 

384 self, evalue 

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

386 chained_evalue = self._get_chained_exception(evalue) 

387 

388 if chained_evalue: 

389 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__ 

390 return None 

391 

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

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

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

395 

396 if cause: 

397 message = [[direct_cause]] 

398 else: 

399 message = [[exception_during_handling]] 

400 return message 

401 

402 @property 

403 def has_colors(self) -> bool: 

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

405 

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

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

408 

409 # Set own color table 

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

411 # for convenience, set Colors to the active scheme 

412 self.Colors = self.color_scheme_table.active_colors 

413 # Also set colors of debugger 

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

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

416 

417 def color_toggle(self): 

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

419 

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

421 self.color_scheme_table.set_active_scheme(self.old_scheme) 

422 self.Colors = self.color_scheme_table.active_colors 

423 else: 

424 self.old_scheme = self.color_scheme_table.active_scheme_name 

425 self.color_scheme_table.set_active_scheme('NoColor') 

426 self.Colors = self.color_scheme_table.active_colors 

427 

428 def stb2text(self, stb): 

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

430 return '\n'.join(stb) 

431 

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

433 """Return formatted traceback. 

434 

435 Subclasses may override this if they add extra arguments. 

436 """ 

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

438 tb_offset, context) 

439 return self.stb2text(tb_list) 

440 

441 def structured_traceback( 

442 self, 

443 etype: type, 

444 evalue: Optional[BaseException], 

445 etb: Optional[TracebackType] = None, 

446 tb_offset: Optional[int] = None, 

447 number_of_lines_of_context: int = 5, 

448 ): 

449 """Return a list of traceback frames. 

450 

451 Must be implemented by each class. 

452 """ 

453 raise NotImplementedError() 

454 

455 

456#--------------------------------------------------------------------------- 

457class ListTB(TBTools): 

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

459 

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

461 as would be obtained by:: 

462 

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

464 if tb: 

465 elist = traceback.extract_tb(tb) 

466 else: 

467 elist = None 

468 

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

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

471 standard library). 

472 

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

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

475 

476 

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

478 self.ostream.flush() 

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

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

481 

482 def _extract_tb(self, tb): 

483 if tb: 

484 return traceback.extract_tb(tb) 

485 else: 

486 return None 

487 

488 def structured_traceback( 

489 self, 

490 etype: type, 

491 evalue: Optional[BaseException], 

492 etb: Optional[TracebackType] = None, 

493 tb_offset: Optional[int] = None, 

494 context=5, 

495 ): 

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

497 

498 Parameters 

499 ---------- 

500 etype : exception type 

501 Type of the exception raised. 

502 evalue : object 

503 Data stored in the exception 

504 etb : list | TracebackType | None 

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

506 If Traceback: Traceback of the exception. 

507 tb_offset : int, optional 

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

509 instance evalue is used (set in constructor). 

510 context : int, optional 

511 Number of lines of context information to print. 

512 

513 Returns 

514 ------- 

515 String with formatted exception. 

516 """ 

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

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

519 if isinstance(etb, tuple): 

520 etb, chained_exc_ids = etb 

521 else: 

522 chained_exc_ids = set() 

523 

524 if isinstance(etb, list): 

525 elist = etb 

526 elif etb is not None: 

527 elist = self._extract_tb(etb) 

528 else: 

529 elist = [] 

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

531 assert isinstance(tb_offset, int) 

532 Colors = self.Colors 

533 out_list = [] 

534 if elist: 

535 

536 if tb_offset and len(elist) > tb_offset: 

537 elist = elist[tb_offset:] 

538 

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

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

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

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

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

544 out_list.append(lines) 

545 

546 exception = self.get_parts_of_chained_exception(evalue) 

547 

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

549 chained_exception_message = ( 

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

551 if evalue is not None 

552 else "" 

553 ) 

554 etype, evalue, etb = exception 

555 # Trace exception to avoid infinite 'cause' loop 

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

557 chained_exceptions_tb_offset = 0 

558 out_list = ( 

559 self.structured_traceback( 

560 etype, 

561 evalue, 

562 (etb, chained_exc_ids), # type: ignore 

563 chained_exceptions_tb_offset, 

564 context, 

565 ) 

566 + chained_exception_message 

567 + out_list) 

568 

569 return out_list 

570 

571 def _format_list(self, extracted_list): 

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

573 

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

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

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

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

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

579 whose source text line is not None. 

580 

581 Lifted almost verbatim from traceback.py 

582 """ 

583 

584 Colors = self.Colors 

585 list = [] 

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

587 normalCol, nameCol, fileCol, lineCol = ( 

588 # Emphasize the last entry 

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

590 if ind == len(extracted_list) - 1 

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

592 ) 

593 

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

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

596 

597 if name != "<module>": 

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

599 else: 

600 item += "\n" 

601 if line: 

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

603 list.append(item) 

604 

605 return list 

606 

607 def _format_exception_only(self, etype, value): 

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

609 

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

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

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

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

614 printed) display detailed information about where the syntax error 

615 occurred. The message indicating which exception occurred is the 

616 always last string in the list. 

617 

618 Also lifted nearly verbatim from traceback.py 

619 """ 

620 have_filedata = False 

621 Colors = self.Colors 

622 list = [] 

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

624 if value is None: 

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

626 list.append(stype + '\n') 

627 else: 

628 if issubclass(etype, SyntaxError): 

629 have_filedata = True 

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

631 if value.lineno: 

632 lineno = value.lineno 

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

634 else: 

635 lineno = "unknown" 

636 textline = "" 

637 list.append( 

638 "%s %s%s\n" 

639 % ( 

640 Colors.normalEm, 

641 _format_filename( 

642 value.filename, 

643 Colors.filenameEm, 

644 Colors.normalEm, 

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

646 ), 

647 Colors.Normal, 

648 ) 

649 ) 

650 if textline == "": 

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

652 

653 if textline is not None: 

654 i = 0 

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

656 i += 1 

657 list.append('%s %s%s\n' % (Colors.line, 

658 textline.strip(), 

659 Colors.Normal)) 

660 if value.offset is not None: 

661 s = ' ' 

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

663 if c.isspace(): 

664 s += c 

665 else: 

666 s += ' ' 

667 list.append('%s%s^%s\n' % (Colors.caret, s, 

668 Colors.Normal)) 

669 

670 try: 

671 s = value.msg 

672 except Exception: 

673 s = self._some_str(value) 

674 if s: 

675 list.append('%s%s:%s %s\n' % (stype, Colors.excName, 

676 Colors.Normal, s)) 

677 else: 

678 list.append('%s\n' % stype) 

679 

680 # sync with user hooks 

681 if have_filedata: 

682 ipinst = get_ipython() 

683 if ipinst is not None: 

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

685 

686 return list 

687 

688 def get_exception_only(self, etype, value): 

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

690 

691 Parameters 

692 ---------- 

693 etype : exception type 

694 value : exception value 

695 """ 

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

697 

698 def show_exception_only(self, etype, evalue): 

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

700 

701 Parameters 

702 ---------- 

703 etype : exception type 

704 evalue : exception value 

705 """ 

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

707 # a subclass whose signature or behavior may be different 

708 ostream = self.ostream 

709 ostream.flush() 

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

711 ostream.flush() 

712 

713 def _some_str(self, value): 

714 # Lifted from traceback.py 

715 try: 

716 return py3compat.cast_unicode(str(value)) 

717 except: 

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

719 

720 

721class FrameInfo: 

722 """ 

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

724 really long frames. 

725 """ 

726 

727 description: Optional[str] 

728 filename: Optional[str] 

729 lineno: Tuple[int] 

730 # number of context lines to use 

731 context: Optional[int] 

732 

733 @classmethod 

734 def _from_stack_data_FrameInfo(cls, frame_info): 

735 return cls( 

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

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

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

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

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

741 sd=frame_info, 

742 context=None, 

743 ) 

744 

745 def __init__( 

746 self, 

747 description: Optional[str], 

748 filename: str, 

749 lineno: Tuple[int], 

750 frame, 

751 code, 

752 *, 

753 sd=None, 

754 context=None, 

755 ): 

756 self.description = description 

757 self.filename = filename 

758 self.lineno = lineno 

759 self.frame = frame 

760 self.code = code 

761 self._sd = sd 

762 self.context = context 

763 

764 # self.lines = [] 

765 if sd is None: 

766 ix = inspect.getsourcelines(frame) 

767 self.raw_lines = ix[0] 

768 

769 @property 

770 def variables_in_executing_piece(self): 

771 if self._sd: 

772 return self._sd.variables_in_executing_piece 

773 else: 

774 return [] 

775 

776 @property 

777 def lines(self): 

778 return self._sd.lines 

779 

780 @property 

781 def executing(self): 

782 if self._sd: 

783 return self._sd.executing 

784 else: 

785 return None 

786 

787 

788# ---------------------------------------------------------------------------- 

789class VerboseTB(TBTools): 

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

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

792 

793 Modified version which optionally strips the topmost entries from the 

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

795 would appear in the traceback).""" 

796 

797 _tb_highlight = "bg:ansiyellow" 

798 

799 def __init__( 

800 self, 

801 color_scheme: str = "Linux", 

802 call_pdb: bool = False, 

803 ostream=None, 

804 tb_offset: int = 0, 

805 long_header: bool = False, 

806 include_vars: bool = True, 

807 check_cache=None, 

808 debugger_cls=None, 

809 parent=None, 

810 config=None, 

811 ): 

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

813 

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

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

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

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

818 TBTools.__init__( 

819 self, 

820 color_scheme=color_scheme, 

821 call_pdb=call_pdb, 

822 ostream=ostream, 

823 parent=parent, 

824 config=config, 

825 debugger_cls=debugger_cls, 

826 ) 

827 self.tb_offset = tb_offset 

828 self.long_header = long_header 

829 self.include_vars = include_vars 

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

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

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

833 if check_cache is None: 

834 check_cache = linecache.checkcache 

835 self.check_cache = check_cache 

836 

837 self.skip_hidden = True 

838 

839 def format_record(self, frame_info: FrameInfo): 

840 """Format a single stack frame""" 

841 assert isinstance(frame_info, FrameInfo) 

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

843 ColorsNormal = Colors.Normal # used a lot 

844 

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

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

847 Colors.excName, frame_info.description, ColorsNormal) 

848 

849 indent = " " * INDENT_SIZE 

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

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

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

853 Colors.vName, 

854 Colors.valEm, 

855 ColorsNormal, 

856 ) 

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

858 

859 link = _format_filename( 

860 frame_info.filename, 

861 Colors.filenameEm, 

862 ColorsNormal, 

863 lineno=frame_info.lineno, 

864 ) 

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

866 if frame_info.executing is not None: 

867 func = frame_info.executing.code_qualname() 

868 else: 

869 func = "?" 

870 if func == "<module>": 

871 call = "" 

872 else: 

873 # Decide whether to include variable details or not 

874 var_repr = eqrepr if self.include_vars else nullrepr 

875 try: 

876 scope = inspect.formatargvalues( 

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

878 ) 

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

880 except KeyError: 

881 # This happens in situations like errors inside generator 

882 # expressions, where local variables are listed in the 

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

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

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

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

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

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

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

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

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

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

893 # test, but running a script consisting of: 

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

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

896 # disabled. 

897 call = tpl_call_fail % func 

898 

899 lvals = '' 

900 lvals_list = [] 

901 if self.include_vars: 

902 try: 

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

904 # still need a workaround. 

905 fibp = frame_info.variables_in_executing_piece 

906 for var in fibp: 

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

908 except Exception: 

909 lvals_list.append( 

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

911 ) 

912 if lvals_list: 

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

914 

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

916 if frame_info._sd is None: 

917 # fast fallback if file is too long 

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

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

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

921 _line_format = PyColorize.Parser( 

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

923 ).format2 

924 first_line = frame_info.code.co_firstlineno 

925 current_line = frame_info.lineno[0] 

926 raw_lines = frame_info.raw_lines 

927 index = current_line - first_line 

928 

929 if index >= frame_info.context: 

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

931 stop = index + frame_info.context 

932 index = frame_info.context 

933 else: 

934 start = 0 

935 stop = index + frame_info.context 

936 raw_lines = raw_lines[start:stop] 

937 

938 return "%s%s" % ( 

939 level, 

940 "".join( 

941 _simple_format_traceback_lines( 

942 current_line, 

943 index, 

944 raw_lines, 

945 Colors, 

946 lvals, 

947 _line_format, 

948 ) 

949 ), 

950 ) 

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

952 else: 

953 result += "".join( 

954 _format_traceback_lines( 

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

956 ) 

957 ) 

958 return result 

959 

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

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

962 colorsnormal = colors.Normal # used a lot 

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

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

965 if long_version: 

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

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

968 date = time.ctime(time.time()) 

969 

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

971 colors.topline, 

972 "-" * width, 

973 colorsnormal, 

974 exc, 

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

976 pyver, 

977 date.rjust(width), 

978 ) 

979 head += ( 

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

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

982 ) 

983 else: 

984 # Simplified header 

985 head = "%s%s" % ( 

986 exc, 

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

988 ) 

989 

990 return head 

991 

992 def format_exception(self, etype, evalue): 

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

994 colorsnormal = colors.Normal # used a lot 

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

996 try: 

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

998 except: 

999 # User exception is improperly defined. 

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

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

1002 # ... and format it 

1003 return ['%s%s%s: %s' % (colors.excName, etype_str, 

1004 colorsnormal, py3compat.cast_unicode(evalue_str))] 

1005 

1006 def format_exception_as_a_whole( 

1007 self, 

1008 etype: type, 

1009 evalue: Optional[BaseException], 

1010 etb: Optional[TracebackType], 

1011 number_of_lines_of_context, 

1012 tb_offset: Optional[int], 

1013 ): 

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

1015 

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

1017 (PEP 3134). 

1018 """ 

1019 # some locals 

1020 orig_etype = etype 

1021 try: 

1022 etype = etype.__name__ # type: ignore 

1023 except AttributeError: 

1024 pass 

1025 

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

1027 assert isinstance(tb_offset, int) 

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

1029 records = ( 

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

1031 ) 

1032 

1033 frames = [] 

1034 skipped = 0 

1035 lastrecord = len(records) - 1 

1036 for i, record in enumerate(records): 

1037 if ( 

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

1039 and self.skip_hidden 

1040 ): 

1041 if ( 

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

1043 and i != lastrecord 

1044 ): 

1045 skipped += 1 

1046 continue 

1047 if skipped: 

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

1049 ColorsNormal = Colors.Normal # used a lot 

1050 frames.append( 

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

1052 % (Colors.excName, skipped, ColorsNormal) 

1053 ) 

1054 skipped = 0 

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

1056 if skipped: 

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

1058 ColorsNormal = Colors.Normal # used a lot 

1059 frames.append( 

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

1061 % (Colors.excName, skipped, ColorsNormal) 

1062 ) 

1063 

1064 formatted_exception = self.format_exception(etype, evalue) 

1065 if records: 

1066 frame_info = records[-1] 

1067 ipinst = get_ipython() 

1068 if ipinst is not None: 

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

1070 

1071 return [[head] + frames + [''.join(formatted_exception[0])]] 

1072 

1073 def get_records( 

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

1075 ): 

1076 assert etb is not None 

1077 context = number_of_lines_of_context - 1 

1078 after = context // 2 

1079 before = context - after 

1080 if self.has_colors: 

1081 style = get_style_by_name("default") 

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

1083 formatter = Terminal256Formatter(style=style) 

1084 else: 

1085 formatter = None 

1086 options = stack_data.Options( 

1087 before=before, 

1088 after=after, 

1089 pygments_formatter=formatter, 

1090 ) 

1091 

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

1093 cf: Optional[TracebackType] = etb 

1094 max_len = 0 

1095 tbs = [] 

1096 while cf is not None: 

1097 try: 

1098 mod = inspect.getmodule(cf.tb_frame) 

1099 if mod is not None: 

1100 mod_name = mod.__name__ 

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

1102 if root_name == "IPython": 

1103 cf = cf.tb_next 

1104 continue 

1105 max_len = get_line_number_of_frame(cf.tb_frame) 

1106 

1107 except OSError: 

1108 max_len = 0 

1109 max_len = max(max_len, max_len) 

1110 tbs.append(cf) 

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

1112 

1113 if max_len > FAST_THRESHOLD: 

1114 FIs = [] 

1115 for tb in tbs: 

1116 frame = tb.tb_frame # type: ignore 

1117 lineno = (frame.f_lineno,) 

1118 code = frame.f_code 

1119 filename = code.co_filename 

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

1121 FIs.append( 

1122 FrameInfo( 

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

1124 ) 

1125 ) 

1126 return FIs 

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

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

1129 return res 

1130 

1131 def structured_traceback( 

1132 self, 

1133 etype: type, 

1134 evalue: Optional[BaseException], 

1135 etb: Optional[TracebackType] = None, 

1136 tb_offset: Optional[int] = None, 

1137 number_of_lines_of_context: int = 5, 

1138 ): 

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

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

1141 tb_offset) 

1142 

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

1144 colorsnormal = colors.Normal # used a lot 

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

1146 structured_traceback_parts = [head] 

1147 chained_exceptions_tb_offset = 0 

1148 lines_of_context = 3 

1149 formatted_exceptions = formatted_exception 

1150 exception = self.get_parts_of_chained_exception(evalue) 

1151 if exception: 

1152 assert evalue is not None 

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

1154 etype, evalue, etb = exception 

1155 else: 

1156 evalue = None 

1157 chained_exc_ids = set() 

1158 while evalue: 

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

1160 chained_exceptions_tb_offset) 

1161 exception = self.get_parts_of_chained_exception(evalue) 

1162 

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

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

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

1166 etype, evalue, etb = exception 

1167 else: 

1168 evalue = None 

1169 

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

1171 # the first exception should be on top 

1172 for formatted_exception in reversed(formatted_exceptions): 

1173 structured_traceback_parts += formatted_exception 

1174 

1175 return structured_traceback_parts 

1176 

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

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

1179 reference. 

1180 

1181 Keywords: 

1182 

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

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

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

1186 is false. 

1187 

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

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

1190 is deleted to prevent lingering references which hamper memory 

1191 management. 

1192 

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

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

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

1196 

1197 if force or self.call_pdb: 

1198 if self.pdb is None: 

1199 self.pdb = self.debugger_cls() 

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

1201 # for pdb 

1202 display_trap = DisplayTrap(hook=sys.__displayhook__) 

1203 with display_trap: 

1204 self.pdb.reset() 

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

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

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

1208 else: 

1209 etb = self.tb = sys.last_traceback 

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

1211 assert self.tb.tb_next is not None 

1212 self.tb = self.tb.tb_next 

1213 if etb and etb.tb_next: 

1214 etb = etb.tb_next 

1215 self.pdb.botframe = etb.tb_frame 

1216 self.pdb.interaction(None, etb) 

1217 

1218 if hasattr(self, 'tb'): 

1219 del self.tb 

1220 

1221 def handler(self, info=None): 

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

1223 self.tb = etb 

1224 ostream = self.ostream 

1225 ostream.flush() 

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

1227 ostream.write('\n') 

1228 ostream.flush() 

1229 

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

1231 # out the right info on its own. 

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

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

1234 if etb is None: 

1235 self.handler() 

1236 else: 

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

1238 try: 

1239 self.debugger() 

1240 except KeyboardInterrupt: 

1241 print("\nKeyboardInterrupt") 

1242 

1243 

1244#---------------------------------------------------------------------------- 

1245class FormattedTB(VerboseTB, ListTB): 

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

1247 

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

1249 

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

1251 

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

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

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

1255 like Python shells). """ 

1256 

1257 mode: str 

1258 

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

1260 ostream=None, 

1261 tb_offset=0, long_header=False, include_vars=False, 

1262 check_cache=None, debugger_cls=None, 

1263 parent=None, config=None): 

1264 

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

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

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

1268 

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

1270 ostream=ostream, tb_offset=tb_offset, 

1271 long_header=long_header, include_vars=include_vars, 

1272 check_cache=check_cache, debugger_cls=debugger_cls, 

1273 parent=parent, config=config) 

1274 

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

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

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

1278 Minimal='') 

1279 # set_mode also sets the tb_join_char attribute 

1280 self.set_mode(mode) 

1281 

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

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

1284 mode = self.mode 

1285 if mode in self.verbose_modes: 

1286 # Verbose modes need a full traceback 

1287 return VerboseTB.structured_traceback( 

1288 self, etype, value, tb, tb_offset, number_of_lines_of_context 

1289 ) 

1290 elif mode == 'Minimal': 

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

1292 else: 

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

1294 # out-of-date source code. 

1295 self.check_cache() 

1296 # Now we can extract and format the exception 

1297 return ListTB.structured_traceback( 

1298 self, etype, value, tb, tb_offset, number_of_lines_of_context 

1299 ) 

1300 

1301 def stb2text(self, stb): 

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

1303 return self.tb_join_char.join(stb) 

1304 

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

1306 """Switch to the desired mode. 

1307 

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

1309 

1310 if not mode: 

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

1312 len(self.valid_modes) 

1313 self.mode = self.valid_modes[new_idx] 

1314 elif mode not in self.valid_modes: 

1315 raise ValueError( 

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

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

1318 ) 

1319 else: 

1320 assert isinstance(mode, str) 

1321 self.mode = mode 

1322 # include variable details only in 'Verbose' mode 

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

1324 # Set the join character for generating text tracebacks 

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

1326 

1327 # some convenient shortcuts 

1328 def plain(self): 

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

1330 

1331 def context(self): 

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

1333 

1334 def verbose(self): 

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

1336 

1337 def minimal(self): 

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

1339 

1340 

1341#---------------------------------------------------------------------------- 

1342class AutoFormattedTB(FormattedTB): 

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

1344 

1345 It will find out about exceptions by itself. 

1346 

1347 A brief example:: 

1348 

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

1350 try: 

1351 ... 

1352 except: 

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

1354 """ 

1355 

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

1357 out=None, tb_offset=None): 

1358 """Print out a formatted exception traceback. 

1359 

1360 Optional arguments: 

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

1362 

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

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

1365 given at initialization time.""" 

1366 

1367 if out is None: 

1368 out = self.ostream 

1369 out.flush() 

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

1371 out.write('\n') 

1372 out.flush() 

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

1374 # that to the clients. 

1375 try: 

1376 self.debugger() 

1377 except KeyboardInterrupt: 

1378 print("\nKeyboardInterrupt") 

1379 

1380 def structured_traceback( 

1381 self, 

1382 etype: type, 

1383 evalue: Optional[BaseException], 

1384 etb: Optional[TracebackType] = None, 

1385 tb_offset: Optional[int] = None, 

1386 number_of_lines_of_context: int = 5, 

1387 ): 

1388 # tb: TracebackType or tupleof tb types ? 

1389 if etype is None: 

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

1391 if isinstance(etb, tuple): 

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

1393 self.tb = etb[0] 

1394 else: 

1395 self.tb = etb 

1396 return FormattedTB.structured_traceback( 

1397 self, etype, evalue, etb, tb_offset, number_of_lines_of_context 

1398 ) 

1399 

1400 

1401#--------------------------------------------------------------------------- 

1402 

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

1404class ColorTB(FormattedTB): 

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

1406 

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

1408 FormattedTB.__init__(self, color_scheme=color_scheme, 

1409 call_pdb=call_pdb, **kwargs) 

1410 

1411 

1412class SyntaxTB(ListTB): 

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

1414 

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

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

1417 self.last_syntax_error = None 

1418 

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

1420 self.last_syntax_error = value 

1421 

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

1423 

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

1425 context=5): 

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

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

1428 # the current value. 

1429 if isinstance(value, SyntaxError) \ 

1430 and isinstance(value.filename, str) \ 

1431 and isinstance(value.lineno, int): 

1432 linecache.checkcache(value.filename) 

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

1434 if newtext: 

1435 value.text = newtext 

1436 self.last_syntax_error = value 

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

1438 tb_offset=tb_offset, context=context) 

1439 

1440 def clear_err_state(self): 

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

1442 e = self.last_syntax_error 

1443 self.last_syntax_error = None 

1444 return e 

1445 

1446 def stb2text(self, stb): 

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

1448 return ''.join(stb) 

1449 

1450 

1451# some internal-use functions 

1452def text_repr(value): 

1453 """Hopefully pretty robust repr equivalent.""" 

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

1455 try: 

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

1457 except KeyboardInterrupt: 

1458 raise 

1459 except: 

1460 try: 

1461 return repr(value) 

1462 except KeyboardInterrupt: 

1463 raise 

1464 except: 

1465 try: 

1466 # all still in an except block so we catch 

1467 # getattr raising 

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

1469 if name: 

1470 # ick, recursion 

1471 return text_repr(name) 

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

1473 if klass: 

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

1475 except KeyboardInterrupt: 

1476 raise 

1477 except: 

1478 return 'UNRECOVERABLE REPR FAILURE' 

1479 

1480 

1481def eqrepr(value, repr=text_repr): 

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

1483 

1484 

1485def nullrepr(value, repr=text_repr): 

1486 return ''