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