python (3.12.0)
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()