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

586 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 06:07 +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 inspect 

93import linecache 

94import pydoc 

95import sys 

96import time 

97import traceback 

98from types import TracebackType 

99from typing import Tuple, List, Any, Optional 

100 

101import stack_data 

102from stack_data import FrameInfo as SDFrameInfo 

103from pygments.formatters.terminal256 import Terminal256Formatter 

104from pygments.styles import get_style_by_name 

105 

106# IPython's own modules 

107from IPython import get_ipython 

108from IPython.core import debugger 

109from IPython.core.display_trap import DisplayTrap 

110from IPython.core.excolors import exception_colors 

111from IPython.utils import PyColorize 

112from IPython.utils import path as util_path 

113from IPython.utils import py3compat 

114from IPython.utils.terminal import get_terminal_size 

115 

116import IPython.utils.colorable as colorable 

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 

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

139 """ 

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

141 

142 Parameters 

143 ---------- 

144 lines : list[Line] 

145 Colors 

146 ColorScheme used. 

147 lvals : str 

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

149 """ 

150 numbers_width = INDENT_SIZE - 1 

151 res = [] 

152 

153 for stack_line in lines: 

154 if stack_line is stack_data.LINE_GAP: 

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

156 continue 

157 

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

159 lineno = stack_line.lineno 

160 if stack_line.is_current: 

161 # This is the line with the error 

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

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

164 start_color = Colors.linenoEm 

165 else: 

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

167 start_color = Colors.lineno 

168 

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

170 

171 res.append(line) 

172 if lvals and stack_line.is_current: 

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

174 return res 

175 

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

177 """ 

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

179 

180 Parameters 

181 ========== 

182 

183 lnum: int 

184 number of the target line of code. 

185 index: int 

186 which line in the list should be highlighted. 

187 lines: list[string] 

188 Colors: 

189 ColorScheme used. 

190 lvals: bytes 

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

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

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

194 """ 

195 numbers_width = INDENT_SIZE - 1 

196 res = [] 

197 

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

199 line = py3compat.cast_unicode(line) 

200 

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

202 if not err: 

203 line = new_line 

204 

205 if i == lnum: 

206 # This is the line with the error 

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

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

209 line = "%s%s%s %s%s" % ( 

210 Colors.linenoEm, 

211 num, 

212 Colors.line, 

213 line, 

214 Colors.Normal, 

215 ) 

216 else: 

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

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

219 

220 res.append(line) 

221 if lvals and i == lnum: 

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

223 return res 

224 

225 

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

227 """ 

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

229 

230 Parameters 

231 ---------- 

232 file : str 

233 ColorFilename 

234 ColorScheme's filename coloring to be used. 

235 ColorNormal 

236 ColorScheme's normal coloring to be used. 

237 """ 

238 ipinst = get_ipython() 

239 if ( 

240 ipinst is not None 

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

242 ): 

243 label, name = data 

244 if lineno is None: 

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

246 else: 

247 tpl_link = ( 

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

249 ) 

250 else: 

251 label = "File" 

252 name = util_path.compress_user( 

253 py3compat.cast_unicode(file, util_path.fs_encoding) 

254 ) 

255 if lineno is None: 

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

257 else: 

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

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

260 

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

262 

263#--------------------------------------------------------------------------- 

264# Module classes 

265class TBTools(colorable.Colorable): 

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

267 

268 # Number of frames to skip when reporting tracebacks 

269 tb_offset = 0 

270 

271 def __init__( 

272 self, 

273 color_scheme="NoColor", 

274 call_pdb=False, 

275 ostream=None, 

276 parent=None, 

277 config=None, 

278 *, 

279 debugger_cls=None, 

280 ): 

281 # Whether to call the interactive pdb debugger after printing 

282 # tracebacks or not 

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

284 self.call_pdb = call_pdb 

285 

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

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

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

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

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

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

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

293 self._ostream = ostream 

294 

295 # Create color table 

296 self.color_scheme_table = exception_colors() 

297 

298 self.set_colors(color_scheme) 

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

300 self.debugger_cls = debugger_cls or debugger.Pdb 

301 

302 if call_pdb: 

303 self.pdb = self.debugger_cls() 

304 else: 

305 self.pdb = None 

306 

307 def _get_ostream(self): 

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

309 

310 Valid values are: 

311 

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

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

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

315 

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

317 """ 

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

319 

320 def _set_ostream(self, val): 

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

322 self._ostream = val 

323 

324 ostream = property(_get_ostream, _set_ostream) 

325 

326 @staticmethod 

327 def _get_chained_exception(exception_value): 

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

329 if cause: 

330 return cause 

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

332 return None 

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

334 

335 def get_parts_of_chained_exception( 

336 self, evalue 

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

338 chained_evalue = self._get_chained_exception(evalue) 

339 

340 if chained_evalue: 

341 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__ 

342 return None 

343 

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

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

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

347 

348 if cause: 

349 message = [[direct_cause]] 

350 else: 

351 message = [[exception_during_handling]] 

352 return message 

353 

354 @property 

355 def has_colors(self) -> bool: 

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

357 

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

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

360 

361 # Set own color table 

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

363 # for convenience, set Colors to the active scheme 

364 self.Colors = self.color_scheme_table.active_colors 

365 # Also set colors of debugger 

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

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

368 

369 def color_toggle(self): 

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

371 

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

373 self.color_scheme_table.set_active_scheme(self.old_scheme) 

374 self.Colors = self.color_scheme_table.active_colors 

375 else: 

376 self.old_scheme = self.color_scheme_table.active_scheme_name 

377 self.color_scheme_table.set_active_scheme('NoColor') 

378 self.Colors = self.color_scheme_table.active_colors 

379 

380 def stb2text(self, stb): 

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

382 return '\n'.join(stb) 

383 

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

385 """Return formatted traceback. 

386 

387 Subclasses may override this if they add extra arguments. 

388 """ 

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

390 tb_offset, context) 

391 return self.stb2text(tb_list) 

392 

393 def structured_traceback( 

394 self, etype, evalue, tb, tb_offset: Optional[int] = None, context=5, mode=None 

395 ): 

396 """Return a list of traceback frames. 

397 

398 Must be implemented by each class. 

399 """ 

400 raise NotImplementedError() 

401 

402 

403#--------------------------------------------------------------------------- 

404class ListTB(TBTools): 

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

406 

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

408 as would be obtained by:: 

409 

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

411 if tb: 

412 elist = traceback.extract_tb(tb) 

413 else: 

414 elist = None 

415 

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

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

418 standard library). 

419 

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

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

422 

423 

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

425 self.ostream.flush() 

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

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

428 

429 def _extract_tb(self, tb): 

430 if tb: 

431 return traceback.extract_tb(tb) 

432 else: 

433 return None 

434 

435 def structured_traceback( 

436 self, 

437 etype: type, 

438 evalue: BaseException, 

439 etb: Optional[TracebackType] = None, 

440 tb_offset: Optional[int] = None, 

441 context=5, 

442 ): 

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

444 

445 Parameters 

446 ---------- 

447 etype : exception type 

448 Type of the exception raised. 

449 evalue : object 

450 Data stored in the exception 

451 etb : list | TracebackType | None 

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

453 If Traceback: Traceback of the exception. 

454 tb_offset : int, optional 

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

456 instance evalue is used (set in constructor). 

457 context : int, optional 

458 Number of lines of context information to print. 

459 

460 Returns 

461 ------- 

462 String with formatted exception. 

463 """ 

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

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

466 if isinstance(etb, tuple): 

467 etb, chained_exc_ids = etb 

468 else: 

469 chained_exc_ids = set() 

470 

471 if isinstance(etb, list): 

472 elist = etb 

473 elif etb is not None: 

474 elist = self._extract_tb(etb) 

475 else: 

476 elist = [] 

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

478 assert isinstance(tb_offset, int) 

479 Colors = self.Colors 

480 out_list = [] 

481 if elist: 

482 

483 if tb_offset and len(elist) > tb_offset: 

484 elist = elist[tb_offset:] 

485 

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

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

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

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

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

491 out_list.append(lines) 

492 

493 exception = self.get_parts_of_chained_exception(evalue) 

494 

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

496 chained_exception_message = self.prepare_chained_exception_message( 

497 evalue.__cause__)[0] 

498 etype, evalue, etb = exception 

499 # Trace exception to avoid infinite 'cause' loop 

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

501 chained_exceptions_tb_offset = 0 

502 out_list = ( 

503 self.structured_traceback( 

504 etype, evalue, (etb, chained_exc_ids), 

505 chained_exceptions_tb_offset, context) 

506 + chained_exception_message 

507 + out_list) 

508 

509 return out_list 

510 

511 def _format_list(self, extracted_list): 

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

513 

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

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

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

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

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

519 whose source text line is not None. 

520 

521 Lifted almost verbatim from traceback.py 

522 """ 

523 

524 Colors = self.Colors 

525 list = [] 

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

527 normalCol, nameCol, fileCol, lineCol = ( 

528 # Emphasize the last entry 

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

530 if ind == len(extracted_list) - 1 

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

532 ) 

533 

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

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

536 

537 if name != "<module>": 

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

539 else: 

540 item += "\n" 

541 if line: 

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

543 list.append(item) 

544 

545 return list 

546 

547 def _format_exception_only(self, etype, value): 

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

549 

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

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

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

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

554 printed) display detailed information about where the syntax error 

555 occurred. The message indicating which exception occurred is the 

556 always last string in the list. 

557 

558 Also lifted nearly verbatim from traceback.py 

559 """ 

560 have_filedata = False 

561 Colors = self.Colors 

562 list = [] 

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

564 if value is None: 

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

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

567 else: 

568 if issubclass(etype, SyntaxError): 

569 have_filedata = True 

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

571 if value.lineno: 

572 lineno = value.lineno 

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

574 else: 

575 lineno = "unknown" 

576 textline = "" 

577 list.append( 

578 "%s %s%s\n" 

579 % ( 

580 Colors.normalEm, 

581 _format_filename( 

582 value.filename, 

583 Colors.filenameEm, 

584 Colors.normalEm, 

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

586 ), 

587 Colors.Normal, 

588 ) 

589 ) 

590 if textline == "": 

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

592 

593 if textline is not None: 

594 i = 0 

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

596 i += 1 

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

598 textline.strip(), 

599 Colors.Normal)) 

600 if value.offset is not None: 

601 s = ' ' 

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

603 if c.isspace(): 

604 s += c 

605 else: 

606 s += ' ' 

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

608 Colors.Normal)) 

609 

610 try: 

611 s = value.msg 

612 except Exception: 

613 s = self._some_str(value) 

614 if s: 

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

616 Colors.Normal, s)) 

617 else: 

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

619 

620 # sync with user hooks 

621 if have_filedata: 

622 ipinst = get_ipython() 

623 if ipinst is not None: 

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

625 

626 return list 

627 

628 def get_exception_only(self, etype, value): 

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

630 

631 Parameters 

632 ---------- 

633 etype : exception type 

634 value : exception value 

635 """ 

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

637 

638 def show_exception_only(self, etype, evalue): 

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

640 

641 Parameters 

642 ---------- 

643 etype : exception type 

644 evalue : exception value 

645 """ 

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

647 # a subclass whose signature or behavior may be different 

648 ostream = self.ostream 

649 ostream.flush() 

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

651 ostream.flush() 

652 

653 def _some_str(self, value): 

654 # Lifted from traceback.py 

655 try: 

656 return py3compat.cast_unicode(str(value)) 

657 except: 

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

659 

660 

661class FrameInfo: 

662 """ 

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

664 really long frames. 

665 """ 

666 

667 description: Optional[str] 

668 filename: str 

669 lineno: int 

670 

671 @classmethod 

672 def _from_stack_data_FrameInfo(cls, frame_info): 

673 return cls( 

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

675 getattr(frame_info, "filename", None), 

676 getattr(frame_info, "lineno", None), 

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

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

679 sd=frame_info, 

680 ) 

681 

682 def __init__(self, description, filename, lineno, frame, code, sd=None): 

683 self.description = description 

684 self.filename = filename 

685 self.lineno = lineno 

686 self.frame = frame 

687 self.code = code 

688 self._sd = sd 

689 

690 # self.lines = [] 

691 if sd is None: 

692 ix = inspect.getsourcelines(frame) 

693 self.raw_lines = ix[0] 

694 

695 @property 

696 def variables_in_executing_piece(self): 

697 if self._sd: 

698 return self._sd.variables_in_executing_piece 

699 else: 

700 return [] 

701 

702 @property 

703 def lines(self): 

704 return self._sd.lines 

705 

706 @property 

707 def executing(self): 

708 if self._sd: 

709 return self._sd.executing 

710 else: 

711 return None 

712 

713 

714# ---------------------------------------------------------------------------- 

715class VerboseTB(TBTools): 

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

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

718 

719 Modified version which optionally strips the topmost entries from the 

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

721 would appear in the traceback).""" 

722 

723 _tb_highlight = "bg:ansiyellow" 

724 

725 def __init__( 

726 self, 

727 color_scheme: str = "Linux", 

728 call_pdb: bool = False, 

729 ostream=None, 

730 tb_offset: int = 0, 

731 long_header: bool = False, 

732 include_vars: bool = True, 

733 check_cache=None, 

734 debugger_cls=None, 

735 parent=None, 

736 config=None, 

737 ): 

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

739 

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

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

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

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

744 TBTools.__init__( 

745 self, 

746 color_scheme=color_scheme, 

747 call_pdb=call_pdb, 

748 ostream=ostream, 

749 parent=parent, 

750 config=config, 

751 debugger_cls=debugger_cls, 

752 ) 

753 self.tb_offset = tb_offset 

754 self.long_header = long_header 

755 self.include_vars = include_vars 

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

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

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

759 if check_cache is None: 

760 check_cache = linecache.checkcache 

761 self.check_cache = check_cache 

762 

763 self.skip_hidden = True 

764 

765 def format_record(self, frame_info: FrameInfo): 

766 """Format a single stack frame""" 

767 assert isinstance(frame_info, FrameInfo) 

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

769 ColorsNormal = Colors.Normal # used a lot 

770 

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

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

773 Colors.excName, frame_info.description, ColorsNormal) 

774 

775 indent = " " * INDENT_SIZE 

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

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

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

779 Colors.vName, 

780 Colors.valEm, 

781 ColorsNormal, 

782 ) 

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

784 

785 link = _format_filename( 

786 frame_info.filename, 

787 Colors.filenameEm, 

788 ColorsNormal, 

789 lineno=frame_info.lineno, 

790 ) 

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

792 if frame_info.executing is not None: 

793 func = frame_info.executing.code_qualname() 

794 else: 

795 func = "?" 

796 if func == "<module>": 

797 call = "" 

798 else: 

799 # Decide whether to include variable details or not 

800 var_repr = eqrepr if self.include_vars else nullrepr 

801 try: 

802 scope = inspect.formatargvalues( 

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

804 ) 

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

806 except KeyError: 

807 # This happens in situations like errors inside generator 

808 # expressions, where local variables are listed in the 

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

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

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

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

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

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

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

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

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

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

819 # test, but running a script consisting of: 

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

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

822 # disabled. 

823 call = tpl_call_fail % func 

824 

825 lvals = '' 

826 lvals_list = [] 

827 if self.include_vars: 

828 try: 

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

830 # still need a workaround. 

831 fibp = frame_info.variables_in_executing_piece 

832 for var in fibp: 

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

834 except Exception: 

835 lvals_list.append( 

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

837 ) 

838 if lvals_list: 

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

840 

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

842 if frame_info._sd is None: 

843 assert False 

844 # fast fallback if file is too long 

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

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

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

848 _line_format = PyColorize.Parser( 

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

850 ).format2 

851 first_line = frame_info.code.co_firstlineno 

852 current_line = frame_info.lineno[0] 

853 return "%s%s" % ( 

854 level, 

855 "".join( 

856 _simple_format_traceback_lines( 

857 current_line, 

858 current_line - first_line, 

859 frame_info.raw_lines, 

860 Colors, 

861 lvals, 

862 _line_format, 

863 ) 

864 ), 

865 ) 

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

867 else: 

868 result += "".join( 

869 _format_traceback_lines( 

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

871 ) 

872 ) 

873 return result 

874 

875 def prepare_header(self, etype, long_version=False): 

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

877 colorsnormal = colors.Normal # used a lot 

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

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

880 if long_version: 

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

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

883 date = time.ctime(time.time()) 

884 

885 head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * width, colorsnormal, 

886 exc, ' ' * (width - len(str(etype)) - len(pyver)), 

887 pyver, date.rjust(width) ) 

888 head += "\nA problem occurred executing Python code. Here is the sequence of function" \ 

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

890 else: 

891 # Simplified header 

892 head = '%s%s' % (exc, 'Traceback (most recent call last)'. \ 

893 rjust(width - len(str(etype))) ) 

894 

895 return head 

896 

897 def format_exception(self, etype, evalue): 

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

899 colorsnormal = colors.Normal # used a lot 

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

901 try: 

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

903 except: 

904 # User exception is improperly defined. 

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

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

907 # ... and format it 

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

909 colorsnormal, py3compat.cast_unicode(evalue_str))] 

910 

911 def format_exception_as_a_whole( 

912 self, 

913 etype: type, 

914 evalue: BaseException, 

915 etb: Optional[TracebackType], 

916 number_of_lines_of_context, 

917 tb_offset: Optional[int], 

918 ): 

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

920 

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

922 (PEP 3134). 

923 """ 

924 # some locals 

925 orig_etype = etype 

926 try: 

927 etype = etype.__name__ 

928 except AttributeError: 

929 pass 

930 

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

932 assert isinstance(tb_offset, int) 

933 head = self.prepare_header(etype, self.long_header) 

934 records = ( 

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

936 ) 

937 

938 frames = [] 

939 skipped = 0 

940 lastrecord = len(records) - 1 

941 for i, record in enumerate(records): 

942 if ( 

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

944 and self.skip_hidden 

945 ): 

946 if ( 

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

948 and i != lastrecord 

949 ): 

950 skipped += 1 

951 continue 

952 if skipped: 

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

954 ColorsNormal = Colors.Normal # used a lot 

955 frames.append( 

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

957 % (Colors.excName, skipped, ColorsNormal) 

958 ) 

959 skipped = 0 

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

961 if skipped: 

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

963 ColorsNormal = Colors.Normal # used a lot 

964 frames.append( 

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

966 % (Colors.excName, skipped, ColorsNormal) 

967 ) 

968 

969 formatted_exception = self.format_exception(etype, evalue) 

970 if records: 

971 frame_info = records[-1] 

972 ipinst = get_ipython() 

973 if ipinst is not None: 

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

975 

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

977 

978 def get_records( 

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

980 ): 

981 assert etb is not None 

982 context = number_of_lines_of_context - 1 

983 after = context // 2 

984 before = context - after 

985 if self.has_colors: 

986 style = get_style_by_name("default") 

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

988 formatter = Terminal256Formatter(style=style) 

989 else: 

990 formatter = None 

991 options = stack_data.Options( 

992 before=before, 

993 after=after, 

994 pygments_formatter=formatter, 

995 ) 

996 

997 # let's estimate the amount of code we eill have to parse/highlight. 

998 cf = etb 

999 max_len = 0 

1000 tbs = [] 

1001 while cf is not None: 

1002 source_file = inspect.getsourcefile(etb.tb_frame) 

1003 lines, first = inspect.getsourcelines(etb.tb_frame) 

1004 max_len = max(max_len, first + len(lines)) 

1005 tbs.append(cf) 

1006 cf = cf.tb_next 

1007 

1008 if max_len > FAST_THRESHOLD: 

1009 FIs = [] 

1010 for tb in tbs: 

1011 frame = tb.tb_frame 

1012 lineno = (frame.f_lineno,) 

1013 code = frame.f_code 

1014 filename = code.co_filename 

1015 FIs.append(FrameInfo("Raw frame", filename, lineno, frame, code)) 

1016 return FIs 

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

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

1019 return res 

1020 

1021 def structured_traceback( 

1022 self, 

1023 etype: type, 

1024 evalue: Optional[BaseException], 

1025 etb: Optional[TracebackType], 

1026 tb_offset: Optional[int] = None, 

1027 number_of_lines_of_context: int = 5, 

1028 ): 

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

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

1031 tb_offset) 

1032 

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

1034 colorsnormal = colors.Normal # used a lot 

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

1036 structured_traceback_parts = [head] 

1037 chained_exceptions_tb_offset = 0 

1038 lines_of_context = 3 

1039 formatted_exceptions = formatted_exception 

1040 exception = self.get_parts_of_chained_exception(evalue) 

1041 if exception: 

1042 assert evalue is not None 

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

1044 etype, evalue, etb = exception 

1045 else: 

1046 evalue = None 

1047 chained_exc_ids = set() 

1048 while evalue: 

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

1050 chained_exceptions_tb_offset) 

1051 exception = self.get_parts_of_chained_exception(evalue) 

1052 

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

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

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

1056 etype, evalue, etb = exception 

1057 else: 

1058 evalue = None 

1059 

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

1061 # the first exception should be on top 

1062 for formatted_exception in reversed(formatted_exceptions): 

1063 structured_traceback_parts += formatted_exception 

1064 

1065 return structured_traceback_parts 

1066 

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

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

1069 reference. 

1070 

1071 Keywords: 

1072 

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

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

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

1076 is false. 

1077 

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

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

1080 is deleted to prevent lingering references which hamper memory 

1081 management. 

1082 

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

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

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

1086 

1087 if force or self.call_pdb: 

1088 if self.pdb is None: 

1089 self.pdb = self.debugger_cls() 

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

1091 # for pdb 

1092 display_trap = DisplayTrap(hook=sys.__displayhook__) 

1093 with display_trap: 

1094 self.pdb.reset() 

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

1096 if hasattr(self, 'tb') and self.tb is not None: 

1097 etb = self.tb 

1098 else: 

1099 etb = self.tb = sys.last_traceback 

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

1101 assert self.tb.tb_next is not None 

1102 self.tb = self.tb.tb_next 

1103 if etb and etb.tb_next: 

1104 etb = etb.tb_next 

1105 self.pdb.botframe = etb.tb_frame 

1106 self.pdb.interaction(None, etb) 

1107 

1108 if hasattr(self, 'tb'): 

1109 del self.tb 

1110 

1111 def handler(self, info=None): 

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

1113 self.tb = etb 

1114 ostream = self.ostream 

1115 ostream.flush() 

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

1117 ostream.write('\n') 

1118 ostream.flush() 

1119 

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

1121 # out the right info on its own. 

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

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

1124 if etb is None: 

1125 self.handler() 

1126 else: 

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

1128 try: 

1129 self.debugger() 

1130 except KeyboardInterrupt: 

1131 print("\nKeyboardInterrupt") 

1132 

1133 

1134#---------------------------------------------------------------------------- 

1135class FormattedTB(VerboseTB, ListTB): 

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

1137 

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

1139 

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

1141 

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

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

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

1145 like Python shells). """ 

1146 

1147 mode: str 

1148 

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

1150 ostream=None, 

1151 tb_offset=0, long_header=False, include_vars=False, 

1152 check_cache=None, debugger_cls=None, 

1153 parent=None, config=None): 

1154 

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

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

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

1158 

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

1160 ostream=ostream, tb_offset=tb_offset, 

1161 long_header=long_header, include_vars=include_vars, 

1162 check_cache=check_cache, debugger_cls=debugger_cls, 

1163 parent=parent, config=config) 

1164 

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

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

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

1168 Minimal='') 

1169 # set_mode also sets the tb_join_char attribute 

1170 self.set_mode(mode) 

1171 

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

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

1174 mode = self.mode 

1175 if mode in self.verbose_modes: 

1176 # Verbose modes need a full traceback 

1177 return VerboseTB.structured_traceback( 

1178 self, etype, value, tb, tb_offset, number_of_lines_of_context 

1179 ) 

1180 elif mode == 'Minimal': 

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

1182 else: 

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

1184 # out-of-date source code. 

1185 self.check_cache() 

1186 # Now we can extract and format the exception 

1187 return ListTB.structured_traceback( 

1188 self, etype, value, tb, tb_offset, number_of_lines_of_context 

1189 ) 

1190 

1191 def stb2text(self, stb): 

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

1193 return self.tb_join_char.join(stb) 

1194 

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

1196 """Switch to the desired mode. 

1197 

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

1199 

1200 if not mode: 

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

1202 len(self.valid_modes) 

1203 self.mode = self.valid_modes[new_idx] 

1204 elif mode not in self.valid_modes: 

1205 raise ValueError( 

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

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

1208 ) 

1209 else: 

1210 assert isinstance(mode, str) 

1211 self.mode = mode 

1212 # include variable details only in 'Verbose' mode 

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

1214 # Set the join character for generating text tracebacks 

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

1216 

1217 # some convenient shortcuts 

1218 def plain(self): 

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

1220 

1221 def context(self): 

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

1223 

1224 def verbose(self): 

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

1226 

1227 def minimal(self): 

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

1229 

1230 

1231#---------------------------------------------------------------------------- 

1232class AutoFormattedTB(FormattedTB): 

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

1234 

1235 It will find out about exceptions by itself. 

1236 

1237 A brief example:: 

1238 

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

1240 try: 

1241 ... 

1242 except: 

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

1244 """ 

1245 

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

1247 out=None, tb_offset=None): 

1248 """Print out a formatted exception traceback. 

1249 

1250 Optional arguments: 

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

1252 

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

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

1255 given at initialization time.""" 

1256 

1257 if out is None: 

1258 out = self.ostream 

1259 out.flush() 

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

1261 out.write('\n') 

1262 out.flush() 

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

1264 # that to the clients. 

1265 try: 

1266 self.debugger() 

1267 except KeyboardInterrupt: 

1268 print("\nKeyboardInterrupt") 

1269 

1270 def structured_traceback( 

1271 self, 

1272 etype=None, 

1273 value=None, 

1274 tb=None, 

1275 tb_offset=None, 

1276 number_of_lines_of_context=5, 

1277 ): 

1278 etype: type 

1279 value: BaseException 

1280 # tb: TracebackType or tupleof tb types ? 

1281 if etype is None: 

1282 etype, value, tb = sys.exc_info() 

1283 if isinstance(tb, tuple): 

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

1285 self.tb = tb[0] 

1286 else: 

1287 self.tb = tb 

1288 return FormattedTB.structured_traceback( 

1289 self, etype, value, tb, tb_offset, number_of_lines_of_context) 

1290 

1291 

1292#--------------------------------------------------------------------------- 

1293 

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

1295class ColorTB(FormattedTB): 

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

1297 

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

1299 FormattedTB.__init__(self, color_scheme=color_scheme, 

1300 call_pdb=call_pdb, **kwargs) 

1301 

1302 

1303class SyntaxTB(ListTB): 

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

1305 

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

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

1308 self.last_syntax_error = None 

1309 

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

1311 self.last_syntax_error = value 

1312 

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

1314 

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

1316 context=5): 

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

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

1319 # the current value. 

1320 if isinstance(value, SyntaxError) \ 

1321 and isinstance(value.filename, str) \ 

1322 and isinstance(value.lineno, int): 

1323 linecache.checkcache(value.filename) 

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

1325 if newtext: 

1326 value.text = newtext 

1327 self.last_syntax_error = value 

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

1329 tb_offset=tb_offset, context=context) 

1330 

1331 def clear_err_state(self): 

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

1333 e = self.last_syntax_error 

1334 self.last_syntax_error = None 

1335 return e 

1336 

1337 def stb2text(self, stb): 

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

1339 return ''.join(stb) 

1340 

1341 

1342# some internal-use functions 

1343def text_repr(value): 

1344 """Hopefully pretty robust repr equivalent.""" 

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

1346 try: 

1347 return pydoc.text.repr(value) 

1348 except KeyboardInterrupt: 

1349 raise 

1350 except: 

1351 try: 

1352 return repr(value) 

1353 except KeyboardInterrupt: 

1354 raise 

1355 except: 

1356 try: 

1357 # all still in an except block so we catch 

1358 # getattr raising 

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

1360 if name: 

1361 # ick, recursion 

1362 return text_repr(name) 

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

1364 if klass: 

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

1366 except KeyboardInterrupt: 

1367 raise 

1368 except: 

1369 return 'UNRECOVERABLE REPR FAILURE' 

1370 

1371 

1372def eqrepr(value, repr=text_repr): 

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

1374 

1375 

1376def nullrepr(value, repr=text_repr): 

1377 return ''