Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/IPython/core/ultratb.py: 16%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Verbose and colourful traceback formatting.
4**ColorTB**
6I've always found it a bit hard to visually parse tracebacks in Python. The
7ColorTB class is a solution to that problem. It colors the different parts of a
8traceback in a manner similar to what you would expect from a syntax-highlighting
9text editor.
11Installation instructions for ColorTB::
13 import sys,ultratb
14 sys.excepthook = ultratb.ColorTB()
16**VerboseTB**
18I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds
19of useful info when a traceback occurs. Ping originally had it spit out HTML
20and intended it for CGI programmers, but why should they have all the fun? I
21altered it to spit out colored text to the terminal. It's a bit overwhelming,
22but kind of neat, and maybe useful for long-running programs that you believe
23are bug-free. If a crash *does* occur in that type of program you want details.
24Give it a shot--you'll love it or you'll hate it.
26.. note::
28 The Verbose mode prints the variables currently visible where the exception
29 happened (shortening their strings if too long). This can potentially be
30 very slow, if you happen to have a huge data structure whose string
31 representation is complex to compute. Your computer may appear to freeze for
32 a while with cpu usage at 100%. If this occurs, you can cancel the traceback
33 with Ctrl-C (maybe hitting it more than once).
35 If you encounter this kind of situation often, you may want to use the
36 Verbose_novars mode instead of the regular Verbose, which avoids formatting
37 variables (but otherwise includes the information and context given by
38 Verbose).
40.. note::
42 The verbose mode print all variables in the stack, which means it can
43 potentially leak sensitive information like access keys, or unencrypted
44 password.
46Installation instructions for VerboseTB::
48 import sys,ultratb
49 sys.excepthook = ultratb.VerboseTB()
51Note: Much of the code in this module was lifted verbatim from the standard
52library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'.
55Inheritance diagram:
57.. inheritance-diagram:: IPython.core.ultratb
58 :parts: 3
59"""
61# *****************************************************************************
62# Copyright (C) 2001 Nathaniel Gray <n8gray@caltech.edu>
63# Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
64#
65# Distributed under the terms of the BSD License. The full license is in
66# the file COPYING, distributed as part of this software.
67# *****************************************************************************
69import functools
70import inspect
71import linecache
72import sys
73import time
74import traceback
75import types
76import warnings
77from collections.abc import Sequence
78from types import TracebackType
79from typing import Any, List, Optional, Tuple
80from collections.abc import Callable
82import stack_data
83from pygments.formatters.terminal256 import Terminal256Formatter
84from pygments.token import Token
86from IPython import get_ipython
87from IPython.utils import path as util_path
88from IPython.utils import py3compat
89from IPython.utils.PyColorize import Parser, Theme, TokenStream, theme_table
90from IPython.utils.terminal import get_terminal_size
92from .display_trap import DisplayTrap
93from .doctb import DocTB
94from .tbtools import (
95 FrameInfo,
96 TBTools,
97 _format_traceback_lines,
98 _safe_string,
99 _simple_format_traceback_lines,
100 _tokens_filename,
101 eqrepr,
102 get_line_number_of_frame,
103 nullrepr,
104 text_repr,
105)
107# Globals
108# amount of space to put line numbers before verbose tracebacks
109INDENT_SIZE = 8
111# When files are too long do not use stackdata to get frames.
112# it is too long.
113FAST_THRESHOLD = 10_000
115# ---------------------------------------------------------------------------
116class ListTB(TBTools):
117 """Print traceback information from a traceback list, with optional color.
119 Calling requires 3 arguments: (etype, evalue, elist)
120 as would be obtained by::
122 etype, evalue, tb = sys.exc_info()
123 if tb:
124 elist = traceback.extract_tb(tb)
125 else:
126 elist = None
128 It can thus be used by programs which need to process the traceback before
129 printing (such as console replacements based on the code module from the
130 standard library).
132 Because they are meant to be called without a full traceback (only a
133 list), instances of this class can't call the interactive pdb debugger."""
135 def __call__(
136 self,
137 etype: type[BaseException],
138 evalue: BaseException | None,
139 etb: TracebackType | None,
140 ) -> None:
141 self.ostream.flush()
142 self.ostream.write(self.text(etype, evalue, etb))
143 self.ostream.write("\n")
145 def _extract_tb(self, tb: TracebackType | None) -> traceback.StackSummary | None:
146 if tb:
147 return traceback.extract_tb(tb)
148 else:
149 return None
151 def structured_traceback(
152 self,
153 etype: type,
154 evalue: Optional[BaseException],
155 etb: Optional[TracebackType] = None,
156 tb_offset: Optional[int] = None,
157 context: int = 5,
158 ) -> list[str]:
159 """Return a color formatted string with the traceback info.
161 Parameters
162 ----------
163 etype : exception type
164 Type of the exception raised.
165 evalue : object
166 Data stored in the exception
167 etb : list | TracebackType | None
168 If list: List of frames, see class docstring for details.
169 If Traceback: Traceback of the exception.
170 tb_offset : int, optional
171 Number of frames in the traceback to skip. If not given, the
172 instance evalue is used (set in constructor).
173 context : int, optional
174 Number of lines of context information to print.
176 Returns
177 -------
178 String with formatted exception.
179 """
180 # This is a workaround to get chained_exc_ids in recursive calls
181 # etb should not be a tuple if structured_traceback is not recursive
182 if isinstance(etb, tuple):
183 etb, chained_exc_ids = etb
184 else:
185 chained_exc_ids = set()
186 elist: list[Any]
187 if isinstance(etb, list):
188 elist = etb
189 elif etb is not None:
190 elist = self._extract_tb(etb) # type: ignore[assignment]
191 else:
192 elist = []
193 tb_offset = self.tb_offset if tb_offset is None else tb_offset
194 assert isinstance(tb_offset, int)
195 out_list: list[str] = []
196 if elist:
197 if tb_offset and len(elist) > tb_offset:
198 elist = elist[tb_offset:]
200 out_list.append(
201 theme_table[self._theme_name].format(
202 [
203 (Token, "Traceback"),
204 (Token, " "),
205 (Token.NormalEm, "(most recent call last)"),
206 (Token, ":"),
207 (Token, "\n"),
208 ]
209 ),
210 )
211 out_list.extend(self._format_list(elist))
212 # The exception info should be a single entry in the list.
213 lines = "".join(self._format_exception_only(etype, evalue))
214 out_list.append(lines)
216 # Find chained exceptions if we have a traceback (not for exception-only mode)
217 if etb is not None:
218 exception = self.get_parts_of_chained_exception(evalue)
220 if exception and (id(exception[1]) not in chained_exc_ids):
221 chained_exception_message: list[str] = (
222 self.prepare_chained_exception_message(evalue.__cause__)[0]
223 if evalue is not None
224 else [""]
225 )
226 etype, evalue, etb = exception
227 # Trace exception to avoid infinite 'cause' loop
228 chained_exc_ids.add(id(exception[1]))
229 chained_exceptions_tb_offset = 0
230 ol1 = self.structured_traceback(
231 etype,
232 evalue,
233 (etb, chained_exc_ids), # type: ignore[arg-type]
234 chained_exceptions_tb_offset,
235 context,
236 )
237 ol2 = chained_exception_message
239 out_list = ol1 + ol2 + out_list
241 return out_list
243 def _format_list(self, extracted_list: list[Any]) -> list[str]:
244 """Format a list of traceback entry tuples for printing.
246 Given a list of tuples as returned by extract_tb() or
247 extract_stack(), return a list of strings ready for printing.
248 Each string in the resulting list corresponds to the item with the
249 same index in the argument list. Each string ends in a newline;
250 the strings may contain internal newlines as well, for those items
251 whose source text line is not None.
253 Lifted almost verbatim from traceback.py
254 """
256 output_list = []
257 for ind, (filename, lineno, name, line) in enumerate(extracted_list):
258 # Will emphasize the last entry
259 em = True if ind == len(extracted_list) - 1 else False
261 item = theme_table[self._theme_name].format(
262 [(Token.NormalEm if em else Token.Normal, " ")]
263 + _tokens_filename(em, filename, lineno=lineno)
264 )
266 # This seem to be only in xmode plain (%run sinpleer), investigate why not share with verbose.
267 # look at _tokens_filename in forma_record.
268 if name != "<module>":
269 item += theme_table[self._theme_name].format(
270 [
271 (Token.NormalEm if em else Token.Normal, " in "),
272 (Token.TB.NameEm if em else Token.TB.Name, name),
273 ]
274 )
275 item += theme_table[self._theme_name].format(
276 [(Token.NormalEm if em else Token, "\n")]
277 )
278 if line:
279 item += theme_table[self._theme_name].format(
280 [
281 (Token.Line if em else Token, " "),
282 (Token.Line if em else Token, line.strip()),
283 (Token, "\n"),
284 ]
285 )
286 output_list.append(item)
288 return output_list
290 def _format_exception_only(
291 self, etype: type[BaseException], value: BaseException | None
292 ) -> list[str]:
293 """Format the exception part of a traceback.
295 The arguments are the exception type and value such as given by
296 sys.exc_info()[:2]. The return value is a list of strings, each ending
297 in a newline. Normally, the list contains a single string; however,
298 for SyntaxError exceptions, it contains several lines that (when
299 printed) display detailed information about where the syntax error
300 occurred. The message indicating which exception occurred is the
301 always last string in the list.
303 Also lifted nearly verbatim from traceback.py
304 """
305 have_filedata = False
306 output_list = []
307 stype_tokens = [(Token.ExcName, etype.__name__)]
308 stype: str = theme_table[self._theme_name].format(stype_tokens)
309 if value is None:
310 # Not sure if this can still happen in Python 2.6 and above
311 output_list.append(stype + "\n")
312 else:
313 if issubclass(etype, SyntaxError):
314 assert hasattr(value, "filename")
315 assert hasattr(value, "lineno")
316 assert hasattr(value, "text")
317 assert hasattr(value, "offset")
318 assert hasattr(value, "msg")
319 have_filedata = True
320 if not value.filename:
321 value.filename = "<string>"
322 if value.lineno:
323 lineno = value.lineno
324 textline = linecache.getline(value.filename, value.lineno)
325 else:
326 lineno = "unknown"
327 textline = ""
328 output_list.append(
329 theme_table[self._theme_name].format(
330 [(Token, " ")]
331 + _tokens_filename(
332 True,
333 value.filename,
334 lineno=(None if lineno == "unknown" else lineno),
335 )
336 + [(Token, "\n")]
337 )
338 )
339 if textline == "":
340 # sep 2025:
341 # textline = py3compat.cast_unicode(value.text, "utf-8")
342 if value.text is None:
343 textline = ""
344 else:
345 assert isinstance(value.text, str)
346 textline = value.text
348 if textline is not None:
349 i = 0
350 while i < len(textline) and textline[i].isspace():
351 i += 1
352 output_list.append(
353 theme_table[self._theme_name].format(
354 [
355 (Token.Line, " "),
356 (Token.Line, textline.strip()),
357 (Token, "\n"),
358 ]
359 )
360 )
361 if value.offset is not None:
362 s = " "
363 for c in textline[i : value.offset - 1]:
364 if c.isspace():
365 s += c
366 else:
367 s += " "
368 output_list.append(
369 theme_table[self._theme_name].format(
370 [(Token.Caret, s + "^"), (Token, "\n")]
371 )
372 )
373 s = value.msg
374 else:
375 s = self._some_str(value)
376 if s:
377 output_list.append(
378 theme_table[self._theme_name].format(
379 stype_tokens
380 + [
381 (Token.ExcName, ":"),
382 (Token, " "),
383 (Token, s),
384 (Token, "\n"),
385 ]
386 )
387 )
388 else:
389 output_list.append("%s\n" % stype)
391 # PEP-678 notes
392 output_list.extend(f"{x}\n" for x in getattr(value, "__notes__", []))
394 # sync with user hooks
395 if have_filedata:
396 ipinst = get_ipython()
397 if ipinst is not None:
398 assert value is not None
399 assert hasattr(value, "lineno")
400 assert hasattr(value, "filename")
401 ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0)
403 return output_list
405 def get_exception_only(self, etype, value):
406 """Only print the exception type and message, without a traceback.
408 Parameters
409 ----------
410 etype : exception type
411 value : exception value
412 """
413 return ListTB.structured_traceback(self, etype, value)
415 def show_exception_only(
416 self, etype: BaseException | None, evalue: TracebackType | None
417 ) -> None:
418 """Only print the exception type and message, without a traceback.
420 Parameters
421 ----------
422 etype : exception type
423 evalue : exception value
424 """
425 # This method needs to use __call__ from *this* class, not the one from
426 # a subclass whose signature or behavior may be different
427 ostream = self.ostream
428 ostream.flush()
429 ostream.write("\n".join(self.get_exception_only(etype, evalue)))
430 ostream.flush()
432 def _some_str(self, value: Any) -> str:
433 # Lifted from traceback.py
434 try:
435 return str(value)
436 except:
437 return "<unprintable %s object>" % type(value).__name__
440_sentinel = object()
441_default = "default"
444# ----------------------------------------------------------------------------
445class VerboseTB(TBTools):
446 """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead
447 of HTML. Requires inspect and pydoc. Crazy, man.
449 Modified version which optionally strips the topmost entries from the
450 traceback, to be used with alternate interpreters (because their own code
451 would appear in the traceback)."""
453 tb_highlight = "bg:ansiyellow"
454 tb_highlight_style = "default"
456 _mode: str
458 def __init__(
459 self,
460 # TODO: no default ?
461 theme_name: str = _default,
462 call_pdb: bool = False,
463 ostream: Any = None,
464 tb_offset: int = 0,
465 long_header: bool = False,
466 include_vars: bool = True,
467 check_cache: Callable[[], None] | None = None,
468 debugger_cls: type | None = None,
469 *,
470 color_scheme: Any = _sentinel,
471 ):
472 """Specify traceback offset, headers and color scheme.
474 Define how many frames to drop from the tracebacks. Calling it with
475 tb_offset=1 allows use of this handler in interpreters which will have
476 their own code at the top of the traceback (VerboseTB will first
477 remove that frame before printing the traceback info)."""
478 if color_scheme is not _sentinel:
479 assert isinstance(color_scheme, str)
480 theme_name = color_scheme.lower()
482 warnings.warn(
483 "color_scheme is deprecated as of IPython 9.0 and replaced by "
484 "theme_name (which should be lowercase). As you passed a "
485 "color_scheme value I will try to see if I have corresponding "
486 "theme.",
487 stacklevel=2,
488 category=DeprecationWarning,
489 )
491 if theme_name != _default:
492 warnings.warn(
493 "You passed both `theme_name` and `color_scheme` "
494 "(deprecated) to VerboseTB constructor. `theme_name` will "
495 "be ignored for the time being.",
496 stacklevel=2,
497 category=DeprecationWarning,
498 )
500 if theme_name == _default:
501 theme_name = "linux"
503 assert isinstance(theme_name, str)
504 super().__init__(
505 theme_name=theme_name,
506 call_pdb=call_pdb,
507 ostream=ostream,
508 debugger_cls=debugger_cls,
509 )
510 self.tb_offset = tb_offset
511 self.long_header = long_header
512 self.include_vars = include_vars
513 # By default we use linecache.checkcache, but the user can provide a
514 # different check_cache implementation. This was formerly used by the
515 # IPython kernel for interactive code, but is no longer necessary.
516 if check_cache is None:
517 check_cache = linecache.checkcache
518 self.check_cache = check_cache
520 self.skip_hidden = True
522 def format_record(self, frame_info: FrameInfo) -> str:
523 """Format a single stack frame"""
524 assert isinstance(frame_info, FrameInfo)
526 if isinstance(frame_info._sd, stack_data.RepeatedFrames):
527 return theme_table[self._theme_name].format(
528 [
529 (Token, " "),
530 (
531 Token.ExcName,
532 "[... skipping similar frames: %s]" % frame_info.description,
533 ),
534 (Token, "\n"),
535 ]
536 )
538 indent: str = " " * INDENT_SIZE
540 assert isinstance(frame_info.lineno, int)
541 args, varargs, varkw, locals_ = inspect.getargvalues(frame_info.frame)
542 if frame_info.executing is not None:
543 func = frame_info.executing.code_qualname()
544 else:
545 func = "?"
546 if func == "<module>":
547 call = ""
548 else:
549 # Decide whether to include variable details or not
550 var_repr = eqrepr if self.include_vars else nullrepr
551 try:
552 scope = inspect.formatargvalues(
553 args, varargs, varkw, locals_, formatvalue=var_repr
554 )
555 assert isinstance(scope, str)
556 call = theme_table[self._theme_name].format(
557 [(Token, "in "), (Token.VName, func), (Token.ValEm, scope)]
558 )
559 except KeyError:
560 # This happens in situations like errors inside generator
561 # expressions, where local variables are listed in the
562 # line, but can't be extracted from the frame. I'm not
563 # 100% sure this isn't actually a bug in inspect itself,
564 # but since there's no info for us to compute with, the
565 # best we can do is report the failure and move on. Here
566 # we must *not* call any traceback construction again,
567 # because that would mess up use of %debug later on. So we
568 # simply report the failure and move on. The only
569 # limitation will be that this frame won't have locals
570 # listed in the call signature. Quite subtle problem...
571 # I can't think of a good way to validate this in a unit
572 # test, but running a script consisting of:
573 # dict( (k,v.strip()) for (k,v) in range(10) )
574 # will illustrate the error, if this exception catch is
575 # disabled.
576 call = theme_table[self._theme_name].format(
577 [
578 (Token, "in "),
579 (Token.VName, func),
580 (Token.ValEm, "(***failed resolving arguments***)"),
581 ]
582 )
584 lvals_toks: list[TokenStream] = []
585 if self.include_vars:
586 try:
587 # we likely want to fix stackdata at some point, but
588 # still need a workaround.
589 fibp = frame_info.variables_in_executing_piece
590 for var in fibp:
591 lvals_toks.append(
592 [
593 (Token, var.name),
594 (Token, " "),
595 (Token.ValEm, "= "),
596 (Token.ValEm, repr(var.value)),
597 ]
598 )
599 except Exception:
600 lvals_toks.append(
601 [
602 (
603 Token,
604 "Exception trying to inspect frame. No more locals available.",
605 ),
606 ]
607 )
609 if frame_info._sd is None:
610 # fast fallback if file is too long
611 assert frame_info.filename is not None
612 level_tokens = [
613 (Token.FilenameEm, util_path.compress_user(frame_info.filename)),
614 (Token, " "),
615 (Token, call),
616 (Token, "\n"),
617 ]
619 _line_format = Parser(theme_name=self._theme_name).format2
620 assert isinstance(frame_info.code, types.CodeType)
621 first_line: int = frame_info.code.co_firstlineno
622 current_line: int = frame_info.lineno
623 raw_lines: list[str] = frame_info.raw_lines
624 index: int = current_line - first_line
625 assert frame_info.context is not None
626 if index >= frame_info.context:
627 start = max(index - frame_info.context, 0)
628 stop = index + frame_info.context
629 index = frame_info.context
630 else:
631 start = 0
632 stop = index + frame_info.context
633 raw_lines = raw_lines[start:stop]
635 # Jan 2025: may need _line_format(py3ompat.cast_unicode(s))
636 raw_color_err = []
637 for s in raw_lines:
638 formatted, is_error = _line_format(s, "str")
639 assert formatted is not None, "format2 should return str when out='str'"
640 raw_color_err.append((s, (formatted, is_error)))
642 tb_tokens = _simple_format_traceback_lines(
643 current_line,
644 index,
645 raw_color_err,
646 lvals_toks,
647 theme=theme_table[self._theme_name],
648 )
649 _tb_lines: str = theme_table[self._theme_name].format(tb_tokens)
651 return theme_table[self._theme_name].format(level_tokens + tb_tokens)
652 else:
653 result = theme_table[self._theme_name].format(
654 _tokens_filename(True, frame_info.filename, lineno=frame_info.lineno)
655 )
656 result += ", " if call else ""
657 result += f"{call}\n"
658 result += theme_table[self._theme_name].format(
659 _format_traceback_lines(
660 frame_info.lines,
661 theme_table[self._theme_name],
662 self.has_colors,
663 lvals_toks,
664 )
665 )
666 return result
668 def prepare_header(self, etype: str, long_version: bool = False) -> str:
669 width = min(75, get_terminal_size()[0])
670 if long_version:
671 # Header with the exception type, python version, and date
672 pyver = "Python " + sys.version.split()[0] + ": " + sys.executable
673 date = time.ctime(time.time())
674 theme = theme_table[self._theme_name]
675 head = theme.format(
676 [
677 (Token.Topline, theme.symbols["top_line"] * width),
678 (Token, "\n"),
679 (Token.ExcName, etype),
680 (Token, " " * (width - len(etype) - len(pyver))),
681 (Token, pyver),
682 (Token, "\n"),
683 (Token, date.rjust(width)),
684 ]
685 )
686 head += (
687 "\nA problem occurred executing Python code. Here is the sequence of function"
688 "\ncalls leading up to the error, with the most recent (innermost) call last."
689 )
690 else:
691 # Simplified header
692 head = theme_table[self._theme_name].format(
693 [
694 (Token.ExcName, etype),
695 (
696 Token,
697 "Traceback (most recent call last)".rjust(width - len(etype)),
698 ),
699 ]
700 )
702 return head
704 def format_exception(self, etype, evalue):
705 # Get (safely) a string form of the exception info
706 try:
707 etype_str, evalue_str = map(str, (etype, evalue))
708 except:
709 # User exception is improperly defined.
710 etype, evalue = str, sys.exc_info()[:2]
711 etype_str, evalue_str = map(str, (etype, evalue))
713 # PEP-678 notes
714 notes = getattr(evalue, "__notes__", [])
715 if not isinstance(notes, Sequence) or isinstance(notes, (str, bytes)):
716 notes = [_safe_string(notes, "__notes__", func=repr)]
718 for note in notes:
719 assert isinstance(note, str)
721 str_notes: Sequence[str] = notes
723 # ... and format it
724 return [
725 theme_table[self._theme_name].format(
726 [(Token.ExcName, etype_str), (Token, ": "), (Token, evalue_str)]
727 ),
728 *(
729 theme_table[self._theme_name].format([(Token, note)])
730 for note in str_notes
731 ),
732 ]
734 def format_exception_as_a_whole(
735 self,
736 etype: type,
737 evalue: Optional[BaseException],
738 etb: Optional[TracebackType],
739 context: int,
740 tb_offset: Optional[int],
741 ) -> list[list[str]]:
742 """Formats the header, traceback and exception message for a single exception.
744 This may be called multiple times by Python 3 exception chaining
745 (PEP 3134).
746 """
747 # some locals
748 orig_etype = etype
749 try:
750 etype = etype.__name__ # type: ignore[assignment]
751 except AttributeError:
752 pass
754 tb_offset = self.tb_offset if tb_offset is None else tb_offset
755 assert isinstance(tb_offset, int)
756 head = self.prepare_header(str(etype), self.long_header)
757 records = self.get_records(etb, context, tb_offset) if etb else []
759 frames = []
760 skipped = 0
761 lastrecord = len(records) - 1
762 for i, record in enumerate(records):
763 if (
764 not isinstance(record._sd, stack_data.RepeatedFrames)
765 and self.skip_hidden
766 ):
767 if (
768 record.frame.f_locals.get("__tracebackhide__", 0)
769 and i != lastrecord
770 ):
771 skipped += 1
772 continue
773 if skipped:
774 frames.append(
775 theme_table[self._theme_name].format(
776 [
777 (Token, " "),
778 (Token.ExcName, "[... skipping hidden %s frame]" % skipped),
779 (Token, "\n"),
780 ]
781 )
782 )
783 skipped = 0
784 frames.append(self.format_record(record))
785 if skipped:
786 frames.append(
787 theme_table[self._theme_name].format(
788 [
789 (Token, " "),
790 (Token.ExcName, "[... skipping hidden %s frame]" % skipped),
791 (Token, "\n"),
792 ]
793 )
794 )
796 formatted_exception = self.format_exception(etype, evalue)
797 if records:
798 frame_info = records[-1]
799 ipinst = get_ipython()
800 if ipinst is not None:
801 ipinst.hooks.synchronize_with_editor(
802 frame_info.filename, frame_info.lineno, 0
803 )
805 return [[head] + frames + formatted_exception]
807 def get_records(self, etb: TracebackType, context: int, tb_offset: int) -> Any:
808 assert etb is not None
809 context = context - 1
810 after = context // 2
811 before = context - after
812 if self.has_colors:
813 base_style = theme_table[self._theme_name].as_pygments_style()
814 style = stack_data.style_with_executing_node(base_style, self.tb_highlight)
815 formatter = Terminal256Formatter(style=style)
816 else:
817 formatter = None
818 options = stack_data.Options(
819 before=before,
820 after=after,
821 pygments_formatter=formatter,
822 )
824 # Let's estimate the amount of code we will have to parse/highlight.
825 cf: Optional[TracebackType] = etb
826 max_len = 0
827 tbs = []
828 while cf is not None:
829 try:
830 mod = inspect.getmodule(cf.tb_frame)
831 if mod is not None:
832 mod_name = mod.__name__
833 root_name, *_ = mod_name.split(".")
834 if root_name == "IPython":
835 cf = cf.tb_next
836 continue
837 max_len = get_line_number_of_frame(cf.tb_frame)
839 except OSError:
840 max_len = 0
841 max_len = max(max_len, max_len)
842 tbs.append(cf)
843 cf = getattr(cf, "tb_next", None)
845 if max_len > FAST_THRESHOLD:
846 FIs: list[FrameInfo] = []
847 for tb in tbs:
848 frame = tb.tb_frame # type: ignore[union-attr]
849 lineno = frame.f_lineno
850 code = frame.f_code
851 filename = code.co_filename
852 # TODO: Here we need to use before/after/
853 FIs.append(
854 FrameInfo(
855 "Raw frame", filename, lineno, frame, code, context=context
856 )
857 )
858 return FIs
859 res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]
860 res2 = [FrameInfo._from_stack_data_FrameInfo(r) for r in res]
861 return res2
863 def structured_traceback(
864 self,
865 etype: type,
866 evalue: Optional[BaseException],
867 etb: Optional[TracebackType] = None,
868 tb_offset: Optional[int] = None,
869 context: int = 5,
870 ) -> list[str]:
871 """Return a nice text document describing the traceback."""
872 formatted_exceptions: list[list[str]] = self.format_exception_as_a_whole(
873 etype, evalue, etb, context, tb_offset
874 )
876 termsize = min(75, get_terminal_size()[0])
877 theme = theme_table[self._theme_name]
878 head: str = theme.format(
879 [
880 (
881 Token.Topline,
882 theme.symbols["top_line"] * termsize,
883 ),
884 ]
885 )
886 structured_traceback_parts: list[str] = [head]
887 chained_exceptions_tb_offset = 0
888 lines_of_context = 3
889 exception = self.get_parts_of_chained_exception(evalue)
890 if exception:
891 assert evalue is not None
892 formatted_exceptions += self.prepare_chained_exception_message(
893 evalue.__cause__
894 )
895 etype, evalue, etb = exception
896 else:
897 evalue = None
898 chained_exc_ids = set()
899 while evalue:
900 formatted_exceptions += self.format_exception_as_a_whole(
901 etype, evalue, etb, lines_of_context, chained_exceptions_tb_offset
902 )
903 exception = self.get_parts_of_chained_exception(evalue)
905 if exception and id(exception[1]) not in chained_exc_ids:
906 chained_exc_ids.add(
907 id(exception[1])
908 ) # trace exception to avoid infinite 'cause' loop
909 formatted_exceptions += self.prepare_chained_exception_message(
910 evalue.__cause__
911 )
912 etype, evalue, etb = exception
913 else:
914 evalue = None
916 # we want to see exceptions in a reversed order:
917 # the first exception should be on top
918 for fx in reversed(formatted_exceptions):
919 structured_traceback_parts += fx
921 return structured_traceback_parts
923 def debugger(self, force: bool = False) -> None:
924 """Call up the pdb debugger if desired, always clean up the tb
925 reference.
927 Keywords:
929 - force(False): by default, this routine checks the instance call_pdb
930 flag and does not actually invoke the debugger if the flag is false.
931 The 'force' option forces the debugger to activate even if the flag
932 is false.
934 If the call_pdb flag is set, the pdb interactive debugger is
935 invoked. In all cases, the self.tb reference to the current traceback
936 is deleted to prevent lingering references which hamper memory
937 management.
939 Note that each call to pdb() does an 'import readline', so if your app
940 requires a special setup for the readline completers, you'll have to
941 fix that by hand after invoking the exception handler."""
943 if force or self.call_pdb:
944 if self.pdb is None:
945 self.pdb = self.debugger_cls()
946 # the system displayhook may have changed, restore the original
947 # for pdb
948 display_trap = DisplayTrap(hook=sys.__displayhook__)
949 with display_trap:
950 self.pdb.reset()
951 # Find the right frame so we don't pop up inside ipython itself
952 if hasattr(self, "tb") and self.tb is not None: # type: ignore[has-type]
953 etb = self.tb # type: ignore[has-type]
954 else:
955 etb = self.tb = sys.last_traceback
956 while self.tb is not None and self.tb.tb_next is not None:
957 assert self.tb.tb_next is not None
958 self.tb = self.tb.tb_next
959 if etb and etb.tb_next:
960 etb = etb.tb_next
961 self.pdb.botframe = etb.tb_frame
962 # last_value should be deprecated, but last-exc sometimme not set
963 # please check why later and remove the getattr.
964 exc = (
965 sys.last_value
966 if sys.version_info < (3, 12)
967 else getattr(sys, "last_exc", sys.last_value)
968 ) # type: ignore[attr-defined]
969 if exc:
970 self.pdb.interaction(None, exc)
971 else:
972 self.pdb.interaction(None, etb)
974 if hasattr(self, "tb"):
975 del self.tb
977 def handler(self, info=None):
978 (etype, evalue, etb) = info or sys.exc_info()
979 self.tb = etb
980 ostream = self.ostream
981 ostream.flush()
982 ostream.write(self.text(etype, evalue, etb)) # type:ignore[arg-type]
983 ostream.write("\n")
984 ostream.flush()
986 # Changed so an instance can just be called as VerboseTB_inst() and print
987 # out the right info on its own.
988 def __call__(self, etype=None, evalue=None, etb=None):
989 """This hook can replace sys.excepthook (for Python 2.1 or higher)."""
990 if etb is None:
991 self.handler()
992 else:
993 self.handler((etype, evalue, etb))
994 try:
995 self.debugger()
996 except KeyboardInterrupt:
997 print("\nKeyboardInterrupt")
1000# ----------------------------------------------------------------------------
1001class FormattedTB(VerboseTB, ListTB):
1002 """Subclass ListTB but allow calling with a traceback.
1004 It can thus be used as a sys.excepthook for Python > 2.1.
1006 Also adds 'Context' and 'Verbose' modes, not available in ListTB.
1008 Allows a tb_offset to be specified. This is useful for situations where
1009 one needs to remove a number of topmost frames from the traceback (such as
1010 occurs with python programs that themselves execute other python code,
1011 like Python shells)."""
1013 mode: str
1015 def __init__(
1016 self,
1017 mode="Plain",
1018 # TODO: no default
1019 theme_name="linux",
1020 call_pdb=False,
1021 ostream=None,
1022 tb_offset=0,
1023 long_header=False,
1024 include_vars=False,
1025 check_cache=None,
1026 debugger_cls=None,
1027 ):
1028 # NEVER change the order of this list. Put new modes at the end:
1029 self.valid_modes = ["Plain", "Context", "Verbose", "Minimal", "Docs"]
1030 self.verbose_modes = self.valid_modes[1:3]
1032 VerboseTB.__init__(
1033 self,
1034 theme_name=theme_name,
1035 call_pdb=call_pdb,
1036 ostream=ostream,
1037 tb_offset=tb_offset,
1038 long_header=long_header,
1039 include_vars=include_vars,
1040 check_cache=check_cache,
1041 debugger_cls=debugger_cls,
1042 )
1044 # Different types of tracebacks are joined with different separators to
1045 # form a single string. They are taken from this dict
1046 self._join_chars = dict(
1047 Plain="", Context="\n", Verbose="\n", Minimal="", Docs=""
1048 )
1049 # set_mode also sets the tb_join_char attribute
1050 self.set_mode(mode)
1052 def structured_traceback(
1053 self,
1054 etype: type,
1055 evalue: BaseException | None,
1056 etb: TracebackType | None = None,
1057 tb_offset: int | None = None,
1058 context: int = 5,
1059 ) -> list[str]:
1060 tb_offset = self.tb_offset if tb_offset is None else tb_offset
1061 mode = self.mode
1062 if mode in self.verbose_modes:
1063 # Verbose modes need a full traceback
1064 return VerboseTB.structured_traceback(
1065 self, etype, evalue, etb, tb_offset, context
1066 )
1067 elif mode == "Docs":
1068 # return DocTB
1069 return DocTB(
1070 theme_name=self._theme_name,
1071 call_pdb=self.call_pdb,
1072 ostream=self.ostream,
1073 tb_offset=tb_offset,
1074 long_header=self.long_header,
1075 include_vars=self.include_vars,
1076 check_cache=self.check_cache,
1077 debugger_cls=self.debugger_cls,
1078 ).structured_traceback(
1079 etype, evalue, etb, tb_offset, 1
1080 ) # type: ignore[arg-type]
1082 elif mode == "Minimal":
1083 return ListTB.get_exception_only(self, etype, evalue)
1084 else:
1085 # We must check the source cache because otherwise we can print
1086 # out-of-date source code.
1087 self.check_cache()
1088 # Now we can extract and format the exception
1089 return ListTB.structured_traceback(
1090 self, etype, evalue, etb, tb_offset, context
1091 )
1093 def stb2text(self, stb: list[str]) -> str:
1094 """Convert a structured traceback (a list) to a string."""
1095 return self.tb_join_char.join(stb)
1097 def set_mode(self, mode: Optional[str] = None) -> None:
1098 """Switch to the desired mode.
1100 If mode is not specified, cycles through the available modes."""
1102 if not mode:
1103 new_idx = (self.valid_modes.index(self.mode) + 1) % len(self.valid_modes)
1104 self.mode = self.valid_modes[new_idx]
1105 elif mode not in self.valid_modes:
1106 raise ValueError(
1107 "Unrecognized mode in FormattedTB: <" + mode + ">\n"
1108 "Valid modes: " + str(self.valid_modes)
1109 )
1110 else:
1111 assert isinstance(mode, str)
1112 self.mode = mode
1113 # include variable details only in 'Verbose' mode
1114 self.include_vars = self.mode == self.valid_modes[2]
1115 # Set the join character for generating text tracebacks
1116 self.tb_join_char = self._join_chars[self.mode]
1118 # some convenient shortcuts
1119 def plain(self) -> None:
1120 self.set_mode(self.valid_modes[0])
1122 def context(self) -> None:
1123 self.set_mode(self.valid_modes[1])
1125 def verbose(self) -> None:
1126 self.set_mode(self.valid_modes[2])
1128 def minimal(self) -> None:
1129 self.set_mode(self.valid_modes[3])
1132# ----------------------------------------------------------------------------
1133class AutoFormattedTB(FormattedTB):
1134 """A traceback printer which can be called on the fly.
1136 It will find out about exceptions by itself.
1138 A brief example::
1140 AutoTB = AutoFormattedTB(mode = 'Verbose', theme_name='linux')
1141 try:
1142 ...
1143 except:
1144 AutoTB() # or AutoTB(out=logfile) where logfile is an open file object
1145 """
1147 def __call__(
1148 self,
1149 etype: type | None = None,
1150 evalue: BaseException | None = None,
1151 etb: TracebackType | None = None,
1152 out: Any = None,
1153 tb_offset: int | None = None,
1154 ) -> None:
1155 """Print out a formatted exception traceback.
1157 Optional arguments:
1158 - out: an open file-like object to direct output to.
1160 - tb_offset: the number of frames to skip over in the stack, on a
1161 per-call basis (this overrides temporarily the instance's tb_offset
1162 given at initialization time."""
1164 if out is None:
1165 out = self.ostream
1166 out.flush()
1167 out.write(self.text(etype, evalue, etb, tb_offset)) # type:ignore[arg-type]
1168 out.write("\n")
1169 out.flush()
1170 # FIXME: we should remove the auto pdb behavior from here and leave
1171 # that to the clients.
1172 try:
1173 self.debugger()
1174 except KeyboardInterrupt:
1175 print("\nKeyboardInterrupt")
1177 def structured_traceback(
1178 self,
1179 etype: type,
1180 evalue: Optional[BaseException],
1181 etb: Optional[TracebackType] = None,
1182 tb_offset: Optional[int] = None,
1183 context: int = 5,
1184 ) -> list[str]:
1185 # tb: TracebackType or tupleof tb types ?
1186 if etype is None:
1187 etype, evalue, etb = sys.exc_info()
1188 if isinstance(etb, tuple):
1189 # tb is a tuple if this is a chained exception.
1190 self.tb = etb[0]
1191 else:
1192 self.tb = etb
1193 return FormattedTB.structured_traceback(
1194 self, etype, evalue, etb, tb_offset, context
1195 )
1198# ---------------------------------------------------------------------------
1201# A simple class to preserve Nathan's original functionality.
1202class ColorTB(FormattedTB):
1203 """Deprecated since IPython 9.0."""
1205 def __init__(self, *args, **kwargs):
1206 warnings.warn(
1207 "Deprecated since IPython 9.0 use FormattedTB directly ColorTB is just an alias",
1208 DeprecationWarning,
1209 stacklevel=2,
1210 )
1212 super().__init__(*args, **kwargs)
1215class SyntaxTB(ListTB):
1216 """Extension which holds some state: the last exception value"""
1218 last_syntax_error: BaseException | None
1220 def __init__(self, *, theme_name):
1221 super().__init__(theme_name=theme_name)
1222 self.last_syntax_error = None
1224 def __call__(self, etype, value, elist):
1225 self.last_syntax_error = value
1227 super().__call__(etype, value, elist)
1229 def structured_traceback(
1230 self,
1231 etype: type,
1232 evalue: BaseException | None,
1233 etb: TracebackType | None = None,
1234 tb_offset: int | None = None,
1235 context: int = 5,
1236 ) -> list[str]:
1237 value = evalue
1238 # If the source file has been edited, the line in the syntax error can
1239 # be wrong (retrieved from an outdated cache). This replaces it with
1240 # the current value.
1241 if (
1242 isinstance(value, SyntaxError)
1243 and isinstance(value.filename, str)
1244 and isinstance(value.lineno, int)
1245 ):
1246 linecache.checkcache(value.filename)
1247 newtext = linecache.getline(value.filename, value.lineno)
1248 if newtext:
1249 value.text = newtext
1250 self.last_syntax_error = value
1251 return super(SyntaxTB, self).structured_traceback(
1252 etype, value, etb, tb_offset=tb_offset, context=context
1253 )
1255 def clear_err_state(self) -> Any | None:
1256 """Return the current error state and clear it"""
1257 e = self.last_syntax_error
1258 self.last_syntax_error = None
1259 return e
1261 def stb2text(self, stb: list[str]) -> str:
1262 """Convert a structured traceback (a list) to a string."""
1263 return "".join(stb)