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