codekingpro/portable-devtools
115k
1import inspect2import linecache3import os4import sys5from dataclasses import dataclass, field6from itertools import islice7from traceback import walk_tb8from types import ModuleType, TracebackType9from 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)22 23from pygments.lexers import guess_lexer_for_filename24from pygments.token import Comment, Keyword, Name, Number, Operator, String25from pygments.token import Text as TextToken26from pygments.token import Token27from pygments.util import ClassNotFound28 29from . import pretty30from ._loop import loop_first_last, loop_last31from .columns import Columns32from .console import (33 Console,34 ConsoleOptions,35 ConsoleRenderable,36 OverflowMethod,37 Group,38 RenderResult,39 group,40)41from .constrain import Constrain42from .highlighter import RegexHighlighter, ReprHighlighter43from .panel import Panel44from .scope import render_scope45from .style import Style46from .syntax import Syntax, SyntaxPosition47from .text import Text48from .theme import Theme49 50WINDOWS = sys.platform == "win32"51 52LOCALS_MAX_LENGTH = 1053LOCALS_MAX_STRING = 8054 55 56def _iter_syntax_lines(57 start: SyntaxPosition, end: SyntaxPosition58) -> Iterable[Tuple[int, int, int]]:59 """Yield start and end positions per line.60 61 Args:62 start: Start position.63 end: End position.64 65 Returns:66 Iterable of (LINE, COLUMN1, COLUMN2).67 """68 69 line1, column1 = start70 line2, column2 = end71 72 if line1 == line2:73 yield line1, column1, column274 else:75 for first, last, line_no in loop_first_last(range(line1, line2 + 1)):76 if first:77 yield line_no, column1, -178 elif last:79 yield line_no, 0, column280 else:81 yield line_no, 0, -182 83 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.104 105 Once installed, any tracebacks will be printed with syntax highlighting and rich formatting.106 107 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 pick114 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.126 127 Returns:128 Callable: The previous exception handler that was replaced.129 130 """131 traceback_console = Console(stderr=True) if console is None else console132 133 locals_hide_sunder = (134 True135 if (traceback_console.is_jupyter and locals_hide_sunder is None)136 else locals_hide_sunder137 )138 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)165 166 def ipy_excepthook_closure(ip: Any) -> None: # pragma: no cover167 tb_data = {} # store information about showtraceback call168 default_showtraceback = ip.showtraceback # keep reference of default traceback169 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_data173 tb_data = kwargs174 default_showtraceback(*args, **kwargs)175 176 def ipy_display_traceback(177 *args: Any, is_syntax: bool = False, **kwargs: Any178 ) -> None:179 """Internally called traceback from ip._showtraceback"""180 nonlocal tb_data181 exc_tuple = ip._get_exc_info()182 183 # do not display trace on syntax error184 tb: Optional[TracebackType] = None if is_syntax else exc_tuple[2]185 186 # determine correct tb_offset187 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 0191 # remove ipython internal frames from trace with tb_offset192 for _ in range(tb_offset):193 if tb is None:194 break195 tb = tb.tb_next196 197 excepthook(exc_tuple[0], exc_tuple[1], tb)198 tb_data = {} # clear data upon usage199 200 # replace _showtraceback instead of showtraceback to allow ipython features such as debugging to work201 # this is also what the ipython docs recommends to modify when subclassing InteractiveShell202 ip._showtraceback = ipy_display_traceback203 # add wrapper to capture tb_data204 ip.showtraceback = ipy_show_traceback205 ip.showsyntaxerror = lambda *args, **kwargs: ipy_display_traceback(206 *args, is_syntax=True, **kwargs207 )208 209 try: # pragma: no cover210 # if within ipython, use customized traceback211 ip = get_ipython() # type: ignore[name-defined]212 ipy_excepthook_closure(ip)213 return sys.excepthook214 except Exception:215 # otherwise use default system hook216 old_excepthook = sys.excepthook217 sys.excepthook = excepthook218 return old_excepthook219 220 221@dataclass222class Frame:223 filename: str224 lineno: int225 name: str226 line: str = ""227 locals: Optional[Dict[str, pretty.Node]] = None228 last_instruction: Optional[Tuple[Tuple[int, int], Tuple[int, int]]] = None229 230 231@dataclass232class _SyntaxError:233 offset: int234 filename: str235 line: str236 lineno: int237 msg: str238 notes: List[str] = field(default_factory=list)239 240 241@dataclass242class Stack:243 exc_type: str244 exc_value: str245 syntax_error: Optional[_SyntaxError] = None246 is_cause: bool = False247 frames: List[Frame] = field(default_factory=list)248 notes: List[str] = field(default_factory=list)249 is_group: bool = False250 exceptions: List["Trace"] = field(default_factory=list)251 252 253@dataclass254class Trace:255 stacks: List[Stack]256 257 258class PathHighlighter(RegexHighlighter):259 highlights = [r"(?P<dim>.*/)(?P<bold>.+)"]260 261 262class Traceback:263 """A Console renderable that renders a traceback.264 265 Args:266 trace (Trace, optional): A `Trace` object produced from `extract`. Defaults to None, which uses267 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.284 285 """286 287 LEXERS = {288 "": "text",289 ".py": "python",290 ".pxd": "cython",291 ".pyx": "cython",292 ".pxi": "pyrex",293 }294 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_locals323 )324 self.trace = trace325 self.width = width326 self.code_width = code_width327 self.extra_lines = extra_lines328 self.theme = Syntax.get_theme(theme or "ansi_dark")329 self.word_wrap = word_wrap330 self.show_locals = show_locals331 self.indent_guides = indent_guides332 self.locals_max_length = locals_max_length333 self.locals_max_string = locals_max_string334 self.locals_max_depth = locals_max_depth335 self.locals_hide_dunder = locals_hide_dunder336 self.locals_hide_sunder = locals_hide_sunder337 self.locals_overflow = locals_overlow338 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 None344 ), f"{suppress_entity!r} must be a module with '__file__' attribute"345 path = os.path.dirname(suppress_entity.__file__)346 else:347 path = suppress_entity348 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 0351 352 @classmethod353 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 info376 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.397 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 )412 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 )431 432 @classmethod433 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.448 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.460 461 Returns:462 Trace: A Trace instance which you can use to construct a `Traceback`.463 """464 465 stacks: List[Stack] = []466 is_cause = False467 468 from rich import _IMPORT_CWD469 470 notes: List[str] = getattr(exc_value, "__notes__", None) or []471 472 grouped_exceptions: Set[BaseException] = (473 set() if _visited_exceptions is None else _visited_exceptions474 )475 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>"482 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 )490 491 if sys.version_info >= (3, 11):492 if isinstance(exc_value, (BaseExceptionGroup, ExceptionGroup)):493 stack.is_group = True494 for exception in exc_value.exceptions:495 if exception in grouped_exceptions:496 continue497 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 )510 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 )520 521 stacks.append(stack)522 append = stack.frames.append523 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_locals530 return531 for key, value in iter_locals:532 if locals_hide_dunder and key.startswith("__"):533 continue534 if locals_hide_sunder and key.startswith("_"):535 continue536 yield key, value537 538 for frame_summary, line_no in walk_tb(traceback):539 filename = frame_summary.f_code.co_filename540 541 last_instruction: Optional[Tuple[Tuple[int, int], Tuple[int, int]]]542 last_instruction = None543 if sys.version_info >= (3, 11):544 instruction_index = frame_summary.f_lasti // 2545 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_position558 if (559 start_line is not None560 and end_line is not None561 and start_column is not None562 and end_column is not None563 ):564 last_instruction = (565 (start_line, start_column),566 (end_line, end_column),567 )568 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 continue574 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_locals591 else None592 ),593 last_instruction=last_instruction,594 )595 append(frame)596 if frame_summary.f_locals.get("_rich_traceback_guard", False):597 del stack.frames[:]598 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 = cause604 # __traceback__ can be None, e.g. for exceptions raised by the605 # 'multiprocessing' module606 traceback = cause.__traceback__607 is_cause = True608 continue609 610 cause = exc_value.__context__611 if cause is not None and not getattr(612 exc_value, "__suppress_context__", False613 ):614 exc_type = cause.__class__615 exc_value = cause616 traceback = cause.__traceback__617 is_cause = False618 continue619 # No cover, code is reached but coverage doesn't recognize it.620 break # pragma: no cover621 622 trace = Trace(stacks=stacks)623 624 return trace625 626 def __rich_console__(627 self, console: Console, options: ConsoleOptions628 ) -> RenderResult:629 theme = self.theme630 background_style = theme.get_background_style()631 token_style = theme.get_style_for_token632 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 )654 655 highlighter = ReprHighlighter()656 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_renderable671 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"))696 697 for note in stack.notes:698 yield Text.assemble(("[NOTE] ", "traceback.note"), highlighter(note))699 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 )714 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 )724 725 for last, stack in loop_last(reversed(self.trace.stacks)):726 yield render_stack(stack, last)727 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 = True743 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_text750 751 @classmethod752 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 hashbang756 # Note, this is an educated guess and not a guarantee757 # If it fails, the only downside is that the code is highlighted strangely758 new_line_index = code.index("\n")759 first_line = code[:new_line_index] if new_line_index != -1 else code760 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).name764 except ClassNotFound:765 return "text"766 767 @group()768 def _render_stack(self, stack: Stack) -> RenderResult:769 path_highlighter = PathHighlighter()770 theme = self.theme771 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 )783 784 exclude_frames: Optional[range] = None785 if self.max_frames != 0:786 exclude_frames = range(787 self.max_frames // 2,788 len(stack.frames) - self.max_frames // 2,789 )790 791 excluded = False792 for frame_index, frame in enumerate(stack.frames):793 if exclude_frames and frame_index in exclude_frames:794 excluded = True795 continue796 797 if excluded:798 assert exclude_frames is not None799 yield Text(800 f"\n... {len(exclude_frames)} frames hidden ...",801 justify="center",802 style="traceback.error",803 )804 excluded = False805 806 first = frame_index == 0807 frame_filename = frame.filename808 suppressed = any(frame_filename.startswith(path) for path in self.suppress)809 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 text830 if frame.filename.startswith("<"):831 yield from render_locals(frame)832 continue833 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, OR839 # if the traceback filename is generated dynamically840 continue841 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_instruction865 866 # Stylize a line at a time867 # 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 here877 # If last_instruction reports a line out-of-bounds, we don't want to crash878 continue879 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.locals894 else syntax895 )896 897 898if __name__ == "__main__": # pragma: no cover899 install(show_locals=True)900 import sys901 902 def bar(903 a: Any,904 ) -> None: # 这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑905 one = 1906 print(one / a)907 908 def foo(a: Any) -> None:909 _rich_traceback_guard = True910 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)920 921 def error() -> None:922 foo(0)923 924 error()925 