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