Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/rich/traceback.py: 67%
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
1import inspect
2import linecache
3import os
4import sys
5from dataclasses import dataclass, field
6from itertools import islice
7from traceback import walk_tb
8from types import ModuleType, TracebackType
9from typing import (
10 Any,
11 Callable,
12 Dict,
13 Iterable,
14 List,
15 Optional,
16 Sequence,
17 Set,
18 Tuple,
19 Type,
20 Union,
21)
23from pygments.lexers import guess_lexer_for_filename
24from pygments.token import Comment, Keyword, Name, Number, Operator, String
25from pygments.token import Text as TextToken
26from pygments.token import Token
27from pygments.util import ClassNotFound
29from . import pretty
30from ._loop import loop_first_last, loop_last
31from .columns import Columns
32from .console import (
33 Console,
34 ConsoleOptions,
35 ConsoleRenderable,
36 OverflowMethod,
37 Group,
38 RenderResult,
39 group,
40)
41from .constrain import Constrain
42from .highlighter import RegexHighlighter, ReprHighlighter
43from .panel import Panel
44from .scope import render_scope
45from .style import Style
46from .syntax import Syntax, SyntaxPosition
47from .text import Text
48from .theme import Theme
50WINDOWS = sys.platform == "win32"
52LOCALS_MAX_LENGTH = 10
53LOCALS_MAX_STRING = 80
56def _iter_syntax_lines(
57 start: SyntaxPosition, end: SyntaxPosition
58) -> Iterable[Tuple[int, int, int]]:
59 """Yield start and end positions per line.
61 Args:
62 start: Start position.
63 end: End position.
65 Returns:
66 Iterable of (LINE, COLUMN1, COLUMN2).
67 """
69 line1, column1 = start
70 line2, column2 = end
72 if line1 == line2:
73 yield line1, column1, column2
74 else:
75 for first, last, line_no in loop_first_last(range(line1, line2 + 1)):
76 if first:
77 yield line_no, column1, -1
78 elif last:
79 yield line_no, 0, column2
80 else:
81 yield line_no, 0, -1
84def install(
85 *,
86 console: Optional[Console] = None,
87 width: Optional[int] = 100,
88 code_width: Optional[int] = 88,
89 extra_lines: int = 3,
90 theme: Optional[str] = None,
91 word_wrap: bool = False,
92 show_locals: bool = False,
93 locals_max_length: int = LOCALS_MAX_LENGTH,
94 locals_max_string: int = LOCALS_MAX_STRING,
95 locals_max_depth: Optional[int] = None,
96 locals_hide_dunder: bool = True,
97 locals_hide_sunder: Optional[bool] = None,
98 locals_overflow: Optional[OverflowMethod] = None,
99 indent_guides: bool = True,
100 suppress: Iterable[Union[str, ModuleType]] = (),
101 max_frames: int = 100,
102) -> Callable[[Type[BaseException], BaseException, Optional[TracebackType]], Any]:
103 """Install a rich traceback handler.
105 Once installed, any tracebacks will be printed with syntax highlighting and rich formatting.
108 Args:
109 console (Optional[Console], optional): Console to write exception to. Default uses internal Console instance.
110 width (Optional[int], optional): Width (in characters) of traceback. Defaults to 100.
111 code_width (Optional[int], optional): Code width (in characters) of traceback. Defaults to 88.
112 extra_lines (int, optional): Extra lines of code. Defaults to 3.
113 theme (Optional[str], optional): Pygments theme to use in traceback. Defaults to ``None`` which will pick
114 a theme appropriate for the platform.
115 word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.
116 show_locals (bool, optional): Enable display of local variables. Defaults to False.
117 locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
118 Defaults to 10.
119 locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
120 locals_max_depth (int, optional): Maximum depths of locals before truncating, or None to disable. Defaults to None.
121 locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
122 locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
123 locals_overflow (OverflowMethod, optional): How to handle overflowing locals, or None to disable. Defaults to None.
124 indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
125 suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
127 Returns:
128 Callable: The previous exception handler that was replaced.
130 """
131 traceback_console = Console(stderr=True) if console is None else console
133 locals_hide_sunder = (
134 True
135 if (traceback_console.is_jupyter and locals_hide_sunder is None)
136 else locals_hide_sunder
137 )
139 def excepthook(
140 type_: Type[BaseException],
141 value: BaseException,
142 traceback: Optional[TracebackType],
143 ) -> None:
144 exception_traceback = Traceback.from_exception(
145 type_,
146 value,
147 traceback,
148 width=width,
149 code_width=code_width,
150 extra_lines=extra_lines,
151 theme=theme,
152 word_wrap=word_wrap,
153 show_locals=show_locals,
154 locals_max_length=locals_max_length,
155 locals_max_string=locals_max_string,
156 locals_max_depth=locals_max_depth,
157 locals_hide_dunder=locals_hide_dunder,
158 locals_hide_sunder=bool(locals_hide_sunder),
159 locals_overflow=locals_overflow,
160 indent_guides=indent_guides,
161 suppress=suppress,
162 max_frames=max_frames,
163 )
164 traceback_console.print(exception_traceback)
166 def ipy_excepthook_closure(ip: Any) -> None: # pragma: no cover
167 tb_data = {} # store information about showtraceback call
168 default_showtraceback = ip.showtraceback # keep reference of default traceback
170 def ipy_show_traceback(*args: Any, **kwargs: Any) -> None:
171 """wrap the default ip.showtraceback to store info for ip._showtraceback"""
172 nonlocal tb_data
173 tb_data = kwargs
174 default_showtraceback(*args, **kwargs)
176 def ipy_display_traceback(
177 *args: Any, is_syntax: bool = False, **kwargs: Any
178 ) -> None:
179 """Internally called traceback from ip._showtraceback"""
180 nonlocal tb_data
181 exc_tuple = ip._get_exc_info()
183 # do not display trace on syntax error
184 tb: Optional[TracebackType] = None if is_syntax else exc_tuple[2]
186 # determine correct tb_offset
187 compiled = tb_data.get("running_compiled_code", False)
188 tb_offset = tb_data.get("tb_offset")
189 if tb_offset is None:
190 tb_offset = 1 if compiled else 0
191 # remove ipython internal frames from trace with tb_offset
192 for _ in range(tb_offset):
193 if tb is None:
194 break
195 tb = tb.tb_next
197 excepthook(exc_tuple[0], exc_tuple[1], tb)
198 tb_data = {} # clear data upon usage
200 # replace _showtraceback instead of showtraceback to allow ipython features such as debugging to work
201 # this is also what the ipython docs recommends to modify when subclassing InteractiveShell
202 ip._showtraceback = ipy_display_traceback
203 # add wrapper to capture tb_data
204 ip.showtraceback = ipy_show_traceback
205 ip.showsyntaxerror = lambda *args, **kwargs: ipy_display_traceback(
206 *args, is_syntax=True, **kwargs
207 )
209 try: # pragma: no cover
210 # if within ipython, use customized traceback
211 ip = get_ipython() # type: ignore[name-defined]
212 ipy_excepthook_closure(ip)
213 return sys.excepthook
214 except Exception:
215 # otherwise use default system hook
216 old_excepthook = sys.excepthook
217 sys.excepthook = excepthook
218 return old_excepthook
221@dataclass
222class Frame:
223 filename: str
224 lineno: int
225 name: str
226 line: str = ""
227 locals: Optional[Dict[str, pretty.Node]] = None
228 last_instruction: Optional[Tuple[Tuple[int, int], Tuple[int, int]]] = None
231@dataclass
232class _SyntaxError:
233 offset: int
234 filename: str
235 line: str
236 lineno: int
237 msg: str
238 notes: List[str] = field(default_factory=list)
241@dataclass
242class Stack:
243 exc_type: str
244 exc_value: str
245 syntax_error: Optional[_SyntaxError] = None
246 is_cause: bool = False
247 frames: List[Frame] = field(default_factory=list)
248 notes: List[str] = field(default_factory=list)
249 is_group: bool = False
250 exceptions: List["Trace"] = field(default_factory=list)
253@dataclass
254class Trace:
255 stacks: List[Stack]
258class PathHighlighter(RegexHighlighter):
259 highlights = [r"(?P<dim>.*/)(?P<bold>.+)"]
262class Traceback:
263 """A Console renderable that renders a traceback.
265 Args:
266 trace (Trace, optional): A `Trace` object produced from `extract`. Defaults to None, which uses
267 the last exception.
268 width (Optional[int], optional): Number of characters used to traceback. Defaults to 100.
269 code_width (Optional[int], optional): Number of code characters used to traceback. Defaults to 88.
270 extra_lines (int, optional): Additional lines of code to render. Defaults to 3.
271 theme (str, optional): Override pygments theme used in traceback.
272 word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.
273 show_locals (bool, optional): Enable display of local variables. Defaults to False.
274 indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
275 locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
276 Defaults to 10.
277 locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
278 locals_max_depth (int, optional): Maximum depths of locals before truncating, or None to disable. Defaults to None.
279 locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
280 locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
281 locals_overflow (OverflowMethod, optional): How to handle overflowing locals, or None to disable. Defaults to None.
282 suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
283 max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
285 """
287 LEXERS = {
288 "": "text",
289 ".py": "python",
290 ".pxd": "cython",
291 ".pyx": "cython",
292 ".pxi": "pyrex",
293 }
295 def __init__(
296 self,
297 trace: Optional[Trace] = None,
298 *,
299 width: Optional[int] = 100,
300 code_width: Optional[int] = 88,
301 extra_lines: int = 3,
302 theme: Optional[str] = None,
303 word_wrap: bool = False,
304 show_locals: bool = False,
305 locals_max_length: int = LOCALS_MAX_LENGTH,
306 locals_max_string: int = LOCALS_MAX_STRING,
307 locals_max_depth: Optional[int] = None,
308 locals_hide_dunder: bool = True,
309 locals_hide_sunder: bool = False,
310 locals_overlow: Optional[OverflowMethod] = None,
311 indent_guides: bool = True,
312 suppress: Iterable[Union[str, ModuleType]] = (),
313 max_frames: int = 100,
314 ):
315 if trace is None:
316 exc_type, exc_value, traceback = sys.exc_info()
317 if exc_type is None or exc_value is None or traceback is None:
318 raise ValueError(
319 "Value for 'trace' required if not called in except: block"
320 )
321 trace = self.extract(
322 exc_type, exc_value, traceback, show_locals=show_locals
323 )
324 self.trace = trace
325 self.width = width
326 self.code_width = code_width
327 self.extra_lines = extra_lines
328 self.theme = Syntax.get_theme(theme or "ansi_dark")
329 self.word_wrap = word_wrap
330 self.show_locals = show_locals
331 self.indent_guides = indent_guides
332 self.locals_max_length = locals_max_length
333 self.locals_max_string = locals_max_string
334 self.locals_max_depth = locals_max_depth
335 self.locals_hide_dunder = locals_hide_dunder
336 self.locals_hide_sunder = locals_hide_sunder
337 self.locals_overflow = locals_overlow
339 self.suppress: Sequence[str] = []
340 for suppress_entity in suppress:
341 if not isinstance(suppress_entity, str):
342 assert (
343 suppress_entity.__file__ is not None
344 ), f"{suppress_entity!r} must be a module with '__file__' attribute"
345 path = os.path.dirname(suppress_entity.__file__)
346 else:
347 path = suppress_entity
348 path = os.path.normpath(os.path.abspath(path))
349 self.suppress.append(path)
350 self.max_frames = max(4, max_frames) if max_frames > 0 else 0
352 @classmethod
353 def from_exception(
354 cls,
355 exc_type: Type[Any],
356 exc_value: BaseException,
357 traceback: Optional[TracebackType],
358 *,
359 width: Optional[int] = 100,
360 code_width: Optional[int] = 88,
361 extra_lines: int = 3,
362 theme: Optional[str] = None,
363 word_wrap: bool = False,
364 show_locals: bool = False,
365 locals_max_length: int = LOCALS_MAX_LENGTH,
366 locals_max_string: int = LOCALS_MAX_STRING,
367 locals_max_depth: Optional[int] = None,
368 locals_hide_dunder: bool = True,
369 locals_hide_sunder: bool = False,
370 locals_overflow: Optional[OverflowMethod] = None,
371 indent_guides: bool = True,
372 suppress: Iterable[Union[str, ModuleType]] = (),
373 max_frames: int = 100,
374 ) -> "Traceback":
375 """Create a traceback from exception info
377 Args:
378 exc_type (Type[BaseException]): Exception type.
379 exc_value (BaseException): Exception value.
380 traceback (TracebackType): Python Traceback object.
381 width (Optional[int], optional): Number of characters used to traceback. Defaults to 100.
382 code_width (Optional[int], optional): Number of code characters used to traceback. Defaults to 88.
383 extra_lines (int, optional): Additional lines of code to render. Defaults to 3.
384 theme (str, optional): Override pygments theme used in traceback.
385 word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.
386 show_locals (bool, optional): Enable display of local variables. Defaults to False.
387 indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
388 locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
389 Defaults to 10.
390 locals_max_depth (int, optional): Maximum depths of locals before truncating, or None to disable. Defaults to None.
391 locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
392 locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
393 locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
394 locals_overflow (OverflowMethod, optional): How to handle overflowing locals, or None to disable. Defaults to None.
395 suppress (Iterable[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
396 max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
398 Returns:
399 Traceback: A Traceback instance that may be printed.
400 """
401 rich_traceback = cls.extract(
402 exc_type,
403 exc_value,
404 traceback,
405 show_locals=show_locals,
406 locals_max_length=locals_max_length,
407 locals_max_string=locals_max_string,
408 locals_max_depth=locals_max_depth,
409 locals_hide_dunder=locals_hide_dunder,
410 locals_hide_sunder=locals_hide_sunder,
411 )
413 return cls(
414 rich_traceback,
415 width=width,
416 code_width=code_width,
417 extra_lines=extra_lines,
418 theme=theme,
419 word_wrap=word_wrap,
420 show_locals=show_locals,
421 indent_guides=indent_guides,
422 locals_max_length=locals_max_length,
423 locals_max_string=locals_max_string,
424 locals_max_depth=locals_max_depth,
425 locals_hide_dunder=locals_hide_dunder,
426 locals_hide_sunder=locals_hide_sunder,
427 locals_overlow=locals_overflow,
428 suppress=suppress,
429 max_frames=max_frames,
430 )
432 @classmethod
433 def extract(
434 cls,
435 exc_type: Type[BaseException],
436 exc_value: BaseException,
437 traceback: Optional[TracebackType],
438 *,
439 show_locals: bool = False,
440 locals_max_length: int = LOCALS_MAX_LENGTH,
441 locals_max_string: int = LOCALS_MAX_STRING,
442 locals_max_depth: Optional[int] = None,
443 locals_hide_dunder: bool = True,
444 locals_hide_sunder: bool = False,
445 _visited_exceptions: Optional[Set[BaseException]] = None,
446 ) -> Trace:
447 """Extract traceback information.
449 Args:
450 exc_type (Type[BaseException]): Exception type.
451 exc_value (BaseException): Exception value.
452 traceback (TracebackType): Python Traceback object.
453 show_locals (bool, optional): Enable display of local variables. Defaults to False.
454 locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
455 Defaults to 10.
456 locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
457 locals_max_depth (int, optional): Maximum depths of locals before truncating, or None to disable. Defaults to None.
458 locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
459 locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
461 Returns:
462 Trace: A Trace instance which you can use to construct a `Traceback`.
463 """
465 stacks: List[Stack] = []
466 is_cause = False
468 from rich import _IMPORT_CWD
470 notes: List[str] = getattr(exc_value, "__notes__", None) or []
472 grouped_exceptions: Set[BaseException] = (
473 set() if _visited_exceptions is None else _visited_exceptions
474 )
476 def safe_str(_object: Any) -> str:
477 """Don't allow exceptions from __str__ to propagate."""
478 try:
479 return str(_object)
480 except Exception:
481 return "<exception str() failed>"
483 while True:
484 stack = Stack(
485 exc_type=safe_str(exc_type.__name__),
486 exc_value=safe_str(exc_value),
487 is_cause=is_cause,
488 notes=notes,
489 )
491 if sys.version_info >= (3, 11):
492 if isinstance(exc_value, (BaseExceptionGroup, ExceptionGroup)):
493 stack.is_group = True
494 for exception in exc_value.exceptions:
495 if exception in grouped_exceptions:
496 continue
497 grouped_exceptions.add(exception)
498 stack.exceptions.append(
499 Traceback.extract(
500 type(exception),
501 exception,
502 exception.__traceback__,
503 show_locals=show_locals,
504 locals_max_length=locals_max_length,
505 locals_hide_dunder=locals_hide_dunder,
506 locals_hide_sunder=locals_hide_sunder,
507 _visited_exceptions=grouped_exceptions,
508 )
509 )
511 if isinstance(exc_value, SyntaxError):
512 stack.syntax_error = _SyntaxError(
513 offset=exc_value.offset or 0,
514 filename=exc_value.filename or "?",
515 lineno=exc_value.lineno or 0,
516 line=exc_value.text or "",
517 msg=exc_value.msg,
518 notes=notes,
519 )
521 stacks.append(stack)
522 append = stack.frames.append
524 def get_locals(
525 iter_locals: Iterable[Tuple[str, object]],
526 ) -> Iterable[Tuple[str, object]]:
527 """Extract locals from an iterator of key pairs."""
528 if not (locals_hide_dunder or locals_hide_sunder):
529 yield from iter_locals
530 return
531 for key, value in iter_locals:
532 if locals_hide_dunder and key.startswith("__"):
533 continue
534 if locals_hide_sunder and key.startswith("_"):
535 continue
536 yield key, value
538 for frame_summary, line_no in walk_tb(traceback):
539 filename = frame_summary.f_code.co_filename
541 last_instruction: Optional[Tuple[Tuple[int, int], Tuple[int, int]]]
542 last_instruction = None
543 if sys.version_info >= (3, 11):
544 instruction_index = frame_summary.f_lasti // 2
545 instruction_position = next(
546 islice(
547 frame_summary.f_code.co_positions(),
548 instruction_index,
549 instruction_index + 1,
550 )
551 )
552 (
553 start_line,
554 end_line,
555 start_column,
556 end_column,
557 ) = instruction_position
558 if (
559 start_line is not None
560 and end_line is not None
561 and start_column is not None
562 and end_column is not None
563 ):
564 last_instruction = (
565 (start_line, start_column),
566 (end_line, end_column),
567 )
569 if filename and not filename.startswith("<"):
570 if not os.path.isabs(filename):
571 filename = os.path.join(_IMPORT_CWD, filename)
572 if frame_summary.f_locals.get("_rich_traceback_omit", False):
573 continue
575 frame = Frame(
576 filename=filename or "?",
577 lineno=line_no,
578 name=frame_summary.f_code.co_name,
579 locals=(
580 {
581 key: pretty.traverse(
582 value,
583 max_length=locals_max_length,
584 max_string=locals_max_string,
585 max_depth=locals_max_depth,
586 )
587 for key, value in get_locals(frame_summary.f_locals.items())
588 if not (inspect.isfunction(value) or inspect.isclass(value))
589 }
590 if show_locals
591 else None
592 ),
593 last_instruction=last_instruction,
594 )
595 append(frame)
596 if frame_summary.f_locals.get("_rich_traceback_guard", False):
597 del stack.frames[:]
599 if not grouped_exceptions:
600 cause = getattr(exc_value, "__cause__", None)
601 if cause is not None and cause is not exc_value:
602 exc_type = cause.__class__
603 exc_value = cause
604 # __traceback__ can be None, e.g. for exceptions raised by the
605 # 'multiprocessing' module
606 traceback = cause.__traceback__
607 is_cause = True
608 continue
610 cause = exc_value.__context__
611 if cause is not None and not getattr(
612 exc_value, "__suppress_context__", False
613 ):
614 exc_type = cause.__class__
615 exc_value = cause
616 traceback = cause.__traceback__
617 is_cause = False
618 continue
619 # No cover, code is reached but coverage doesn't recognize it.
620 break # pragma: no cover
622 trace = Trace(stacks=stacks)
624 return trace
626 def __rich_console__(
627 self, console: Console, options: ConsoleOptions
628 ) -> RenderResult:
629 theme = self.theme
630 background_style = theme.get_background_style()
631 token_style = theme.get_style_for_token
633 traceback_theme = Theme(
634 {
635 "pretty": token_style(TextToken),
636 "pygments.text": token_style(Token),
637 "pygments.string": token_style(String),
638 "pygments.function": token_style(Name.Function),
639 "pygments.number": token_style(Number),
640 "repr.indent": token_style(Comment) + Style(dim=True),
641 "repr.str": token_style(String),
642 "repr.brace": token_style(TextToken) + Style(bold=True),
643 "repr.number": token_style(Number),
644 "repr.bool_true": token_style(Keyword.Constant),
645 "repr.bool_false": token_style(Keyword.Constant),
646 "repr.none": token_style(Keyword.Constant),
647 "scope.border": token_style(String.Delimiter),
648 "scope.equals": token_style(Operator),
649 "scope.key": token_style(Name),
650 "scope.key.special": token_style(Name.Constant) + Style(dim=True),
651 },
652 inherit=False,
653 )
655 highlighter = ReprHighlighter()
657 @group()
658 def render_stack(stack: Stack, last: bool) -> RenderResult:
659 if stack.frames:
660 stack_renderable: ConsoleRenderable = Panel(
661 self._render_stack(stack),
662 title="[traceback.title]Traceback [dim](most recent call last)",
663 style=background_style,
664 border_style="traceback.border",
665 expand=True,
666 padding=(0, 1),
667 )
668 stack_renderable = Constrain(stack_renderable, self.width)
669 with console.use_theme(traceback_theme):
670 yield stack_renderable
672 if stack.syntax_error is not None:
673 with console.use_theme(traceback_theme):
674 yield Constrain(
675 Panel(
676 self._render_syntax_error(stack.syntax_error),
677 style=background_style,
678 border_style="traceback.border.syntax_error",
679 expand=True,
680 padding=(0, 1),
681 width=self.width,
682 ),
683 self.width,
684 )
685 yield Text.assemble(
686 (f"{stack.exc_type}: ", "traceback.exc_type"),
687 highlighter(stack.syntax_error.msg),
688 )
689 elif stack.exc_value:
690 yield Text.assemble(
691 (f"{stack.exc_type}: ", "traceback.exc_type"),
692 highlighter(stack.exc_value),
693 )
694 else:
695 yield Text.assemble((f"{stack.exc_type}", "traceback.exc_type"))
697 for note in stack.notes:
698 yield Text.assemble(("[NOTE] ", "traceback.note"), highlighter(note))
700 if stack.is_group:
701 for group_no, group_exception in enumerate(stack.exceptions, 1):
702 grouped_exceptions: List[Group] = []
703 for group_last, group_stack in loop_last(group_exception.stacks):
704 grouped_exceptions.append(render_stack(group_stack, group_last))
705 yield ""
706 yield Constrain(
707 Panel(
708 Group(*grouped_exceptions),
709 title=f"Sub-exception #{group_no}",
710 border_style="traceback.group.border",
711 ),
712 self.width,
713 )
715 if not last:
716 if stack.is_cause:
717 yield Text.from_markup(
718 "\n[i]The above exception was the direct cause of the following exception:\n",
719 )
720 else:
721 yield Text.from_markup(
722 "\n[i]During handling of the above exception, another exception occurred:\n",
723 )
725 for last, stack in loop_last(reversed(self.trace.stacks)):
726 yield render_stack(stack, last)
728 @group()
729 def _render_syntax_error(self, syntax_error: _SyntaxError) -> RenderResult:
730 highlighter = ReprHighlighter()
731 path_highlighter = PathHighlighter()
732 if syntax_error.filename != "<stdin>":
733 if os.path.exists(syntax_error.filename):
734 text = Text.assemble(
735 (f" {syntax_error.filename}", "pygments.string"),
736 (":", "pygments.text"),
737 (str(syntax_error.lineno), "pygments.number"),
738 style="pygments.text",
739 )
740 yield path_highlighter(text)
741 syntax_error_text = highlighter(syntax_error.line.rstrip())
742 syntax_error_text.no_wrap = True
743 offset = min(syntax_error.offset - 1, len(syntax_error_text))
744 syntax_error_text.stylize("bold underline", offset, offset)
745 syntax_error_text += Text.from_markup(
746 "\n" + " " * offset + "[traceback.offset]▲[/]",
747 style="pygments.text",
748 )
749 yield syntax_error_text
751 @classmethod
752 def _guess_lexer(cls, filename: str, code: str) -> str:
753 ext = os.path.splitext(filename)[-1]
754 if not ext:
755 # No extension, look at first line to see if it is a hashbang
756 # Note, this is an educated guess and not a guarantee
757 # If it fails, the only downside is that the code is highlighted strangely
758 new_line_index = code.index("\n")
759 first_line = code[:new_line_index] if new_line_index != -1 else code
760 if first_line.startswith("#!") and "python" in first_line.lower():
761 return "python"
762 try:
763 return cls.LEXERS.get(ext) or guess_lexer_for_filename(filename, code).name
764 except ClassNotFound:
765 return "text"
767 @group()
768 def _render_stack(self, stack: Stack) -> RenderResult:
769 path_highlighter = PathHighlighter()
770 theme = self.theme
772 def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]:
773 if frame.locals:
774 yield render_scope(
775 frame.locals,
776 title="locals",
777 indent_guides=self.indent_guides,
778 max_length=self.locals_max_length,
779 max_string=self.locals_max_string,
780 max_depth=self.locals_max_depth,
781 overflow=self.locals_overflow,
782 )
784 exclude_frames: Optional[range] = None
785 if self.max_frames != 0:
786 exclude_frames = range(
787 self.max_frames // 2,
788 len(stack.frames) - self.max_frames // 2,
789 )
791 excluded = False
792 for frame_index, frame in enumerate(stack.frames):
793 if exclude_frames and frame_index in exclude_frames:
794 excluded = True
795 continue
797 if excluded:
798 assert exclude_frames is not None
799 yield Text(
800 f"\n... {len(exclude_frames)} frames hidden ...",
801 justify="center",
802 style="traceback.error",
803 )
804 excluded = False
806 first = frame_index == 0
807 frame_filename = frame.filename
808 suppressed = any(frame_filename.startswith(path) for path in self.suppress)
810 if os.path.exists(frame.filename):
811 text = Text.assemble(
812 path_highlighter(Text(frame.filename, style="pygments.string")),
813 (":", "pygments.text"),
814 (str(frame.lineno), "pygments.number"),
815 " in ",
816 (frame.name, "pygments.function"),
817 style="pygments.text",
818 )
819 else:
820 text = Text.assemble(
821 "in ",
822 (frame.name, "pygments.function"),
823 (":", "pygments.text"),
824 (str(frame.lineno), "pygments.number"),
825 style="pygments.text",
826 )
827 if not frame.filename.startswith("<") and not first:
828 yield ""
829 yield text
830 if frame.filename.startswith("<"):
831 yield from render_locals(frame)
832 continue
833 if not suppressed:
834 try:
835 code_lines = linecache.getlines(frame.filename)
836 code = "".join(code_lines)
837 if not code:
838 # code may be an empty string if the file doesn't exist, OR
839 # if the traceback filename is generated dynamically
840 continue
841 lexer_name = self._guess_lexer(frame.filename, code)
842 syntax = Syntax(
843 code,
844 lexer_name,
845 theme=theme,
846 line_numbers=True,
847 line_range=(
848 frame.lineno - self.extra_lines,
849 frame.lineno + self.extra_lines,
850 ),
851 highlight_lines={frame.lineno},
852 word_wrap=self.word_wrap,
853 code_width=self.code_width,
854 indent_guides=self.indent_guides,
855 dedent=False,
856 )
857 yield ""
858 except Exception as error:
859 yield Text.assemble(
860 (f"\n{error}", "traceback.error"),
861 )
862 else:
863 if frame.last_instruction is not None:
864 start, end = frame.last_instruction
866 # Stylize a line at a time
867 # So that indentation isn't underlined (which looks bad)
868 for line1, column1, column2 in _iter_syntax_lines(start, end):
869 try:
870 if column1 == 0:
871 line = code_lines[line1 - 1]
872 column1 = len(line) - len(line.lstrip())
873 if column2 == -1:
874 column2 = len(code_lines[line1 - 1])
875 except IndexError:
876 # Being defensive here
877 # If last_instruction reports a line out-of-bounds, we don't want to crash
878 continue
880 syntax.stylize_range(
881 style="traceback.error_range",
882 start=(line1, column1),
883 end=(line1, column2),
884 )
885 yield (
886 Columns(
887 [
888 syntax,
889 *render_locals(frame),
890 ],
891 padding=1,
892 )
893 if frame.locals
894 else syntax
895 )
898if __name__ == "__main__": # pragma: no cover
899 install(show_locals=True)
900 import sys
902 def bar(
903 a: Any,
904 ) -> None: # 这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑
905 one = 1
906 print(one / a)
908 def foo(a: Any) -> None:
909 _rich_traceback_guard = True
910 zed = {
911 "characters": {
912 "Paul Atreides",
913 "Vladimir Harkonnen",
914 "Thufir Hawat",
915 "Duncan Idaho",
916 },
917 "atomic_types": (None, False, True),
918 }
919 bar(a)
921 def error() -> None:
922 foo(0)
924 error()