Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/rich/traceback.py: 67%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

301 statements  

1import inspect 

2import linecache 

3import os 

4import sys 

5from dataclasses import dataclass, field 

6from itertools import islice 

7from traceback import walk_tb 

8from types import ModuleType, TracebackType 

9from typing import ( 

10 Any, 

11 Callable, 

12 Dict, 

13 Iterable, 

14 List, 

15 Optional, 

16 Sequence, 

17 Set, 

18 Tuple, 

19 Type, 

20 Union, 

21) 

22 

23from pygments.lexers import guess_lexer_for_filename 

24from pygments.token import Comment, Keyword, Name, Number, Operator, String 

25from pygments.token import Text as TextToken 

26from pygments.token import Token 

27from pygments.util import ClassNotFound 

28 

29from . import pretty 

30from ._loop import loop_first_last, loop_last 

31from .columns import Columns 

32from .console import ( 

33 Console, 

34 ConsoleOptions, 

35 ConsoleRenderable, 

36 Group, 

37 RenderResult, 

38 group, 

39) 

40from .constrain import Constrain 

41from .highlighter import RegexHighlighter, ReprHighlighter 

42from .panel import Panel 

43from .scope import render_scope 

44from .style import Style 

45from .syntax import Syntax, SyntaxPosition 

46from .text import Text 

47from .theme import Theme 

48 

49WINDOWS = sys.platform == "win32" 

50 

51LOCALS_MAX_LENGTH = 10 

52LOCALS_MAX_STRING = 80 

53 

54 

55def _iter_syntax_lines( 

56 start: SyntaxPosition, end: SyntaxPosition 

57) -> Iterable[Tuple[int, int, int]]: 

58 """Yield start and end positions per line. 

59 

60 Args: 

61 start: Start position. 

62 end: End position. 

63 

64 Returns: 

65 Iterable of (LINE, COLUMN1, COLUMN2). 

66 """ 

67 

68 line1, column1 = start 

69 line2, column2 = end 

70 

71 if line1 == line2: 

72 yield line1, column1, column2 

73 else: 

74 for first, last, line_no in loop_first_last(range(line1, line2 + 1)): 

75 if first: 

76 yield line_no, column1, -1 

77 elif last: 

78 yield line_no, 0, column2 

79 else: 

80 yield line_no, 0, -1 

81 

82 

83def install( 

84 *, 

85 console: Optional[Console] = None, 

86 width: Optional[int] = 100, 

87 code_width: Optional[int] = 88, 

88 extra_lines: int = 3, 

89 theme: Optional[str] = None, 

90 word_wrap: bool = False, 

91 show_locals: bool = False, 

92 locals_max_length: int = LOCALS_MAX_LENGTH, 

93 locals_max_string: int = LOCALS_MAX_STRING, 

94 locals_hide_dunder: bool = True, 

95 locals_hide_sunder: Optional[bool] = None, 

96 indent_guides: bool = True, 

97 suppress: Iterable[Union[str, ModuleType]] = (), 

98 max_frames: int = 100, 

99) -> Callable[[Type[BaseException], BaseException, Optional[TracebackType]], Any]: 

100 """Install a rich traceback handler. 

101 

102 Once installed, any tracebacks will be printed with syntax highlighting and rich formatting. 

103 

104 

105 Args: 

106 console (Optional[Console], optional): Console to write exception to. Default uses internal Console instance. 

107 width (Optional[int], optional): Width (in characters) of traceback. Defaults to 100. 

108 code_width (Optional[int], optional): Code width (in characters) of traceback. Defaults to 88. 

109 extra_lines (int, optional): Extra lines of code. Defaults to 3. 

110 theme (Optional[str], optional): Pygments theme to use in traceback. Defaults to ``None`` which will pick 

111 a theme appropriate for the platform. 

112 word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False. 

113 show_locals (bool, optional): Enable display of local variables. Defaults to False. 

114 locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation. 

115 Defaults to 10. 

116 locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80. 

117 locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True. 

118 locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False. 

119 indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True. 

120 suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback. 

121 

122 Returns: 

123 Callable: The previous exception handler that was replaced. 

124 

125 """ 

126 traceback_console = Console(stderr=True) if console is None else console 

127 

128 locals_hide_sunder = ( 

129 True 

130 if (traceback_console.is_jupyter and locals_hide_sunder is None) 

131 else locals_hide_sunder 

132 ) 

133 

134 def excepthook( 

135 type_: Type[BaseException], 

136 value: BaseException, 

137 traceback: Optional[TracebackType], 

138 ) -> None: 

139 exception_traceback = Traceback.from_exception( 

140 type_, 

141 value, 

142 traceback, 

143 width=width, 

144 code_width=code_width, 

145 extra_lines=extra_lines, 

146 theme=theme, 

147 word_wrap=word_wrap, 

148 show_locals=show_locals, 

149 locals_max_length=locals_max_length, 

150 locals_max_string=locals_max_string, 

151 locals_hide_dunder=locals_hide_dunder, 

152 locals_hide_sunder=bool(locals_hide_sunder), 

153 indent_guides=indent_guides, 

154 suppress=suppress, 

155 max_frames=max_frames, 

156 ) 

157 traceback_console.print(exception_traceback) 

158 

159 def ipy_excepthook_closure(ip: Any) -> None: # pragma: no cover 

160 tb_data = {} # store information about showtraceback call 

161 default_showtraceback = ip.showtraceback # keep reference of default traceback 

162 

163 def ipy_show_traceback(*args: Any, **kwargs: Any) -> None: 

164 """wrap the default ip.showtraceback to store info for ip._showtraceback""" 

165 nonlocal tb_data 

166 tb_data = kwargs 

167 default_showtraceback(*args, **kwargs) 

168 

169 def ipy_display_traceback( 

170 *args: Any, is_syntax: bool = False, **kwargs: Any 

171 ) -> None: 

172 """Internally called traceback from ip._showtraceback""" 

173 nonlocal tb_data 

174 exc_tuple = ip._get_exc_info() 

175 

176 # do not display trace on syntax error 

177 tb: Optional[TracebackType] = None if is_syntax else exc_tuple[2] 

178 

179 # determine correct tb_offset 

180 compiled = tb_data.get("running_compiled_code", False) 

181 tb_offset = tb_data.get("tb_offset") 

182 if tb_offset is None: 

183 tb_offset = 1 if compiled else 0 

184 # remove ipython internal frames from trace with tb_offset 

185 for _ in range(tb_offset): 

186 if tb is None: 

187 break 

188 tb = tb.tb_next 

189 

190 excepthook(exc_tuple[0], exc_tuple[1], tb) 

191 tb_data = {} # clear data upon usage 

192 

193 # replace _showtraceback instead of showtraceback to allow ipython features such as debugging to work 

194 # this is also what the ipython docs recommends to modify when subclassing InteractiveShell 

195 ip._showtraceback = ipy_display_traceback 

196 # add wrapper to capture tb_data 

197 ip.showtraceback = ipy_show_traceback 

198 ip.showsyntaxerror = lambda *args, **kwargs: ipy_display_traceback( 

199 *args, is_syntax=True, **kwargs 

200 ) 

201 

202 try: # pragma: no cover 

203 # if within ipython, use customized traceback 

204 ip = get_ipython() # type: ignore[name-defined] 

205 ipy_excepthook_closure(ip) 

206 return sys.excepthook 

207 except Exception: 

208 # otherwise use default system hook 

209 old_excepthook = sys.excepthook 

210 sys.excepthook = excepthook 

211 return old_excepthook 

212 

213 

214@dataclass 

215class Frame: 

216 filename: str 

217 lineno: int 

218 name: str 

219 line: str = "" 

220 locals: Optional[Dict[str, pretty.Node]] = None 

221 last_instruction: Optional[Tuple[Tuple[int, int], Tuple[int, int]]] = None 

222 

223 

224@dataclass 

225class _SyntaxError: 

226 offset: int 

227 filename: str 

228 line: str 

229 lineno: int 

230 msg: str 

231 notes: List[str] = field(default_factory=list) 

232 

233 

234@dataclass 

235class Stack: 

236 exc_type: str 

237 exc_value: str 

238 syntax_error: Optional[_SyntaxError] = None 

239 is_cause: bool = False 

240 frames: List[Frame] = field(default_factory=list) 

241 notes: List[str] = field(default_factory=list) 

242 is_group: bool = False 

243 exceptions: List["Trace"] = field(default_factory=list) 

244 

245 

246@dataclass 

247class Trace: 

248 stacks: List[Stack] 

249 

250 

251class PathHighlighter(RegexHighlighter): 

252 highlights = [r"(?P<dim>.*/)(?P<bold>.+)"] 

253 

254 

255class Traceback: 

256 """A Console renderable that renders a traceback. 

257 

258 Args: 

259 trace (Trace, optional): A `Trace` object produced from `extract`. Defaults to None, which uses 

260 the last exception. 

261 width (Optional[int], optional): Number of characters used to traceback. Defaults to 100. 

262 code_width (Optional[int], optional): Number of code characters used to traceback. Defaults to 88. 

263 extra_lines (int, optional): Additional lines of code to render. Defaults to 3. 

264 theme (str, optional): Override pygments theme used in traceback. 

265 word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False. 

266 show_locals (bool, optional): Enable display of local variables. Defaults to False. 

267 indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True. 

268 locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation. 

269 Defaults to 10. 

270 locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80. 

271 locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True. 

272 locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False. 

273 suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback. 

274 max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100. 

275 

276 """ 

277 

278 LEXERS = { 

279 "": "text", 

280 ".py": "python", 

281 ".pxd": "cython", 

282 ".pyx": "cython", 

283 ".pxi": "pyrex", 

284 } 

285 

286 def __init__( 

287 self, 

288 trace: Optional[Trace] = None, 

289 *, 

290 width: Optional[int] = 100, 

291 code_width: Optional[int] = 88, 

292 extra_lines: int = 3, 

293 theme: Optional[str] = None, 

294 word_wrap: bool = False, 

295 show_locals: bool = False, 

296 locals_max_length: int = LOCALS_MAX_LENGTH, 

297 locals_max_string: int = LOCALS_MAX_STRING, 

298 locals_hide_dunder: bool = True, 

299 locals_hide_sunder: bool = False, 

300 indent_guides: bool = True, 

301 suppress: Iterable[Union[str, ModuleType]] = (), 

302 max_frames: int = 100, 

303 ): 

304 if trace is None: 

305 exc_type, exc_value, traceback = sys.exc_info() 

306 if exc_type is None or exc_value is None or traceback is None: 

307 raise ValueError( 

308 "Value for 'trace' required if not called in except: block" 

309 ) 

310 trace = self.extract( 

311 exc_type, exc_value, traceback, show_locals=show_locals 

312 ) 

313 self.trace = trace 

314 self.width = width 

315 self.code_width = code_width 

316 self.extra_lines = extra_lines 

317 self.theme = Syntax.get_theme(theme or "ansi_dark") 

318 self.word_wrap = word_wrap 

319 self.show_locals = show_locals 

320 self.indent_guides = indent_guides 

321 self.locals_max_length = locals_max_length 

322 self.locals_max_string = locals_max_string 

323 self.locals_hide_dunder = locals_hide_dunder 

324 self.locals_hide_sunder = locals_hide_sunder 

325 

326 self.suppress: Sequence[str] = [] 

327 for suppress_entity in suppress: 

328 if not isinstance(suppress_entity, str): 

329 assert ( 

330 suppress_entity.__file__ is not None 

331 ), f"{suppress_entity!r} must be a module with '__file__' attribute" 

332 path = os.path.dirname(suppress_entity.__file__) 

333 else: 

334 path = suppress_entity 

335 path = os.path.normpath(os.path.abspath(path)) 

336 self.suppress.append(path) 

337 self.max_frames = max(4, max_frames) if max_frames > 0 else 0 

338 

339 @classmethod 

340 def from_exception( 

341 cls, 

342 exc_type: Type[Any], 

343 exc_value: BaseException, 

344 traceback: Optional[TracebackType], 

345 *, 

346 width: Optional[int] = 100, 

347 code_width: Optional[int] = 88, 

348 extra_lines: int = 3, 

349 theme: Optional[str] = None, 

350 word_wrap: bool = False, 

351 show_locals: bool = False, 

352 locals_max_length: int = LOCALS_MAX_LENGTH, 

353 locals_max_string: int = LOCALS_MAX_STRING, 

354 locals_hide_dunder: bool = True, 

355 locals_hide_sunder: bool = False, 

356 indent_guides: bool = True, 

357 suppress: Iterable[Union[str, ModuleType]] = (), 

358 max_frames: int = 100, 

359 ) -> "Traceback": 

360 """Create a traceback from exception info 

361 

362 Args: 

363 exc_type (Type[BaseException]): Exception type. 

364 exc_value (BaseException): Exception value. 

365 traceback (TracebackType): Python Traceback object. 

366 width (Optional[int], optional): Number of characters used to traceback. Defaults to 100. 

367 code_width (Optional[int], optional): Number of code characters used to traceback. Defaults to 88. 

368 extra_lines (int, optional): Additional lines of code to render. Defaults to 3. 

369 theme (str, optional): Override pygments theme used in traceback. 

370 word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False. 

371 show_locals (bool, optional): Enable display of local variables. Defaults to False. 

372 indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True. 

373 locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation. 

374 Defaults to 10. 

375 locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80. 

376 locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True. 

377 locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False. 

378 suppress (Iterable[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback. 

379 max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100. 

380 

381 Returns: 

382 Traceback: A Traceback instance that may be printed. 

383 """ 

384 rich_traceback = cls.extract( 

385 exc_type, 

386 exc_value, 

387 traceback, 

388 show_locals=show_locals, 

389 locals_max_length=locals_max_length, 

390 locals_max_string=locals_max_string, 

391 locals_hide_dunder=locals_hide_dunder, 

392 locals_hide_sunder=locals_hide_sunder, 

393 ) 

394 

395 return cls( 

396 rich_traceback, 

397 width=width, 

398 code_width=code_width, 

399 extra_lines=extra_lines, 

400 theme=theme, 

401 word_wrap=word_wrap, 

402 show_locals=show_locals, 

403 indent_guides=indent_guides, 

404 locals_max_length=locals_max_length, 

405 locals_max_string=locals_max_string, 

406 locals_hide_dunder=locals_hide_dunder, 

407 locals_hide_sunder=locals_hide_sunder, 

408 suppress=suppress, 

409 max_frames=max_frames, 

410 ) 

411 

412 @classmethod 

413 def extract( 

414 cls, 

415 exc_type: Type[BaseException], 

416 exc_value: BaseException, 

417 traceback: Optional[TracebackType], 

418 *, 

419 show_locals: bool = False, 

420 locals_max_length: int = LOCALS_MAX_LENGTH, 

421 locals_max_string: int = LOCALS_MAX_STRING, 

422 locals_hide_dunder: bool = True, 

423 locals_hide_sunder: bool = False, 

424 _visited_exceptions: Optional[Set[BaseException]] = None, 

425 ) -> Trace: 

426 """Extract traceback information. 

427 

428 Args: 

429 exc_type (Type[BaseException]): Exception type. 

430 exc_value (BaseException): Exception value. 

431 traceback (TracebackType): Python Traceback object. 

432 show_locals (bool, optional): Enable display of local variables. Defaults to False. 

433 locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation. 

434 Defaults to 10. 

435 locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80. 

436 locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True. 

437 locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False. 

438 

439 Returns: 

440 Trace: A Trace instance which you can use to construct a `Traceback`. 

441 """ 

442 

443 stacks: List[Stack] = [] 

444 is_cause = False 

445 

446 from rich import _IMPORT_CWD 

447 

448 notes: List[str] = getattr(exc_value, "__notes__", None) or [] 

449 

450 grouped_exceptions: Set[BaseException] = ( 

451 set() if _visited_exceptions is None else _visited_exceptions 

452 ) 

453 

454 def safe_str(_object: Any) -> str: 

455 """Don't allow exceptions from __str__ to propagate.""" 

456 try: 

457 return str(_object) 

458 except Exception: 

459 return "<exception str() failed>" 

460 

461 while True: 

462 stack = Stack( 

463 exc_type=safe_str(exc_type.__name__), 

464 exc_value=safe_str(exc_value), 

465 is_cause=is_cause, 

466 notes=notes, 

467 ) 

468 

469 if sys.version_info >= (3, 11): 

470 if isinstance(exc_value, (BaseExceptionGroup, ExceptionGroup)): 

471 stack.is_group = True 

472 for exception in exc_value.exceptions: 

473 if exception in grouped_exceptions: 

474 continue 

475 grouped_exceptions.add(exception) 

476 stack.exceptions.append( 

477 Traceback.extract( 

478 type(exception), 

479 exception, 

480 exception.__traceback__, 

481 show_locals=show_locals, 

482 locals_max_length=locals_max_length, 

483 locals_hide_dunder=locals_hide_dunder, 

484 locals_hide_sunder=locals_hide_sunder, 

485 _visited_exceptions=grouped_exceptions, 

486 ) 

487 ) 

488 

489 if isinstance(exc_value, SyntaxError): 

490 stack.syntax_error = _SyntaxError( 

491 offset=exc_value.offset or 0, 

492 filename=exc_value.filename or "?", 

493 lineno=exc_value.lineno or 0, 

494 line=exc_value.text or "", 

495 msg=exc_value.msg, 

496 notes=notes, 

497 ) 

498 

499 stacks.append(stack) 

500 append = stack.frames.append 

501 

502 def get_locals( 

503 iter_locals: Iterable[Tuple[str, object]], 

504 ) -> Iterable[Tuple[str, object]]: 

505 """Extract locals from an iterator of key pairs.""" 

506 if not (locals_hide_dunder or locals_hide_sunder): 

507 yield from iter_locals 

508 return 

509 for key, value in iter_locals: 

510 if locals_hide_dunder and key.startswith("__"): 

511 continue 

512 if locals_hide_sunder and key.startswith("_"): 

513 continue 

514 yield key, value 

515 

516 for frame_summary, line_no in walk_tb(traceback): 

517 filename = frame_summary.f_code.co_filename 

518 

519 last_instruction: Optional[Tuple[Tuple[int, int], Tuple[int, int]]] 

520 last_instruction = None 

521 if sys.version_info >= (3, 11): 

522 instruction_index = frame_summary.f_lasti // 2 

523 instruction_position = next( 

524 islice( 

525 frame_summary.f_code.co_positions(), 

526 instruction_index, 

527 instruction_index + 1, 

528 ) 

529 ) 

530 ( 

531 start_line, 

532 end_line, 

533 start_column, 

534 end_column, 

535 ) = instruction_position 

536 if ( 

537 start_line is not None 

538 and end_line is not None 

539 and start_column is not None 

540 and end_column is not None 

541 ): 

542 last_instruction = ( 

543 (start_line, start_column), 

544 (end_line, end_column), 

545 ) 

546 

547 if filename and not filename.startswith("<"): 

548 if not os.path.isabs(filename): 

549 filename = os.path.join(_IMPORT_CWD, filename) 

550 if frame_summary.f_locals.get("_rich_traceback_omit", False): 

551 continue 

552 

553 frame = Frame( 

554 filename=filename or "?", 

555 lineno=line_no, 

556 name=frame_summary.f_code.co_name, 

557 locals=( 

558 { 

559 key: pretty.traverse( 

560 value, 

561 max_length=locals_max_length, 

562 max_string=locals_max_string, 

563 ) 

564 for key, value in get_locals(frame_summary.f_locals.items()) 

565 if not (inspect.isfunction(value) or inspect.isclass(value)) 

566 } 

567 if show_locals 

568 else None 

569 ), 

570 last_instruction=last_instruction, 

571 ) 

572 append(frame) 

573 if frame_summary.f_locals.get("_rich_traceback_guard", False): 

574 del stack.frames[:] 

575 

576 if not grouped_exceptions: 

577 cause = getattr(exc_value, "__cause__", None) 

578 if cause is not None and cause is not exc_value: 

579 exc_type = cause.__class__ 

580 exc_value = cause 

581 # __traceback__ can be None, e.g. for exceptions raised by the 

582 # 'multiprocessing' module 

583 traceback = cause.__traceback__ 

584 is_cause = True 

585 continue 

586 

587 cause = exc_value.__context__ 

588 if cause is not None and not getattr( 

589 exc_value, "__suppress_context__", False 

590 ): 

591 exc_type = cause.__class__ 

592 exc_value = cause 

593 traceback = cause.__traceback__ 

594 is_cause = False 

595 continue 

596 # No cover, code is reached but coverage doesn't recognize it. 

597 break # pragma: no cover 

598 

599 trace = Trace(stacks=stacks) 

600 

601 return trace 

602 

603 def __rich_console__( 

604 self, console: Console, options: ConsoleOptions 

605 ) -> RenderResult: 

606 theme = self.theme 

607 background_style = theme.get_background_style() 

608 token_style = theme.get_style_for_token 

609 

610 traceback_theme = Theme( 

611 { 

612 "pretty": token_style(TextToken), 

613 "pygments.text": token_style(Token), 

614 "pygments.string": token_style(String), 

615 "pygments.function": token_style(Name.Function), 

616 "pygments.number": token_style(Number), 

617 "repr.indent": token_style(Comment) + Style(dim=True), 

618 "repr.str": token_style(String), 

619 "repr.brace": token_style(TextToken) + Style(bold=True), 

620 "repr.number": token_style(Number), 

621 "repr.bool_true": token_style(Keyword.Constant), 

622 "repr.bool_false": token_style(Keyword.Constant), 

623 "repr.none": token_style(Keyword.Constant), 

624 "scope.border": token_style(String.Delimiter), 

625 "scope.equals": token_style(Operator), 

626 "scope.key": token_style(Name), 

627 "scope.key.special": token_style(Name.Constant) + Style(dim=True), 

628 }, 

629 inherit=False, 

630 ) 

631 

632 highlighter = ReprHighlighter() 

633 

634 @group() 

635 def render_stack(stack: Stack, last: bool) -> RenderResult: 

636 if stack.frames: 

637 stack_renderable: ConsoleRenderable = Panel( 

638 self._render_stack(stack), 

639 title="[traceback.title]Traceback [dim](most recent call last)", 

640 style=background_style, 

641 border_style="traceback.border", 

642 expand=True, 

643 padding=(0, 1), 

644 ) 

645 stack_renderable = Constrain(stack_renderable, self.width) 

646 with console.use_theme(traceback_theme): 

647 yield stack_renderable 

648 

649 if stack.syntax_error is not None: 

650 with console.use_theme(traceback_theme): 

651 yield Constrain( 

652 Panel( 

653 self._render_syntax_error(stack.syntax_error), 

654 style=background_style, 

655 border_style="traceback.border.syntax_error", 

656 expand=True, 

657 padding=(0, 1), 

658 width=self.width, 

659 ), 

660 self.width, 

661 ) 

662 yield Text.assemble( 

663 (f"{stack.exc_type}: ", "traceback.exc_type"), 

664 highlighter(stack.syntax_error.msg), 

665 ) 

666 elif stack.exc_value: 

667 yield Text.assemble( 

668 (f"{stack.exc_type}: ", "traceback.exc_type"), 

669 highlighter(stack.exc_value), 

670 ) 

671 else: 

672 yield Text.assemble((f"{stack.exc_type}", "traceback.exc_type")) 

673 

674 for note in stack.notes: 

675 yield Text.assemble(("[NOTE] ", "traceback.note"), highlighter(note)) 

676 

677 if stack.is_group: 

678 for group_no, group_exception in enumerate(stack.exceptions, 1): 

679 grouped_exceptions: List[Group] = [] 

680 for group_last, group_stack in loop_last(group_exception.stacks): 

681 grouped_exceptions.append(render_stack(group_stack, group_last)) 

682 yield "" 

683 yield Constrain( 

684 Panel( 

685 Group(*grouped_exceptions), 

686 title=f"Sub-exception #{group_no}", 

687 border_style="traceback.group.border", 

688 ), 

689 self.width, 

690 ) 

691 

692 if not last: 

693 if stack.is_cause: 

694 yield Text.from_markup( 

695 "\n[i]The above exception was the direct cause of the following exception:\n", 

696 ) 

697 else: 

698 yield Text.from_markup( 

699 "\n[i]During handling of the above exception, another exception occurred:\n", 

700 ) 

701 

702 for last, stack in loop_last(reversed(self.trace.stacks)): 

703 yield render_stack(stack, last) 

704 

705 @group() 

706 def _render_syntax_error(self, syntax_error: _SyntaxError) -> RenderResult: 

707 highlighter = ReprHighlighter() 

708 path_highlighter = PathHighlighter() 

709 if syntax_error.filename != "<stdin>": 

710 if os.path.exists(syntax_error.filename): 

711 text = Text.assemble( 

712 (f" {syntax_error.filename}", "pygments.string"), 

713 (":", "pygments.text"), 

714 (str(syntax_error.lineno), "pygments.number"), 

715 style="pygments.text", 

716 ) 

717 yield path_highlighter(text) 

718 syntax_error_text = highlighter(syntax_error.line.rstrip()) 

719 syntax_error_text.no_wrap = True 

720 offset = min(syntax_error.offset - 1, len(syntax_error_text)) 

721 syntax_error_text.stylize("bold underline", offset, offset) 

722 syntax_error_text += Text.from_markup( 

723 "\n" + " " * offset + "[traceback.offset]▲[/]", 

724 style="pygments.text", 

725 ) 

726 yield syntax_error_text 

727 

728 @classmethod 

729 def _guess_lexer(cls, filename: str, code: str) -> str: 

730 ext = os.path.splitext(filename)[-1] 

731 if not ext: 

732 # No extension, look at first line to see if it is a hashbang 

733 # Note, this is an educated guess and not a guarantee 

734 # If it fails, the only downside is that the code is highlighted strangely 

735 new_line_index = code.index("\n") 

736 first_line = code[:new_line_index] if new_line_index != -1 else code 

737 if first_line.startswith("#!") and "python" in first_line.lower(): 

738 return "python" 

739 try: 

740 return cls.LEXERS.get(ext) or guess_lexer_for_filename(filename, code).name 

741 except ClassNotFound: 

742 return "text" 

743 

744 @group() 

745 def _render_stack(self, stack: Stack) -> RenderResult: 

746 path_highlighter = PathHighlighter() 

747 theme = self.theme 

748 

749 def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]: 

750 if frame.locals: 

751 yield render_scope( 

752 frame.locals, 

753 title="locals", 

754 indent_guides=self.indent_guides, 

755 max_length=self.locals_max_length, 

756 max_string=self.locals_max_string, 

757 ) 

758 

759 exclude_frames: Optional[range] = None 

760 if self.max_frames != 0: 

761 exclude_frames = range( 

762 self.max_frames // 2, 

763 len(stack.frames) - self.max_frames // 2, 

764 ) 

765 

766 excluded = False 

767 for frame_index, frame in enumerate(stack.frames): 

768 if exclude_frames and frame_index in exclude_frames: 

769 excluded = True 

770 continue 

771 

772 if excluded: 

773 assert exclude_frames is not None 

774 yield Text( 

775 f"\n... {len(exclude_frames)} frames hidden ...", 

776 justify="center", 

777 style="traceback.error", 

778 ) 

779 excluded = False 

780 

781 first = frame_index == 0 

782 frame_filename = frame.filename 

783 suppressed = any(frame_filename.startswith(path) for path in self.suppress) 

784 

785 if os.path.exists(frame.filename): 

786 text = Text.assemble( 

787 path_highlighter(Text(frame.filename, style="pygments.string")), 

788 (":", "pygments.text"), 

789 (str(frame.lineno), "pygments.number"), 

790 " in ", 

791 (frame.name, "pygments.function"), 

792 style="pygments.text", 

793 ) 

794 else: 

795 text = Text.assemble( 

796 "in ", 

797 (frame.name, "pygments.function"), 

798 (":", "pygments.text"), 

799 (str(frame.lineno), "pygments.number"), 

800 style="pygments.text", 

801 ) 

802 if not frame.filename.startswith("<") and not first: 

803 yield "" 

804 yield text 

805 if frame.filename.startswith("<"): 

806 yield from render_locals(frame) 

807 continue 

808 if not suppressed: 

809 try: 

810 code_lines = linecache.getlines(frame.filename) 

811 code = "".join(code_lines) 

812 if not code: 

813 # code may be an empty string if the file doesn't exist, OR 

814 # if the traceback filename is generated dynamically 

815 continue 

816 lexer_name = self._guess_lexer(frame.filename, code) 

817 syntax = Syntax( 

818 code, 

819 lexer_name, 

820 theme=theme, 

821 line_numbers=True, 

822 line_range=( 

823 frame.lineno - self.extra_lines, 

824 frame.lineno + self.extra_lines, 

825 ), 

826 highlight_lines={frame.lineno}, 

827 word_wrap=self.word_wrap, 

828 code_width=self.code_width, 

829 indent_guides=self.indent_guides, 

830 dedent=False, 

831 ) 

832 yield "" 

833 except Exception as error: 

834 yield Text.assemble( 

835 (f"\n{error}", "traceback.error"), 

836 ) 

837 else: 

838 if frame.last_instruction is not None: 

839 start, end = frame.last_instruction 

840 

841 # Stylize a line at a time 

842 # So that indentation isn't underlined (which looks bad) 

843 for line1, column1, column2 in _iter_syntax_lines(start, end): 

844 try: 

845 if column1 == 0: 

846 line = code_lines[line1 - 1] 

847 column1 = len(line) - len(line.lstrip()) 

848 if column2 == -1: 

849 column2 = len(code_lines[line1 - 1]) 

850 except IndexError: 

851 # Being defensive here 

852 # If last_instruction reports a line out-of-bounds, we don't want to crash 

853 continue 

854 

855 syntax.stylize_range( 

856 style="traceback.error_range", 

857 start=(line1, column1), 

858 end=(line1, column2), 

859 ) 

860 yield ( 

861 Columns( 

862 [ 

863 syntax, 

864 *render_locals(frame), 

865 ], 

866 padding=1, 

867 ) 

868 if frame.locals 

869 else syntax 

870 ) 

871 

872 

873if __name__ == "__main__": # pragma: no cover 

874 install(show_locals=True) 

875 import sys 

876 

877 def bar( 

878 a: Any, 

879 ) -> None: # 这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑 

880 one = 1 

881 print(one / a) 

882 

883 def foo(a: Any) -> None: 

884 _rich_traceback_guard = True 

885 zed = { 

886 "characters": { 

887 "Paul Atreides", 

888 "Vladimir Harkonnen", 

889 "Thufir Hawat", 

890 "Duncan Idaho", 

891 }, 

892 "atomic_types": (None, False, True), 

893 } 

894 bar(a) 

895 

896 def error() -> None: 

897 foo(0) 

898 

899 error()