python (3.11.7)
       1  from __future__ import absolute_import
       2  
       3  import linecache
       4  import os
       5  import platform
       6  import sys
       7  from dataclasses import dataclass, field
       8  from traceback import walk_tb
       9  from types import ModuleType, TracebackType
      10  from typing import (
      11      Any,
      12      Callable,
      13      Dict,
      14      Iterable,
      15      List,
      16      Optional,
      17      Sequence,
      18      Tuple,
      19      Type,
      20      Union,
      21  )
      22  
      23  from pip._vendor.pygments.lexers import guess_lexer_for_filename
      24  from pip._vendor.pygments.token import Comment, Keyword, Name, Number, Operator, String
      25  from pip._vendor.pygments.token import Text as TextToken
      26  from pip._vendor.pygments.token import Token
      27  from pip._vendor.pygments.util import ClassNotFound
      28  
      29  from . import pretty
      30  from ._loop import loop_last
      31  from .columns import Columns
      32  from .console import Console, ConsoleOptions, ConsoleRenderable, RenderResult, group
      33  from .constrain import Constrain
      34  from .highlighter import RegexHighlighter, ReprHighlighter
      35  from .panel import Panel
      36  from .scope import render_scope
      37  from .style import Style
      38  from .syntax import Syntax
      39  from .text import Text
      40  from .theme import Theme
      41  
      42  WINDOWS = platform.system() == "Windows"
      43  
      44  LOCALS_MAX_LENGTH = 10
      45  LOCALS_MAX_STRING = 80
      46  
      47  
      48  def install(
      49      *,
      50      console: Optional[Console] = None,
      51      width: Optional[int] = 100,
      52      extra_lines: int = 3,
      53      theme: Optional[str] = None,
      54      word_wrap: bool = False,
      55      show_locals: bool = False,
      56      locals_max_length: int = LOCALS_MAX_LENGTH,
      57      locals_max_string: int = LOCALS_MAX_STRING,
      58      locals_hide_dunder: bool = True,
      59      locals_hide_sunder: Optional[bool] = None,
      60      indent_guides: bool = True,
      61      suppress: Iterable[Union[str, ModuleType]] = (),
      62      max_frames: int = 100,
      63  ) -> Callable[[Type[BaseException], BaseException, Optional[TracebackType]], Any]:
      64      """Install a rich traceback handler.
      65  
      66      Once installed, any tracebacks will be printed with syntax highlighting and rich formatting.
      67  
      68  
      69      Args:
      70          console (Optional[Console], optional): Console to write exception to. Default uses internal Console instance.
      71          width (Optional[int], optional): Width (in characters) of traceback. Defaults to 100.
      72          extra_lines (int, optional): Extra lines of code. Defaults to 3.
      73          theme (Optional[str], optional): Pygments theme to use in traceback. Defaults to ``None`` which will pick
      74              a theme appropriate for the platform.
      75          word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.
      76          show_locals (bool, optional): Enable display of local variables. Defaults to False.
      77          locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
      78              Defaults to 10.
      79          locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
      80          locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
      81          locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
      82          indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
      83          suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
      84  
      85      Returns:
      86          Callable: The previous exception handler that was replaced.
      87  
      88      """
      89      traceback_console = Console(stderr=True) if console is None else console
      90  
      91      locals_hide_sunder = (
      92          True
      93          if (traceback_console.is_jupyter and locals_hide_sunder is None)
      94          else locals_hide_sunder
      95      )
      96  
      97      def excepthook(
      98          type_: Type[BaseException],
      99          value: BaseException,
     100          traceback: Optional[TracebackType],
     101      ) -> None:
     102          traceback_console.print(
     103              Traceback.from_exception(
     104                  type_,
     105                  value,
     106                  traceback,
     107                  width=width,
     108                  extra_lines=extra_lines,
     109                  theme=theme,
     110                  word_wrap=word_wrap,
     111                  show_locals=show_locals,
     112                  locals_max_length=locals_max_length,
     113                  locals_max_string=locals_max_string,
     114                  locals_hide_dunder=locals_hide_dunder,
     115                  locals_hide_sunder=bool(locals_hide_sunder),
     116                  indent_guides=indent_guides,
     117                  suppress=suppress,
     118                  max_frames=max_frames,
     119              )
     120          )
     121  
     122      def ipy_excepthook_closure(ip: Any) -> None:  # pragma: no cover
     123          tb_data = {}  # store information about showtraceback call
     124          default_showtraceback = ip.showtraceback  # keep reference of default traceback
     125  
     126          def ipy_show_traceback(*args: Any, **kwargs: Any) -> None:
     127              """wrap the default ip.showtraceback to store info for ip._showtraceback"""
     128              nonlocal tb_data
     129              tb_data = kwargs
     130              default_showtraceback(*args, **kwargs)
     131  
     132          def ipy_display_traceback(
     133              *args: Any, is_syntax: bool = False, **kwargs: Any
     134          ) -> None:
     135              """Internally called traceback from ip._showtraceback"""
     136              nonlocal tb_data
     137              exc_tuple = ip._get_exc_info()
     138  
     139              # do not display trace on syntax error
     140              tb: Optional[TracebackType] = None if is_syntax else exc_tuple[2]
     141  
     142              # determine correct tb_offset
     143              compiled = tb_data.get("running_compiled_code", False)
     144              tb_offset = tb_data.get("tb_offset", 1 if compiled else 0)
     145              # remove ipython internal frames from trace with tb_offset
     146              for _ in range(tb_offset):
     147                  if tb is None:
     148                      break
     149                  tb = tb.tb_next
     150  
     151              excepthook(exc_tuple[0], exc_tuple[1], tb)
     152              tb_data = {}  # clear data upon usage
     153  
     154          # replace _showtraceback instead of showtraceback to allow ipython features such as debugging to work
     155          # this is also what the ipython docs recommends to modify when subclassing InteractiveShell
     156          ip._showtraceback = ipy_display_traceback
     157          # add wrapper to capture tb_data
     158          ip.showtraceback = ipy_show_traceback
     159          ip.showsyntaxerror = lambda *args, **kwargs: ipy_display_traceback(
     160              *args, is_syntax=True, **kwargs
     161          )
     162  
     163      try:  # pragma: no cover
     164          # if within ipython, use customized traceback
     165          ip = get_ipython()  # type: ignore[name-defined]
     166          ipy_excepthook_closure(ip)
     167          return sys.excepthook
     168      except Exception:
     169          # otherwise use default system hook
     170          old_excepthook = sys.excepthook
     171          sys.excepthook = excepthook
     172          return old_excepthook
     173  
     174  
     175  @dataclass
     176  class ESC[4;38;5;81mFrame:
     177      filename: str
     178      lineno: int
     179      name: str
     180      line: str = ""
     181      locals: Optional[Dict[str, pretty.Node]] = None
     182  
     183  
     184  @dataclass
     185  class ESC[4;38;5;81m_SyntaxError:
     186      offset: int
     187      filename: str
     188      line: str
     189      lineno: int
     190      msg: str
     191  
     192  
     193  @dataclass
     194  class ESC[4;38;5;81mStack:
     195      exc_type: str
     196      exc_value: str
     197      syntax_error: Optional[_SyntaxError] = None
     198      is_cause: bool = False
     199      frames: List[Frame] = field(default_factory=list)
     200  
     201  
     202  @dataclass
     203  class ESC[4;38;5;81mTrace:
     204      stacks: List[Stack]
     205  
     206  
     207  class ESC[4;38;5;81mPathHighlighter(ESC[4;38;5;149mRegexHighlighter):
     208      highlights = [r"(?P<dim>.*/)(?P<bold>.+)"]
     209  
     210  
     211  class ESC[4;38;5;81mTraceback:
     212      """A Console renderable that renders a traceback.
     213  
     214      Args:
     215          trace (Trace, optional): A `Trace` object produced from `extract`. Defaults to None, which uses
     216              the last exception.
     217          width (Optional[int], optional): Number of characters used to traceback. Defaults to 100.
     218          extra_lines (int, optional): Additional lines of code to render. Defaults to 3.
     219          theme (str, optional): Override pygments theme used in traceback.
     220          word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.
     221          show_locals (bool, optional): Enable display of local variables. Defaults to False.
     222          indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
     223          locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
     224              Defaults to 10.
     225          locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
     226          locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
     227          locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
     228          suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
     229          max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
     230  
     231      """
     232  
     233      LEXERS = {
     234          "": "text",
     235          ".py": "python",
     236          ".pxd": "cython",
     237          ".pyx": "cython",
     238          ".pxi": "pyrex",
     239      }
     240  
     241      def __init__(
     242          self,
     243          trace: Optional[Trace] = None,
     244          *,
     245          width: Optional[int] = 100,
     246          extra_lines: int = 3,
     247          theme: Optional[str] = None,
     248          word_wrap: bool = False,
     249          show_locals: bool = False,
     250          locals_max_length: int = LOCALS_MAX_LENGTH,
     251          locals_max_string: int = LOCALS_MAX_STRING,
     252          locals_hide_dunder: bool = True,
     253          locals_hide_sunder: bool = False,
     254          indent_guides: bool = True,
     255          suppress: Iterable[Union[str, ModuleType]] = (),
     256          max_frames: int = 100,
     257      ):
     258          if trace is None:
     259              exc_type, exc_value, traceback = sys.exc_info()
     260              if exc_type is None or exc_value is None or traceback is None:
     261                  raise ValueError(
     262                      "Value for 'trace' required if not called in except: block"
     263                  )
     264              trace = self.extract(
     265                  exc_type, exc_value, traceback, show_locals=show_locals
     266              )
     267          self.trace = trace
     268          self.width = width
     269          self.extra_lines = extra_lines
     270          self.theme = Syntax.get_theme(theme or "ansi_dark")
     271          self.word_wrap = word_wrap
     272          self.show_locals = show_locals
     273          self.indent_guides = indent_guides
     274          self.locals_max_length = locals_max_length
     275          self.locals_max_string = locals_max_string
     276          self.locals_hide_dunder = locals_hide_dunder
     277          self.locals_hide_sunder = locals_hide_sunder
     278  
     279          self.suppress: Sequence[str] = []
     280          for suppress_entity in suppress:
     281              if not isinstance(suppress_entity, str):
     282                  assert (
     283                      suppress_entity.__file__ is not None
     284                  ), f"{suppress_entity!r} must be a module with '__file__' attribute"
     285                  path = os.path.dirname(suppress_entity.__file__)
     286              else:
     287                  path = suppress_entity
     288              path = os.path.normpath(os.path.abspath(path))
     289              self.suppress.append(path)
     290          self.max_frames = max(4, max_frames) if max_frames > 0 else 0
     291  
     292      @classmethod
     293      def from_exception(
     294          cls,
     295          exc_type: Type[Any],
     296          exc_value: BaseException,
     297          traceback: Optional[TracebackType],
     298          *,
     299          width: Optional[int] = 100,
     300          extra_lines: int = 3,
     301          theme: Optional[str] = None,
     302          word_wrap: bool = False,
     303          show_locals: bool = False,
     304          locals_max_length: int = LOCALS_MAX_LENGTH,
     305          locals_max_string: int = LOCALS_MAX_STRING,
     306          locals_hide_dunder: bool = True,
     307          locals_hide_sunder: bool = False,
     308          indent_guides: bool = True,
     309          suppress: Iterable[Union[str, ModuleType]] = (),
     310          max_frames: int = 100,
     311      ) -> "Traceback":
     312          """Create a traceback from exception info
     313  
     314          Args:
     315              exc_type (Type[BaseException]): Exception type.
     316              exc_value (BaseException): Exception value.
     317              traceback (TracebackType): Python Traceback object.
     318              width (Optional[int], optional): Number of characters used to traceback. Defaults to 100.
     319              extra_lines (int, optional): Additional lines of code to render. Defaults to 3.
     320              theme (str, optional): Override pygments theme used in traceback.
     321              word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.
     322              show_locals (bool, optional): Enable display of local variables. Defaults to False.
     323              indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
     324              locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
     325                  Defaults to 10.
     326              locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
     327              locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
     328              locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
     329              suppress (Iterable[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
     330              max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
     331  
     332          Returns:
     333              Traceback: A Traceback instance that may be printed.
     334          """
     335          rich_traceback = cls.extract(
     336              exc_type,
     337              exc_value,
     338              traceback,
     339              show_locals=show_locals,
     340              locals_max_length=locals_max_length,
     341              locals_max_string=locals_max_string,
     342              locals_hide_dunder=locals_hide_dunder,
     343              locals_hide_sunder=locals_hide_sunder,
     344          )
     345  
     346          return cls(
     347              rich_traceback,
     348              width=width,
     349              extra_lines=extra_lines,
     350              theme=theme,
     351              word_wrap=word_wrap,
     352              show_locals=show_locals,
     353              indent_guides=indent_guides,
     354              locals_max_length=locals_max_length,
     355              locals_max_string=locals_max_string,
     356              locals_hide_dunder=locals_hide_dunder,
     357              locals_hide_sunder=locals_hide_sunder,
     358              suppress=suppress,
     359              max_frames=max_frames,
     360          )
     361  
     362      @classmethod
     363      def extract(
     364          cls,
     365          exc_type: Type[BaseException],
     366          exc_value: BaseException,
     367          traceback: Optional[TracebackType],
     368          *,
     369          show_locals: bool = False,
     370          locals_max_length: int = LOCALS_MAX_LENGTH,
     371          locals_max_string: int = LOCALS_MAX_STRING,
     372          locals_hide_dunder: bool = True,
     373          locals_hide_sunder: bool = False,
     374      ) -> Trace:
     375          """Extract traceback information.
     376  
     377          Args:
     378              exc_type (Type[BaseException]): Exception type.
     379              exc_value (BaseException): Exception value.
     380              traceback (TracebackType): Python Traceback object.
     381              show_locals (bool, optional): Enable display of local variables. Defaults to False.
     382              locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
     383                  Defaults to 10.
     384              locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
     385              locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
     386              locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
     387  
     388          Returns:
     389              Trace: A Trace instance which you can use to construct a `Traceback`.
     390          """
     391  
     392          stacks: List[Stack] = []
     393          is_cause = False
     394  
     395          from pip._vendor.rich import _IMPORT_CWD
     396  
     397          def safe_str(_object: Any) -> str:
     398              """Don't allow exceptions from __str__ to propagate."""
     399              try:
     400                  return str(_object)
     401              except Exception:
     402                  return "<exception str() failed>"
     403  
     404          while True:
     405              stack = Stack(
     406                  exc_type=safe_str(exc_type.__name__),
     407                  exc_value=safe_str(exc_value),
     408                  is_cause=is_cause,
     409              )
     410  
     411              if isinstance(exc_value, SyntaxError):
     412                  stack.syntax_error = _SyntaxError(
     413                      offset=exc_value.offset or 0,
     414                      filename=exc_value.filename or "?",
     415                      lineno=exc_value.lineno or 0,
     416                      line=exc_value.text or "",
     417                      msg=exc_value.msg,
     418                  )
     419  
     420              stacks.append(stack)
     421              append = stack.frames.append
     422  
     423              def get_locals(
     424                  iter_locals: Iterable[Tuple[str, object]]
     425              ) -> Iterable[Tuple[str, object]]:
     426                  """Extract locals from an iterator of key pairs."""
     427                  if not (locals_hide_dunder or locals_hide_sunder):
     428                      yield from iter_locals
     429                      return
     430                  for key, value in iter_locals:
     431                      if locals_hide_dunder and key.startswith("__"):
     432                          continue
     433                      if locals_hide_sunder and key.startswith("_"):
     434                          continue
     435                      yield key, value
     436  
     437              for frame_summary, line_no in walk_tb(traceback):
     438                  filename = frame_summary.f_code.co_filename
     439                  if filename and not filename.startswith("<"):
     440                      if not os.path.isabs(filename):
     441                          filename = os.path.join(_IMPORT_CWD, filename)
     442                  if frame_summary.f_locals.get("_rich_traceback_omit", False):
     443                      continue
     444  
     445                  frame = Frame(
     446                      filename=filename or "?",
     447                      lineno=line_no,
     448                      name=frame_summary.f_code.co_name,
     449                      locals={
     450                          key: pretty.traverse(
     451                              value,
     452                              max_length=locals_max_length,
     453                              max_string=locals_max_string,
     454                          )
     455                          for key, value in get_locals(frame_summary.f_locals.items())
     456                      }
     457                      if show_locals
     458                      else None,
     459                  )
     460                  append(frame)
     461                  if frame_summary.f_locals.get("_rich_traceback_guard", False):
     462                      del stack.frames[:]
     463  
     464              cause = getattr(exc_value, "__cause__", None)
     465              if cause:
     466                  exc_type = cause.__class__
     467                  exc_value = cause
     468                  # __traceback__ can be None, e.g. for exceptions raised by the
     469                  # 'multiprocessing' module
     470                  traceback = cause.__traceback__
     471                  is_cause = True
     472                  continue
     473  
     474              cause = exc_value.__context__
     475              if cause and not getattr(exc_value, "__suppress_context__", False):
     476                  exc_type = cause.__class__
     477                  exc_value = cause
     478                  traceback = cause.__traceback__
     479                  is_cause = False
     480                  continue
     481              # No cover, code is reached but coverage doesn't recognize it.
     482              break  # pragma: no cover
     483  
     484          trace = Trace(stacks=stacks)
     485          return trace
     486  
     487      def __rich_console__(
     488          self, console: Console, options: ConsoleOptions
     489      ) -> RenderResult:
     490          theme = self.theme
     491          background_style = theme.get_background_style()
     492          token_style = theme.get_style_for_token
     493  
     494          traceback_theme = Theme(
     495              {
     496                  "pretty": token_style(TextToken),
     497                  "pygments.text": token_style(Token),
     498                  "pygments.string": token_style(String),
     499                  "pygments.function": token_style(Name.Function),
     500                  "pygments.number": token_style(Number),
     501                  "repr.indent": token_style(Comment) + Style(dim=True),
     502                  "repr.str": token_style(String),
     503                  "repr.brace": token_style(TextToken) + Style(bold=True),
     504                  "repr.number": token_style(Number),
     505                  "repr.bool_true": token_style(Keyword.Constant),
     506                  "repr.bool_false": token_style(Keyword.Constant),
     507                  "repr.none": token_style(Keyword.Constant),
     508                  "scope.border": token_style(String.Delimiter),
     509                  "scope.equals": token_style(Operator),
     510                  "scope.key": token_style(Name),
     511                  "scope.key.special": token_style(Name.Constant) + Style(dim=True),
     512              },
     513              inherit=False,
     514          )
     515  
     516          highlighter = ReprHighlighter()
     517          for last, stack in loop_last(reversed(self.trace.stacks)):
     518              if stack.frames:
     519                  stack_renderable: ConsoleRenderable = Panel(
     520                      self._render_stack(stack),
     521                      title="[traceback.title]Traceback [dim](most recent call last)",
     522                      style=background_style,
     523                      border_style="traceback.border",
     524                      expand=True,
     525                      padding=(0, 1),
     526                  )
     527                  stack_renderable = Constrain(stack_renderable, self.width)
     528                  with console.use_theme(traceback_theme):
     529                      yield stack_renderable
     530              if stack.syntax_error is not None:
     531                  with console.use_theme(traceback_theme):
     532                      yield Constrain(
     533                          Panel(
     534                              self._render_syntax_error(stack.syntax_error),
     535                              style=background_style,
     536                              border_style="traceback.border.syntax_error",
     537                              expand=True,
     538                              padding=(0, 1),
     539                              width=self.width,
     540                          ),
     541                          self.width,
     542                      )
     543                  yield Text.assemble(
     544                      (f"{stack.exc_type}: ", "traceback.exc_type"),
     545                      highlighter(stack.syntax_error.msg),
     546                  )
     547              elif stack.exc_value:
     548                  yield Text.assemble(
     549                      (f"{stack.exc_type}: ", "traceback.exc_type"),
     550                      highlighter(stack.exc_value),
     551                  )
     552              else:
     553                  yield Text.assemble((f"{stack.exc_type}", "traceback.exc_type"))
     554  
     555              if not last:
     556                  if stack.is_cause:
     557                      yield Text.from_markup(
     558                          "\n[i]The above exception was the direct cause of the following exception:\n",
     559                      )
     560                  else:
     561                      yield Text.from_markup(
     562                          "\n[i]During handling of the above exception, another exception occurred:\n",
     563                      )
     564  
     565      @group()
     566      def _render_syntax_error(self, syntax_error: _SyntaxError) -> RenderResult:
     567          highlighter = ReprHighlighter()
     568          path_highlighter = PathHighlighter()
     569          if syntax_error.filename != "<stdin>":
     570              if os.path.exists(syntax_error.filename):
     571                  text = Text.assemble(
     572                      (f" {syntax_error.filename}", "pygments.string"),
     573                      (":", "pygments.text"),
     574                      (str(syntax_error.lineno), "pygments.number"),
     575                      style="pygments.text",
     576                  )
     577                  yield path_highlighter(text)
     578          syntax_error_text = highlighter(syntax_error.line.rstrip())
     579          syntax_error_text.no_wrap = True
     580          offset = min(syntax_error.offset - 1, len(syntax_error_text))
     581          syntax_error_text.stylize("bold underline", offset, offset)
     582          syntax_error_text += Text.from_markup(
     583              "\n" + " " * offset + "[traceback.offset]▲[/]",
     584              style="pygments.text",
     585          )
     586          yield syntax_error_text
     587  
     588      @classmethod
     589      def _guess_lexer(cls, filename: str, code: str) -> str:
     590          ext = os.path.splitext(filename)[-1]
     591          if not ext:
     592              # No extension, look at first line to see if it is a hashbang
     593              # Note, this is an educated guess and not a guarantee
     594              # If it fails, the only downside is that the code is highlighted strangely
     595              new_line_index = code.index("\n")
     596              first_line = code[:new_line_index] if new_line_index != -1 else code
     597              if first_line.startswith("#!") and "python" in first_line.lower():
     598                  return "python"
     599          try:
     600              return cls.LEXERS.get(ext) or guess_lexer_for_filename(filename, code).name
     601          except ClassNotFound:
     602              return "text"
     603  
     604      @group()
     605      def _render_stack(self, stack: Stack) -> RenderResult:
     606          path_highlighter = PathHighlighter()
     607          theme = self.theme
     608  
     609          def read_code(filename: str) -> str:
     610              """Read files, and cache results on filename.
     611  
     612              Args:
     613                  filename (str): Filename to read
     614  
     615              Returns:
     616                  str: Contents of file
     617              """
     618              return "".join(linecache.getlines(filename))
     619  
     620          def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]:
     621              if frame.locals:
     622                  yield render_scope(
     623                      frame.locals,
     624                      title="locals",
     625                      indent_guides=self.indent_guides,
     626                      max_length=self.locals_max_length,
     627                      max_string=self.locals_max_string,
     628                  )
     629  
     630          exclude_frames: Optional[range] = None
     631          if self.max_frames != 0:
     632              exclude_frames = range(
     633                  self.max_frames // 2,
     634                  len(stack.frames) - self.max_frames // 2,
     635              )
     636  
     637          excluded = False
     638          for frame_index, frame in enumerate(stack.frames):
     639  
     640              if exclude_frames and frame_index in exclude_frames:
     641                  excluded = True
     642                  continue
     643  
     644              if excluded:
     645                  assert exclude_frames is not None
     646                  yield Text(
     647                      f"\n... {len(exclude_frames)} frames hidden ...",
     648                      justify="center",
     649                      style="traceback.error",
     650                  )
     651                  excluded = False
     652  
     653              first = frame_index == 0
     654              frame_filename = frame.filename
     655              suppressed = any(frame_filename.startswith(path) for path in self.suppress)
     656  
     657              if os.path.exists(frame.filename):
     658                  text = Text.assemble(
     659                      path_highlighter(Text(frame.filename, style="pygments.string")),
     660                      (":", "pygments.text"),
     661                      (str(frame.lineno), "pygments.number"),
     662                      " in ",
     663                      (frame.name, "pygments.function"),
     664                      style="pygments.text",
     665                  )
     666              else:
     667                  text = Text.assemble(
     668                      "in ",
     669                      (frame.name, "pygments.function"),
     670                      (":", "pygments.text"),
     671                      (str(frame.lineno), "pygments.number"),
     672                      style="pygments.text",
     673                  )
     674              if not frame.filename.startswith("<") and not first:
     675                  yield ""
     676              yield text
     677              if frame.filename.startswith("<"):
     678                  yield from render_locals(frame)
     679                  continue
     680              if not suppressed:
     681                  try:
     682                      code = read_code(frame.filename)
     683                      if not code:
     684                          # code may be an empty string if the file doesn't exist, OR
     685                          # if the traceback filename is generated dynamically
     686                          continue
     687                      lexer_name = self._guess_lexer(frame.filename, code)
     688                      syntax = Syntax(
     689                          code,
     690                          lexer_name,
     691                          theme=theme,
     692                          line_numbers=True,
     693                          line_range=(
     694                              frame.lineno - self.extra_lines,
     695                              frame.lineno + self.extra_lines,
     696                          ),
     697                          highlight_lines={frame.lineno},
     698                          word_wrap=self.word_wrap,
     699                          code_width=88,
     700                          indent_guides=self.indent_guides,
     701                          dedent=False,
     702                      )
     703                      yield ""
     704                  except Exception as error:
     705                      yield Text.assemble(
     706                          (f"\n{error}", "traceback.error"),
     707                      )
     708                  else:
     709                      yield (
     710                          Columns(
     711                              [
     712                                  syntax,
     713                                  *render_locals(frame),
     714                              ],
     715                              padding=1,
     716                          )
     717                          if frame.locals
     718                          else syntax
     719                      )
     720  
     721  
     722  if __name__ == "__main__":  # pragma: no cover
     723  
     724      from .console import Console
     725  
     726      console = Console()
     727      import sys
     728  
     729      def bar(a: Any) -> None:  # 这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑
     730          one = 1
     731          print(one / a)
     732  
     733      def foo(a: Any) -> None:
     734          _rich_traceback_guard = True
     735          zed = {
     736              "characters": {
     737                  "Paul Atreides",
     738                  "Vladimir Harkonnen",
     739                  "Thufir Hawat",
     740                  "Duncan Idaho",
     741              },
     742              "atomic_types": (None, False, True),
     743          }
     744          bar(a)
     745  
     746      def error() -> None:
     747  
     748          try:
     749              try:
     750                  foo(0)
     751              except:
     752                  slfkjsldkfj  # type: ignore[name-defined]
     753          except:
     754              console.print_exception(show_locals=True)
     755  
     756      error()