Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/IPython/core/ultratb.py: 17%
637 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:05 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:05 +0000
1# -*- coding: utf-8 -*-
2"""
3Verbose and colourful traceback formatting.
5**ColorTB**
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.
12Installation instructions for ColorTB::
14 import sys,ultratb
15 sys.excepthook = ultratb.ColorTB()
17**VerboseTB**
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.
27.. note::
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).
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).
41.. note::
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.
47Installation instructions for VerboseTB::
49 import sys,ultratb
50 sys.excepthook = ultratb.VerboseTB()
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'.
55Color schemes
56-------------
58The colors are defined in the class TBTools through the use of the
59ColorSchemeTable class. Currently the following exist:
61 - NoColor: allows all of this module to be used in any terminal (the color
62 escapes are just dummy blank strings).
64 - Linux: is meant to look good in a terminal like the Linux console (black
65 or very dark background).
67 - LightBG: similar to Linux but swaps dark/light colors to be more readable
68 in light background terminals.
70 - Neutral: a neutral color scheme that should be readable on both light and
71 dark background
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.
77Inheritance diagram:
79.. inheritance-diagram:: IPython.core.ultratb
80 :parts: 3
81"""
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#*****************************************************************************
92from collections.abc import Sequence
93import functools
94import inspect
95import linecache
96import pydoc
97import sys
98import time
99import traceback
100import types
101from types import TracebackType
102from typing import Any, List, Optional, Tuple
104import stack_data
105from pygments.formatters.terminal256 import Terminal256Formatter
106from pygments.styles import get_style_by_name
108import IPython.utils.colorable as colorable
109# IPython's own modules
110from IPython import get_ipython
111from IPython.core import debugger
112from IPython.core.display_trap import DisplayTrap
113from IPython.core.excolors import exception_colors
114from IPython.utils import PyColorize
115from IPython.utils import path as util_path
116from IPython.utils import py3compat
117from IPython.utils.terminal import get_terminal_size
119# Globals
120# amount of space to put line numbers before verbose tracebacks
121INDENT_SIZE = 8
123# Default color scheme. This is used, for example, by the traceback
124# formatter. When running in an actual IPython instance, the user's rc.colors
125# value is used, but having a module global makes this functionality available
126# to users of ultratb who are NOT running inside ipython.
127DEFAULT_SCHEME = 'NoColor'
128FAST_THRESHOLD = 10_000
130# ---------------------------------------------------------------------------
131# Code begins
133# Helper function -- largely belongs to VerboseTB, but we need the same
134# functionality to produce a pseudo verbose TB for SyntaxErrors, so that they
135# can be recognized properly by ipython.el's py-traceback-line-re
136# (SyntaxErrors have to be treated specially because they have no traceback)
139@functools.lru_cache()
140def count_lines_in_py_file(filename: str) -> int:
141 """
142 Given a filename, returns the number of lines in the file
143 if it ends with the extension ".py". Otherwise, returns 0.
144 """
145 if not filename.endswith(".py"):
146 return 0
147 else:
148 try:
149 with open(filename, "r") as file:
150 s = sum(1 for line in file)
151 except UnicodeError:
152 return 0
153 return s
155 """
156 Given a frame object, returns the total number of lines in the file
157 if the filename ends with the extension ".py". Otherwise, returns 0.
158 """
161def get_line_number_of_frame(frame: types.FrameType) -> int:
162 """
163 Given a frame object, returns the total number of lines in the file
164 containing the frame's code object, or the number of lines in the
165 frame's source code if the file is not available.
167 Parameters
168 ----------
169 frame : FrameType
170 The frame object whose line number is to be determined.
172 Returns
173 -------
174 int
175 The total number of lines in the file containing the frame's
176 code object, or the number of lines in the frame's source code
177 if the file is not available.
178 """
179 filename = frame.f_code.co_filename
180 if filename is None:
181 print("No file....")
182 lines, first = inspect.getsourcelines(frame)
183 return first + len(lines)
184 return count_lines_in_py_file(filename)
187def _safe_string(value, what, func=str):
188 # Copied from cpython/Lib/traceback.py
189 try:
190 return func(value)
191 except:
192 return f"<{what} {func.__name__}() failed>"
195def _format_traceback_lines(lines, Colors, has_colors: bool, lvals):
196 """
197 Format tracebacks lines with pointing arrow, leading numbers...
199 Parameters
200 ----------
201 lines : list[Line]
202 Colors
203 ColorScheme used.
204 lvals : str
205 Values of local variables, already colored, to inject just after the error line.
206 """
207 numbers_width = INDENT_SIZE - 1
208 res = []
210 for stack_line in lines:
211 if stack_line is stack_data.LINE_GAP:
212 res.append('%s (...)%s\n' % (Colors.linenoEm, Colors.Normal))
213 continue
215 line = stack_line.render(pygmented=has_colors).rstrip('\n') + '\n'
216 lineno = stack_line.lineno
217 if stack_line.is_current:
218 # This is the line with the error
219 pad = numbers_width - len(str(lineno))
220 num = '%s%s' % (debugger.make_arrow(pad), str(lineno))
221 start_color = Colors.linenoEm
222 else:
223 num = '%*s' % (numbers_width, lineno)
224 start_color = Colors.lineno
226 line = '%s%s%s %s' % (start_color, num, Colors.Normal, line)
228 res.append(line)
229 if lvals and stack_line.is_current:
230 res.append(lvals + '\n')
231 return res
233def _simple_format_traceback_lines(lnum, index, lines, Colors, lvals, _line_format):
234 """
235 Format tracebacks lines with pointing arrow, leading numbers...
237 Parameters
238 ==========
240 lnum: int
241 number of the target line of code.
242 index: int
243 which line in the list should be highlighted.
244 lines: list[string]
245 Colors:
246 ColorScheme used.
247 lvals: bytes
248 Values of local variables, already colored, to inject just after the error line.
249 _line_format: f (str) -> (str, bool)
250 return (colorized version of str, failure to do so)
251 """
252 numbers_width = INDENT_SIZE - 1
253 res = []
254 for i, line in enumerate(lines, lnum - index):
255 # assert isinstance(line, str)
256 line = py3compat.cast_unicode(line)
258 new_line, err = _line_format(line, "str")
259 if not err:
260 line = new_line
262 if i == lnum:
263 # This is the line with the error
264 pad = numbers_width - len(str(i))
265 num = "%s%s" % (debugger.make_arrow(pad), str(lnum))
266 line = "%s%s%s %s%s" % (
267 Colors.linenoEm,
268 num,
269 Colors.line,
270 line,
271 Colors.Normal,
272 )
273 else:
274 num = "%*s" % (numbers_width, i)
275 line = "%s%s%s %s" % (Colors.lineno, num, Colors.Normal, line)
277 res.append(line)
278 if lvals and i == lnum:
279 res.append(lvals + "\n")
280 return res
283def _format_filename(file, ColorFilename, ColorNormal, *, lineno=None):
284 """
285 Format filename lines with custom formatting from caching compiler or `File *.py` by default
287 Parameters
288 ----------
289 file : str
290 ColorFilename
291 ColorScheme's filename coloring to be used.
292 ColorNormal
293 ColorScheme's normal coloring to be used.
294 """
295 ipinst = get_ipython()
296 if (
297 ipinst is not None
298 and (data := ipinst.compile.format_code_name(file)) is not None
299 ):
300 label, name = data
301 if lineno is None:
302 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}"
303 else:
304 tpl_link = (
305 f"{{label}} {ColorFilename}{{name}}, line {{lineno}}{ColorNormal}"
306 )
307 else:
308 label = "File"
309 name = util_path.compress_user(
310 py3compat.cast_unicode(file, util_path.fs_encoding)
311 )
312 if lineno is None:
313 tpl_link = f"{{label}} {ColorFilename}{{name}}{ColorNormal}"
314 else:
315 # can we make this the more friendly ", line {{lineno}}", or do we need to preserve the formatting with the colon?
316 tpl_link = f"{{label}} {ColorFilename}{{name}}:{{lineno}}{ColorNormal}"
318 return tpl_link.format(label=label, name=name, lineno=lineno)
320#---------------------------------------------------------------------------
321# Module classes
322class TBTools(colorable.Colorable):
323 """Basic tools used by all traceback printer classes."""
325 # Number of frames to skip when reporting tracebacks
326 tb_offset = 0
328 def __init__(
329 self,
330 color_scheme="NoColor",
331 call_pdb=False,
332 ostream=None,
333 parent=None,
334 config=None,
335 *,
336 debugger_cls=None,
337 ):
338 # Whether to call the interactive pdb debugger after printing
339 # tracebacks or not
340 super(TBTools, self).__init__(parent=parent, config=config)
341 self.call_pdb = call_pdb
343 # Output stream to write to. Note that we store the original value in
344 # a private attribute and then make the public ostream a property, so
345 # that we can delay accessing sys.stdout until runtime. The way
346 # things are written now, the sys.stdout object is dynamically managed
347 # so a reference to it should NEVER be stored statically. This
348 # property approach confines this detail to a single location, and all
349 # subclasses can simply access self.ostream for writing.
350 self._ostream = ostream
352 # Create color table
353 self.color_scheme_table = exception_colors()
355 self.set_colors(color_scheme)
356 self.old_scheme = color_scheme # save initial value for toggles
357 self.debugger_cls = debugger_cls or debugger.Pdb
359 if call_pdb:
360 self.pdb = self.debugger_cls()
361 else:
362 self.pdb = None
364 def _get_ostream(self):
365 """Output stream that exceptions are written to.
367 Valid values are:
369 - None: the default, which means that IPython will dynamically resolve
370 to sys.stdout. This ensures compatibility with most tools, including
371 Windows (where plain stdout doesn't recognize ANSI escapes).
373 - Any object with 'write' and 'flush' attributes.
374 """
375 return sys.stdout if self._ostream is None else self._ostream
377 def _set_ostream(self, val):
378 assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush'))
379 self._ostream = val
381 ostream = property(_get_ostream, _set_ostream)
383 @staticmethod
384 def _get_chained_exception(exception_value):
385 cause = getattr(exception_value, "__cause__", None)
386 if cause:
387 return cause
388 if getattr(exception_value, "__suppress_context__", False):
389 return None
390 return getattr(exception_value, "__context__", None)
392 def get_parts_of_chained_exception(
393 self, evalue
394 ) -> Optional[Tuple[type, BaseException, TracebackType]]:
395 chained_evalue = self._get_chained_exception(evalue)
397 if chained_evalue:
398 return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__
399 return None
401 def prepare_chained_exception_message(self, cause) -> List[Any]:
402 direct_cause = "\nThe above exception was the direct cause of the following exception:\n"
403 exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n"
405 if cause:
406 message = [[direct_cause]]
407 else:
408 message = [[exception_during_handling]]
409 return message
411 @property
412 def has_colors(self) -> bool:
413 return self.color_scheme_table.active_scheme_name.lower() != "nocolor"
415 def set_colors(self, *args, **kw):
416 """Shorthand access to the color table scheme selector method."""
418 # Set own color table
419 self.color_scheme_table.set_active_scheme(*args, **kw)
420 # for convenience, set Colors to the active scheme
421 self.Colors = self.color_scheme_table.active_colors
422 # Also set colors of debugger
423 if hasattr(self, 'pdb') and self.pdb is not None:
424 self.pdb.set_colors(*args, **kw)
426 def color_toggle(self):
427 """Toggle between the currently active color scheme and NoColor."""
429 if self.color_scheme_table.active_scheme_name == 'NoColor':
430 self.color_scheme_table.set_active_scheme(self.old_scheme)
431 self.Colors = self.color_scheme_table.active_colors
432 else:
433 self.old_scheme = self.color_scheme_table.active_scheme_name
434 self.color_scheme_table.set_active_scheme('NoColor')
435 self.Colors = self.color_scheme_table.active_colors
437 def stb2text(self, stb):
438 """Convert a structured traceback (a list) to a string."""
439 return '\n'.join(stb)
441 def text(self, etype, value, tb, tb_offset: Optional[int] = None, context=5):
442 """Return formatted traceback.
444 Subclasses may override this if they add extra arguments.
445 """
446 tb_list = self.structured_traceback(etype, value, tb,
447 tb_offset, context)
448 return self.stb2text(tb_list)
450 def structured_traceback(
451 self,
452 etype: type,
453 evalue: Optional[BaseException],
454 etb: Optional[TracebackType] = None,
455 tb_offset: Optional[int] = None,
456 number_of_lines_of_context: int = 5,
457 ):
458 """Return a list of traceback frames.
460 Must be implemented by each class.
461 """
462 raise NotImplementedError()
465#---------------------------------------------------------------------------
466class ListTB(TBTools):
467 """Print traceback information from a traceback list, with optional color.
469 Calling requires 3 arguments: (etype, evalue, elist)
470 as would be obtained by::
472 etype, evalue, tb = sys.exc_info()
473 if tb:
474 elist = traceback.extract_tb(tb)
475 else:
476 elist = None
478 It can thus be used by programs which need to process the traceback before
479 printing (such as console replacements based on the code module from the
480 standard library).
482 Because they are meant to be called without a full traceback (only a
483 list), instances of this class can't call the interactive pdb debugger."""
486 def __call__(self, etype, value, elist):
487 self.ostream.flush()
488 self.ostream.write(self.text(etype, value, elist))
489 self.ostream.write('\n')
491 def _extract_tb(self, tb):
492 if tb:
493 return traceback.extract_tb(tb)
494 else:
495 return None
497 def structured_traceback(
498 self,
499 etype: type,
500 evalue: Optional[BaseException],
501 etb: Optional[TracebackType] = None,
502 tb_offset: Optional[int] = None,
503 context=5,
504 ):
505 """Return a color formatted string with the traceback info.
507 Parameters
508 ----------
509 etype : exception type
510 Type of the exception raised.
511 evalue : object
512 Data stored in the exception
513 etb : list | TracebackType | None
514 If list: List of frames, see class docstring for details.
515 If Traceback: Traceback of the exception.
516 tb_offset : int, optional
517 Number of frames in the traceback to skip. If not given, the
518 instance evalue is used (set in constructor).
519 context : int, optional
520 Number of lines of context information to print.
522 Returns
523 -------
524 String with formatted exception.
525 """
526 # This is a workaround to get chained_exc_ids in recursive calls
527 # etb should not be a tuple if structured_traceback is not recursive
528 if isinstance(etb, tuple):
529 etb, chained_exc_ids = etb
530 else:
531 chained_exc_ids = set()
533 if isinstance(etb, list):
534 elist = etb
535 elif etb is not None:
536 elist = self._extract_tb(etb)
537 else:
538 elist = []
539 tb_offset = self.tb_offset if tb_offset is None else tb_offset
540 assert isinstance(tb_offset, int)
541 Colors = self.Colors
542 out_list = []
543 if elist:
545 if tb_offset and len(elist) > tb_offset:
546 elist = elist[tb_offset:]
548 out_list.append('Traceback %s(most recent call last)%s:' %
549 (Colors.normalEm, Colors.Normal) + '\n')
550 out_list.extend(self._format_list(elist))
551 # The exception info should be a single entry in the list.
552 lines = ''.join(self._format_exception_only(etype, evalue))
553 out_list.append(lines)
555 exception = self.get_parts_of_chained_exception(evalue)
557 if exception and (id(exception[1]) not in chained_exc_ids):
558 chained_exception_message = (
559 self.prepare_chained_exception_message(evalue.__cause__)[0]
560 if evalue is not None
561 else ""
562 )
563 etype, evalue, etb = exception
564 # Trace exception to avoid infinite 'cause' loop
565 chained_exc_ids.add(id(exception[1]))
566 chained_exceptions_tb_offset = 0
567 out_list = (
568 self.structured_traceback(
569 etype,
570 evalue,
571 (etb, chained_exc_ids), # type: ignore
572 chained_exceptions_tb_offset,
573 context,
574 )
575 + chained_exception_message
576 + out_list)
578 return out_list
580 def _format_list(self, extracted_list):
581 """Format a list of traceback entry tuples for printing.
583 Given a list of tuples as returned by extract_tb() or
584 extract_stack(), return a list of strings ready for printing.
585 Each string in the resulting list corresponds to the item with the
586 same index in the argument list. Each string ends in a newline;
587 the strings may contain internal newlines as well, for those items
588 whose source text line is not None.
590 Lifted almost verbatim from traceback.py
591 """
593 Colors = self.Colors
594 output_list = []
595 for ind, (filename, lineno, name, line) in enumerate(extracted_list):
596 normalCol, nameCol, fileCol, lineCol = (
597 # Emphasize the last entry
598 (Colors.normalEm, Colors.nameEm, Colors.filenameEm, Colors.line)
599 if ind == len(extracted_list) - 1
600 else (Colors.Normal, Colors.name, Colors.filename, "")
601 )
603 fns = _format_filename(filename, fileCol, normalCol, lineno=lineno)
604 item = f"{normalCol} {fns}"
606 if name != "<module>":
607 item += f" in {nameCol}{name}{normalCol}\n"
608 else:
609 item += "\n"
610 if line:
611 item += f"{lineCol} {line.strip()}{normalCol}\n"
612 output_list.append(item)
614 return output_list
616 def _format_exception_only(self, etype, value):
617 """Format the exception part of a traceback.
619 The arguments are the exception type and value such as given by
620 sys.exc_info()[:2]. The return value is a list of strings, each ending
621 in a newline. Normally, the list contains a single string; however,
622 for SyntaxError exceptions, it contains several lines that (when
623 printed) display detailed information about where the syntax error
624 occurred. The message indicating which exception occurred is the
625 always last string in the list.
627 Also lifted nearly verbatim from traceback.py
628 """
629 have_filedata = False
630 Colors = self.Colors
631 output_list = []
632 stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal)
633 if value is None:
634 # Not sure if this can still happen in Python 2.6 and above
635 output_list.append(stype + "\n")
636 else:
637 if issubclass(etype, SyntaxError):
638 have_filedata = True
639 if not value.filename: value.filename = "<string>"
640 if value.lineno:
641 lineno = value.lineno
642 textline = linecache.getline(value.filename, value.lineno)
643 else:
644 lineno = "unknown"
645 textline = ""
646 output_list.append(
647 "%s %s%s\n"
648 % (
649 Colors.normalEm,
650 _format_filename(
651 value.filename,
652 Colors.filenameEm,
653 Colors.normalEm,
654 lineno=(None if lineno == "unknown" else lineno),
655 ),
656 Colors.Normal,
657 )
658 )
659 if textline == "":
660 textline = py3compat.cast_unicode(value.text, "utf-8")
662 if textline is not None:
663 i = 0
664 while i < len(textline) and textline[i].isspace():
665 i += 1
666 output_list.append(
667 "%s %s%s\n" % (Colors.line, textline.strip(), Colors.Normal)
668 )
669 if value.offset is not None:
670 s = ' '
671 for c in textline[i:value.offset - 1]:
672 if c.isspace():
673 s += c
674 else:
675 s += " "
676 output_list.append(
677 "%s%s^%s\n" % (Colors.caret, s, Colors.Normal)
678 )
680 try:
681 s = value.msg
682 except Exception:
683 s = self._some_str(value)
684 if s:
685 output_list.append(
686 "%s%s:%s %s\n" % (stype, Colors.excName, Colors.Normal, s)
687 )
688 else:
689 output_list.append("%s\n" % stype)
691 # PEP-678 notes
692 output_list.extend(f"{x}\n" for x in getattr(value, "__notes__", []))
694 # sync with user hooks
695 if have_filedata:
696 ipinst = get_ipython()
697 if ipinst is not None:
698 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
700 return output_list
702 def get_exception_only(self, etype, value):
703 """Only print the exception type and message, without a traceback.
705 Parameters
706 ----------
707 etype : exception type
708 value : exception value
709 """
710 return ListTB.structured_traceback(self, etype, value)
712 def show_exception_only(self, etype, evalue):
713 """Only print the exception type and message, without a traceback.
715 Parameters
716 ----------
717 etype : exception type
718 evalue : exception value
719 """
720 # This method needs to use __call__ from *this* class, not the one from
721 # a subclass whose signature or behavior may be different
722 ostream = self.ostream
723 ostream.flush()
724 ostream.write('\n'.join(self.get_exception_only(etype, evalue)))
725 ostream.flush()
727 def _some_str(self, value):
728 # Lifted from traceback.py
729 try:
730 return py3compat.cast_unicode(str(value))
731 except:
732 return u'<unprintable %s object>' % type(value).__name__
735class FrameInfo:
736 """
737 Mirror of stack data's FrameInfo, but so that we can bypass highlighting on
738 really long frames.
739 """
741 description: Optional[str]
742 filename: Optional[str]
743 lineno: Tuple[int]
744 # number of context lines to use
745 context: Optional[int]
747 @classmethod
748 def _from_stack_data_FrameInfo(cls, frame_info):
749 return cls(
750 getattr(frame_info, "description", None),
751 getattr(frame_info, "filename", None), # type: ignore[arg-type]
752 getattr(frame_info, "lineno", None), # type: ignore[arg-type]
753 getattr(frame_info, "frame", None),
754 getattr(frame_info, "code", None),
755 sd=frame_info,
756 context=None,
757 )
759 def __init__(
760 self,
761 description: Optional[str],
762 filename: str,
763 lineno: Tuple[int],
764 frame,
765 code,
766 *,
767 sd=None,
768 context=None,
769 ):
770 self.description = description
771 self.filename = filename
772 self.lineno = lineno
773 self.frame = frame
774 self.code = code
775 self._sd = sd
776 self.context = context
778 # self.lines = []
779 if sd is None:
780 ix = inspect.getsourcelines(frame)
781 self.raw_lines = ix[0]
783 @property
784 def variables_in_executing_piece(self):
785 if self._sd:
786 return self._sd.variables_in_executing_piece
787 else:
788 return []
790 @property
791 def lines(self):
792 return self._sd.lines
794 @property
795 def executing(self):
796 if self._sd:
797 return self._sd.executing
798 else:
799 return None
802# ----------------------------------------------------------------------------
803class VerboseTB(TBTools):
804 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
805 of HTML. Requires inspect and pydoc. Crazy, man.
807 Modified version which optionally strips the topmost entries from the
808 traceback, to be used with alternate interpreters (because their own code
809 would appear in the traceback)."""
811 _tb_highlight = "bg:ansiyellow"
812 _tb_highlight_style = "default"
814 def __init__(
815 self,
816 color_scheme: str = "Linux",
817 call_pdb: bool = False,
818 ostream=None,
819 tb_offset: int = 0,
820 long_header: bool = False,
821 include_vars: bool = True,
822 check_cache=None,
823 debugger_cls=None,
824 parent=None,
825 config=None,
826 ):
827 """Specify traceback offset, headers and color scheme.
829 Define how many frames to drop from the tracebacks. Calling it with
830 tb_offset=1 allows use of this handler in interpreters which will have
831 their own code at the top of the traceback (VerboseTB will first
832 remove that frame before printing the traceback info)."""
833 TBTools.__init__(
834 self,
835 color_scheme=color_scheme,
836 call_pdb=call_pdb,
837 ostream=ostream,
838 parent=parent,
839 config=config,
840 debugger_cls=debugger_cls,
841 )
842 self.tb_offset = tb_offset
843 self.long_header = long_header
844 self.include_vars = include_vars
845 # By default we use linecache.checkcache, but the user can provide a
846 # different check_cache implementation. This was formerly used by the
847 # IPython kernel for interactive code, but is no longer necessary.
848 if check_cache is None:
849 check_cache = linecache.checkcache
850 self.check_cache = check_cache
852 self.skip_hidden = True
854 def format_record(self, frame_info: FrameInfo):
855 """Format a single stack frame"""
856 assert isinstance(frame_info, FrameInfo)
857 Colors = self.Colors # just a shorthand + quicker name lookup
858 ColorsNormal = Colors.Normal # used a lot
860 if isinstance(frame_info._sd, stack_data.RepeatedFrames):
861 return ' %s[... skipping similar frames: %s]%s\n' % (
862 Colors.excName, frame_info.description, ColorsNormal)
864 indent = " " * INDENT_SIZE
865 em_normal = "%s\n%s%s" % (Colors.valEm, indent, ColorsNormal)
866 tpl_call = f"in {Colors.vName}{{file}}{Colors.valEm}{{scope}}{ColorsNormal}"
867 tpl_call_fail = "in %s%%s%s(***failed resolving arguments***)%s" % (
868 Colors.vName,
869 Colors.valEm,
870 ColorsNormal,
871 )
872 tpl_name_val = "%%s %s= %%s%s" % (Colors.valEm, ColorsNormal)
874 link = _format_filename(
875 frame_info.filename,
876 Colors.filenameEm,
877 ColorsNormal,
878 lineno=frame_info.lineno,
879 )
880 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
881 if frame_info.executing is not None:
882 func = frame_info.executing.code_qualname()
883 else:
884 func = "?"
885 if func == "<module>":
886 call = ""
887 else:
888 # Decide whether to include variable details or not
889 var_repr = eqrepr if self.include_vars else nullrepr
890 try:
891 scope = inspect.formatargvalues(
892 args, varargs, varkw, locals_, formatvalue=var_repr
893 )
894 call = tpl_call.format(file=func, scope=scope)
895 except KeyError:
896 # This happens in situations like errors inside generator
897 # expressions, where local variables are listed in the
898 # line, but can't be extracted from the frame. I'm not
899 # 100% sure this isn't actually a bug in inspect itself,
900 # but since there's no info for us to compute with, the
901 # best we can do is report the failure and move on. Here
902 # we must *not* call any traceback construction again,
903 # because that would mess up use of %debug later on. So we
904 # simply report the failure and move on. The only
905 # limitation will be that this frame won't have locals
906 # listed in the call signature. Quite subtle problem...
907 # I can't think of a good way to validate this in a unit
908 # test, but running a script consisting of:
909 # dict( (k,v.strip()) for (k,v) in range(10) )
910 # will illustrate the error, if this exception catch is
911 # disabled.
912 call = tpl_call_fail % func
914 lvals = ''
915 lvals_list = []
916 if self.include_vars:
917 try:
918 # we likely want to fix stackdata at some point, but
919 # still need a workaround.
920 fibp = frame_info.variables_in_executing_piece
921 for var in fibp:
922 lvals_list.append(tpl_name_val % (var.name, repr(var.value)))
923 except Exception:
924 lvals_list.append(
925 "Exception trying to inspect frame. No more locals available."
926 )
927 if lvals_list:
928 lvals = '%s%s' % (indent, em_normal.join(lvals_list))
930 result = f'{link}{", " if call else ""}{call}\n'
931 if frame_info._sd is None:
932 # fast fallback if file is too long
933 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
934 link = tpl_link % util_path.compress_user(frame_info.filename)
935 level = "%s %s\n" % (link, call)
936 _line_format = PyColorize.Parser(
937 style=self.color_scheme_table.active_scheme_name, parent=self
938 ).format2
939 first_line = frame_info.code.co_firstlineno
940 current_line = frame_info.lineno[0]
941 raw_lines = frame_info.raw_lines
942 index = current_line - first_line
944 if index >= frame_info.context:
945 start = max(index - frame_info.context, 0)
946 stop = index + frame_info.context
947 index = frame_info.context
948 else:
949 start = 0
950 stop = index + frame_info.context
951 raw_lines = raw_lines[start:stop]
953 return "%s%s" % (
954 level,
955 "".join(
956 _simple_format_traceback_lines(
957 current_line,
958 index,
959 raw_lines,
960 Colors,
961 lvals,
962 _line_format,
963 )
964 ),
965 )
966 # result += "\n".join(frame_info.raw_lines)
967 else:
968 result += "".join(
969 _format_traceback_lines(
970 frame_info.lines, Colors, self.has_colors, lvals
971 )
972 )
973 return result
975 def prepare_header(self, etype: str, long_version: bool = False):
976 colors = self.Colors # just a shorthand + quicker name lookup
977 colorsnormal = colors.Normal # used a lot
978 exc = '%s%s%s' % (colors.excName, etype, colorsnormal)
979 width = min(75, get_terminal_size()[0])
980 if long_version:
981 # Header with the exception type, python version, and date
982 pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
983 date = time.ctime(time.time())
985 head = "%s%s%s\n%s%s%s\n%s" % (
986 colors.topline,
987 "-" * width,
988 colorsnormal,
989 exc,
990 " " * (width - len(etype) - len(pyver)),
991 pyver,
992 date.rjust(width),
993 )
994 head += (
995 "\nA problem occurred executing Python code. Here is the sequence of function"
996 "\ncalls leading up to the error, with the most recent (innermost) call last."
997 )
998 else:
999 # Simplified header
1000 head = "%s%s" % (
1001 exc,
1002 "Traceback (most recent call last)".rjust(width - len(etype)),
1003 )
1005 return head
1007 def format_exception(self, etype, evalue):
1008 colors = self.Colors # just a shorthand + quicker name lookup
1009 colorsnormal = colors.Normal # used a lot
1010 # Get (safely) a string form of the exception info
1011 try:
1012 etype_str, evalue_str = map(str, (etype, evalue))
1013 except:
1014 # User exception is improperly defined.
1015 etype, evalue = str, sys.exc_info()[:2]
1016 etype_str, evalue_str = map(str, (etype, evalue))
1018 # PEP-678 notes
1019 notes = getattr(evalue, "__notes__", [])
1020 if not isinstance(notes, Sequence) or isinstance(notes, (str, bytes)):
1021 notes = [_safe_string(notes, "__notes__", func=repr)]
1023 # ... and format it
1024 return [
1025 "{}{}{}: {}".format(
1026 colors.excName,
1027 etype_str,
1028 colorsnormal,
1029 py3compat.cast_unicode(evalue_str),
1030 ),
1031 *(
1032 "{}{}".format(
1033 colorsnormal, _safe_string(py3compat.cast_unicode(n), "note")
1034 )
1035 for n in notes
1036 ),
1037 ]
1039 def format_exception_as_a_whole(
1040 self,
1041 etype: type,
1042 evalue: Optional[BaseException],
1043 etb: Optional[TracebackType],
1044 number_of_lines_of_context,
1045 tb_offset: Optional[int],
1046 ):
1047 """Formats the header, traceback and exception message for a single exception.
1049 This may be called multiple times by Python 3 exception chaining
1050 (PEP 3134).
1051 """
1052 # some locals
1053 orig_etype = etype
1054 try:
1055 etype = etype.__name__ # type: ignore
1056 except AttributeError:
1057 pass
1059 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1060 assert isinstance(tb_offset, int)
1061 head = self.prepare_header(str(etype), self.long_header)
1062 records = (
1063 self.get_records(etb, number_of_lines_of_context, tb_offset) if etb else []
1064 )
1066 frames = []
1067 skipped = 0
1068 lastrecord = len(records) - 1
1069 for i, record in enumerate(records):
1070 if (
1071 not isinstance(record._sd, stack_data.RepeatedFrames)
1072 and self.skip_hidden
1073 ):
1074 if (
1075 record.frame.f_locals.get("__tracebackhide__", 0)
1076 and i != lastrecord
1077 ):
1078 skipped += 1
1079 continue
1080 if skipped:
1081 Colors = self.Colors # just a shorthand + quicker name lookup
1082 ColorsNormal = Colors.Normal # used a lot
1083 frames.append(
1084 " %s[... skipping hidden %s frame]%s\n"
1085 % (Colors.excName, skipped, ColorsNormal)
1086 )
1087 skipped = 0
1088 frames.append(self.format_record(record))
1089 if skipped:
1090 Colors = self.Colors # just a shorthand + quicker name lookup
1091 ColorsNormal = Colors.Normal # used a lot
1092 frames.append(
1093 " %s[... skipping hidden %s frame]%s\n"
1094 % (Colors.excName, skipped, ColorsNormal)
1095 )
1097 formatted_exception = self.format_exception(etype, evalue)
1098 if records:
1099 frame_info = records[-1]
1100 ipinst = get_ipython()
1101 if ipinst is not None:
1102 ipinst.hooks.synchronize_with_editor(frame_info.filename, frame_info.lineno, 0)
1104 return [[head] + frames + formatted_exception]
1106 def get_records(
1107 self, etb: TracebackType, number_of_lines_of_context: int, tb_offset: int
1108 ):
1109 assert etb is not None
1110 context = number_of_lines_of_context - 1
1111 after = context // 2
1112 before = context - after
1113 if self.has_colors:
1114 style = get_style_by_name(self._tb_highlight_style)
1115 style = stack_data.style_with_executing_node(style, self._tb_highlight)
1116 formatter = Terminal256Formatter(style=style)
1117 else:
1118 formatter = None
1119 options = stack_data.Options(
1120 before=before,
1121 after=after,
1122 pygments_formatter=formatter,
1123 )
1125 # Let's estimate the amount of code we will have to parse/highlight.
1126 cf: Optional[TracebackType] = etb
1127 max_len = 0
1128 tbs = []
1129 while cf is not None:
1130 try:
1131 mod = inspect.getmodule(cf.tb_frame)
1132 if mod is not None:
1133 mod_name = mod.__name__
1134 root_name, *_ = mod_name.split(".")
1135 if root_name == "IPython":
1136 cf = cf.tb_next
1137 continue
1138 max_len = get_line_number_of_frame(cf.tb_frame)
1140 except OSError:
1141 max_len = 0
1142 max_len = max(max_len, max_len)
1143 tbs.append(cf)
1144 cf = getattr(cf, "tb_next", None)
1146 if max_len > FAST_THRESHOLD:
1147 FIs = []
1148 for tb in tbs:
1149 frame = tb.tb_frame # type: ignore
1150 lineno = (frame.f_lineno,)
1151 code = frame.f_code
1152 filename = code.co_filename
1153 # TODO: Here we need to use before/after/
1154 FIs.append(
1155 FrameInfo(
1156 "Raw frame", filename, lineno, frame, code, context=context
1157 )
1158 )
1159 return FIs
1160 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
1161 res = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
1162 return res
1164 def structured_traceback(
1165 self,
1166 etype: type,
1167 evalue: Optional[BaseException],
1168 etb: Optional[TracebackType] = None,
1169 tb_offset: Optional[int] = None,
1170 number_of_lines_of_context: int = 5,
1171 ):
1172 """Return a nice text document describing the traceback."""
1173 formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context,
1174 tb_offset)
1176 colors = self.Colors # just a shorthand + quicker name lookup
1177 colorsnormal = colors.Normal # used a lot
1178 head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal)
1179 structured_traceback_parts = [head]
1180 chained_exceptions_tb_offset = 0
1181 lines_of_context = 3
1182 formatted_exceptions = formatted_exception
1183 exception = self.get_parts_of_chained_exception(evalue)
1184 if exception:
1185 assert evalue is not None
1186 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1187 etype, evalue, etb = exception
1188 else:
1189 evalue = None
1190 chained_exc_ids = set()
1191 while evalue:
1192 formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context,
1193 chained_exceptions_tb_offset)
1194 exception = self.get_parts_of_chained_exception(evalue)
1196 if exception and not id(exception[1]) in chained_exc_ids:
1197 chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop
1198 formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__)
1199 etype, evalue, etb = exception
1200 else:
1201 evalue = None
1203 # we want to see exceptions in a reversed order:
1204 # the first exception should be on top
1205 for formatted_exception in reversed(formatted_exceptions):
1206 structured_traceback_parts += formatted_exception
1208 return structured_traceback_parts
1210 def debugger(self, force: bool = False):
1211 """Call up the pdb debugger if desired, always clean up the tb
1212 reference.
1214 Keywords:
1216 - force(False): by default, this routine checks the instance call_pdb
1217 flag and does not actually invoke the debugger if the flag is false.
1218 The 'force' option forces the debugger to activate even if the flag
1219 is false.
1221 If the call_pdb flag is set, the pdb interactive debugger is
1222 invoked. In all cases, the self.tb reference to the current traceback
1223 is deleted to prevent lingering references which hamper memory
1224 management.
1226 Note that each call to pdb() does an 'import readline', so if your app
1227 requires a special setup for the readline completers, you'll have to
1228 fix that by hand after invoking the exception handler."""
1230 if force or self.call_pdb:
1231 if self.pdb is None:
1232 self.pdb = self.debugger_cls()
1233 # the system displayhook may have changed, restore the original
1234 # for pdb
1235 display_trap = DisplayTrap(hook=sys.__displayhook__)
1236 with display_trap:
1237 self.pdb.reset()
1238 # Find the right frame so we don't pop up inside ipython itself
1239 if hasattr(self, "tb") and self.tb is not None: # type: ignore[has-type]
1240 etb = self.tb # type: ignore[has-type]
1241 else:
1242 etb = self.tb = sys.last_traceback
1243 while self.tb is not None and self.tb.tb_next is not None:
1244 assert self.tb.tb_next is not None
1245 self.tb = self.tb.tb_next
1246 if etb and etb.tb_next:
1247 etb = etb.tb_next
1248 self.pdb.botframe = etb.tb_frame
1249 # last_value should be deprecated, but last-exc sometimme not set
1250 # please check why later and remove the getattr.
1251 exc = sys.last_value if sys.version_info < (3, 12) else getattr(sys, "last_exc", sys.last_value) # type: ignore[attr-defined]
1252 if exc:
1253 self.pdb.interaction(None, exc)
1254 else:
1255 self.pdb.interaction(None, etb)
1257 if hasattr(self, 'tb'):
1258 del self.tb
1260 def handler(self, info=None):
1261 (etype, evalue, etb) = info or sys.exc_info()
1262 self.tb = etb
1263 ostream = self.ostream
1264 ostream.flush()
1265 ostream.write(self.text(etype, evalue, etb))
1266 ostream.write('\n')
1267 ostream.flush()
1269 # Changed so an instance can just be called as VerboseTB_inst() and print
1270 # out the right info on its own.
1271 def __call__(self, etype=None, evalue=None, etb=None):
1272 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
1273 if etb is None:
1274 self.handler()
1275 else:
1276 self.handler((etype, evalue, etb))
1277 try:
1278 self.debugger()
1279 except KeyboardInterrupt:
1280 print("\nKeyboardInterrupt")
1283#----------------------------------------------------------------------------
1284class FormattedTB(VerboseTB, ListTB):
1285 """Subclass ListTB but allow calling with a traceback.
1287 It can thus be used as a sys.excepthook for Python > 2.1.
1289 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1291 Allows a tb_offset to be specified. This is useful for situations where
1292 one needs to remove a number of topmost frames from the traceback (such as
1293 occurs with python programs that themselves execute other python code,
1294 like Python shells). """
1296 mode: str
1298 def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False,
1299 ostream=None,
1300 tb_offset=0, long_header=False, include_vars=False,
1301 check_cache=None, debugger_cls=None,
1302 parent=None, config=None):
1304 # NEVER change the order of this list. Put new modes at the end:
1305 self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal']
1306 self.verbose_modes = self.valid_modes[1:3]
1308 VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb,
1309 ostream=ostream, tb_offset=tb_offset,
1310 long_header=long_header, include_vars=include_vars,
1311 check_cache=check_cache, debugger_cls=debugger_cls,
1312 parent=parent, config=config)
1314 # Different types of tracebacks are joined with different separators to
1315 # form a single string. They are taken from this dict
1316 self._join_chars = dict(Plain='', Context='\n', Verbose='\n',
1317 Minimal='')
1318 # set_mode also sets the tb_join_char attribute
1319 self.set_mode(mode)
1321 def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5):
1322 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1323 mode = self.mode
1324 if mode in self.verbose_modes:
1325 # Verbose modes need a full traceback
1326 return VerboseTB.structured_traceback(
1327 self, etype, value, tb, tb_offset, number_of_lines_of_context
1328 )
1329 elif mode == 'Minimal':
1330 return ListTB.get_exception_only(self, etype, value)
1331 else:
1332 # We must check the source cache because otherwise we can print
1333 # out-of-date source code.
1334 self.check_cache()
1335 # Now we can extract and format the exception
1336 return ListTB.structured_traceback(
1337 self, etype, value, tb, tb_offset, number_of_lines_of_context
1338 )
1340 def stb2text(self, stb):
1341 """Convert a structured traceback (a list) to a string."""
1342 return self.tb_join_char.join(stb)
1344 def set_mode(self, mode: Optional[str] = None):
1345 """Switch to the desired mode.
1347 If mode is not specified, cycles through the available modes."""
1349 if not mode:
1350 new_idx = (self.valid_modes.index(self.mode) + 1 ) % \
1351 len(self.valid_modes)
1352 self.mode = self.valid_modes[new_idx]
1353 elif mode not in self.valid_modes:
1354 raise ValueError(
1355 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1356 "Valid modes: " + str(self.valid_modes)
1357 )
1358 else:
1359 assert isinstance(mode, str)
1360 self.mode = mode
1361 # include variable details only in 'Verbose' mode
1362 self.include_vars = (self.mode == self.valid_modes[2])
1363 # Set the join character for generating text tracebacks
1364 self.tb_join_char = self._join_chars[self.mode]
1366 # some convenient shortcuts
1367 def plain(self):
1368 self.set_mode(self.valid_modes[0])
1370 def context(self):
1371 self.set_mode(self.valid_modes[1])
1373 def verbose(self):
1374 self.set_mode(self.valid_modes[2])
1376 def minimal(self):
1377 self.set_mode(self.valid_modes[3])
1380#----------------------------------------------------------------------------
1381class AutoFormattedTB(FormattedTB):
1382 """A traceback printer which can be called on the fly.
1384 It will find out about exceptions by itself.
1386 A brief example::
1388 AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux')
1389 try:
1390 ...
1391 except:
1392 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1393 """
1395 def __call__(self, etype=None, evalue=None, etb=None,
1396 out=None, tb_offset=None):
1397 """Print out a formatted exception traceback.
1399 Optional arguments:
1400 - out: an open file-like object to direct output to.
1402 - tb_offset: the number of frames to skip over in the stack, on a
1403 per-call basis (this overrides temporarily the instance's tb_offset
1404 given at initialization time."""
1406 if out is None:
1407 out = self.ostream
1408 out.flush()
1409 out.write(self.text(etype, evalue, etb, tb_offset))
1410 out.write('\n')
1411 out.flush()
1412 # FIXME: we should remove the auto pdb behavior from here and leave
1413 # that to the clients.
1414 try:
1415 self.debugger()
1416 except KeyboardInterrupt:
1417 print("\nKeyboardInterrupt")
1419 def structured_traceback(
1420 self,
1421 etype: type,
1422 evalue: Optional[BaseException],
1423 etb: Optional[TracebackType] = None,
1424 tb_offset: Optional[int] = None,
1425 number_of_lines_of_context: int = 5,
1426 ):
1427 # tb: TracebackType or tupleof tb types ?
1428 if etype is None:
1429 etype, evalue, etb = sys.exc_info()
1430 if isinstance(etb, tuple):
1431 # tb is a tuple if this is a chained exception.
1432 self.tb = etb[0]
1433 else:
1434 self.tb = etb
1435 return FormattedTB.structured_traceback(
1436 self, etype, evalue, etb, tb_offset, number_of_lines_of_context
1437 )
1440#---------------------------------------------------------------------------
1442# A simple class to preserve Nathan's original functionality.
1443class ColorTB(FormattedTB):
1444 """Shorthand to initialize a FormattedTB in Linux colors mode."""
1446 def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs):
1447 FormattedTB.__init__(self, color_scheme=color_scheme,
1448 call_pdb=call_pdb, **kwargs)
1451class SyntaxTB(ListTB):
1452 """Extension which holds some state: the last exception value"""
1454 def __init__(self, color_scheme='NoColor', parent=None, config=None):
1455 ListTB.__init__(self, color_scheme, parent=parent, config=config)
1456 self.last_syntax_error = None
1458 def __call__(self, etype, value, elist):
1459 self.last_syntax_error = value
1461 ListTB.__call__(self, etype, value, elist)
1463 def structured_traceback(self, etype, value, elist, tb_offset=None,
1464 context=5):
1465 # If the source file has been edited, the line in the syntax error can
1466 # be wrong (retrieved from an outdated cache). This replaces it with
1467 # the current value.
1468 if isinstance(value, SyntaxError) \
1469 and isinstance(value.filename, str) \
1470 and isinstance(value.lineno, int):
1471 linecache.checkcache(value.filename)
1472 newtext = linecache.getline(value.filename, value.lineno)
1473 if newtext:
1474 value.text = newtext
1475 self.last_syntax_error = value
1476 return super(SyntaxTB, self).structured_traceback(etype, value, elist,
1477 tb_offset=tb_offset, context=context)
1479 def clear_err_state(self):
1480 """Return the current error state and clear it"""
1481 e = self.last_syntax_error
1482 self.last_syntax_error = None
1483 return e
1485 def stb2text(self, stb):
1486 """Convert a structured traceback (a list) to a string."""
1487 return ''.join(stb)
1490# some internal-use functions
1491def text_repr(value):
1492 """Hopefully pretty robust repr equivalent."""
1493 # this is pretty horrible but should always return *something*
1494 try:
1495 return pydoc.text.repr(value) # type: ignore[call-arg]
1496 except KeyboardInterrupt:
1497 raise
1498 except:
1499 try:
1500 return repr(value)
1501 except KeyboardInterrupt:
1502 raise
1503 except:
1504 try:
1505 # all still in an except block so we catch
1506 # getattr raising
1507 name = getattr(value, '__name__', None)
1508 if name:
1509 # ick, recursion
1510 return text_repr(name)
1511 klass = getattr(value, '__class__', None)
1512 if klass:
1513 return '%s instance' % text_repr(klass)
1514 except KeyboardInterrupt:
1515 raise
1516 except:
1517 return 'UNRECOVERABLE REPR FAILURE'
1520def eqrepr(value, repr=text_repr):
1521 return '=%s' % repr(value)
1524def nullrepr(value, repr=text_repr):
1525 return ''