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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

296 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 Tuple, 

18 Type, 

19 Union, 

20) 

21 

22from pygments.lexers import guess_lexer_for_filename 

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

24from pygments.token import Text as TextToken 

25from pygments.token import Token 

26from pygments.util import ClassNotFound 

27 

28from . import pretty 

29from ._loop import loop_first_last, loop_last 

30from .columns import Columns 

31from .console import ( 

32 Console, 

33 ConsoleOptions, 

34 ConsoleRenderable, 

35 Group, 

36 RenderResult, 

37 group, 

38) 

39from .constrain import Constrain 

40from .highlighter import RegexHighlighter, ReprHighlighter 

41from .panel import Panel 

42from .scope import render_scope 

43from .style import Style 

44from .syntax import Syntax, SyntaxPosition 

45from .text import Text 

46from .theme import Theme 

47 

48WINDOWS = sys.platform == "win32" 

49 

50LOCALS_MAX_LENGTH = 10 

51LOCALS_MAX_STRING = 80 

52 

53 

54def _iter_syntax_lines( 

55 start: SyntaxPosition, end: SyntaxPosition 

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

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

58 

59 Args: 

60 start: Start position. 

61 end: End position. 

62 

63 Returns: 

64 Iterable of (LINE, COLUMN1, COLUMN2). 

65 """ 

66 

67 line1, column1 = start 

68 line2, column2 = end 

69 

70 if line1 == line2: 

71 yield line1, column1, column2 

72 else: 

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

74 if first: 

75 yield line_no, column1, -1 

76 elif last: 

77 yield line_no, 0, column2 

78 else: 

79 yield line_no, 0, -1 

80 

81 

82def install( 

83 *, 

84 console: Optional[Console] = None, 

85 width: Optional[int] = 100, 

86 code_width: Optional[int] = 88, 

87 extra_lines: int = 3, 

88 theme: Optional[str] = None, 

89 word_wrap: bool = False, 

90 show_locals: bool = False, 

91 locals_max_length: int = LOCALS_MAX_LENGTH, 

92 locals_max_string: int = LOCALS_MAX_STRING, 

93 locals_hide_dunder: bool = True, 

94 locals_hide_sunder: Optional[bool] = None, 

95 indent_guides: bool = True, 

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

97 max_frames: int = 100, 

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

99 """Install a rich traceback handler. 

100 

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

102 

103 

104 Args: 

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

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

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

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

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

110 a theme appropriate for the platform. 

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

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

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

114 Defaults to 10. 

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

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

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

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

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

120 

121 Returns: 

122 Callable: The previous exception handler that was replaced. 

123 

124 """ 

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

126 

127 locals_hide_sunder = ( 

128 True 

129 if (traceback_console.is_jupyter and locals_hide_sunder is None) 

130 else locals_hide_sunder 

131 ) 

132 

133 def excepthook( 

134 type_: Type[BaseException], 

135 value: BaseException, 

136 traceback: Optional[TracebackType], 

137 ) -> None: 

138 exception_traceback = Traceback.from_exception( 

139 type_, 

140 value, 

141 traceback, 

142 width=width, 

143 code_width=code_width, 

144 extra_lines=extra_lines, 

145 theme=theme, 

146 word_wrap=word_wrap, 

147 show_locals=show_locals, 

148 locals_max_length=locals_max_length, 

149 locals_max_string=locals_max_string, 

150 locals_hide_dunder=locals_hide_dunder, 

151 locals_hide_sunder=bool(locals_hide_sunder), 

152 indent_guides=indent_guides, 

153 suppress=suppress, 

154 max_frames=max_frames, 

155 ) 

156 traceback_console.print(exception_traceback) 

157 

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

159 tb_data = {} # store information about showtraceback call 

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

161 

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

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

164 nonlocal tb_data 

165 tb_data = kwargs 

166 default_showtraceback(*args, **kwargs) 

167 

168 def ipy_display_traceback( 

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

170 ) -> None: 

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

172 nonlocal tb_data 

173 exc_tuple = ip._get_exc_info() 

174 

175 # do not display trace on syntax error 

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

177 

178 # determine correct tb_offset 

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

180 tb_offset = tb_data.get("tb_offset", 1 if compiled else 0) 

181 # remove ipython internal frames from trace with tb_offset 

182 for _ in range(tb_offset): 

183 if tb is None: 

184 break 

185 tb = tb.tb_next 

186 

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

188 tb_data = {} # clear data upon usage 

189 

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

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

192 ip._showtraceback = ipy_display_traceback 

193 # add wrapper to capture tb_data 

194 ip.showtraceback = ipy_show_traceback 

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

196 *args, is_syntax=True, **kwargs 

197 ) 

198 

199 try: # pragma: no cover 

200 # if within ipython, use customized traceback 

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

202 ipy_excepthook_closure(ip) 

203 return sys.excepthook 

204 except Exception: 

205 # otherwise use default system hook 

206 old_excepthook = sys.excepthook 

207 sys.excepthook = excepthook 

208 return old_excepthook 

209 

210 

211@dataclass 

212class Frame: 

213 filename: str 

214 lineno: int 

215 name: str 

216 line: str = "" 

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

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

219 

220 

221@dataclass 

222class _SyntaxError: 

223 offset: int 

224 filename: str 

225 line: str 

226 lineno: int 

227 msg: str 

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

229 

230 

231@dataclass 

232class Stack: 

233 exc_type: str 

234 exc_value: str 

235 syntax_error: Optional[_SyntaxError] = None 

236 is_cause: bool = False 

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

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

239 is_group: bool = False 

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

241 

242 

243@dataclass 

244class Trace: 

245 stacks: List[Stack] 

246 

247 

248class PathHighlighter(RegexHighlighter): 

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

250 

251 

252class Traceback: 

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

254 

255 Args: 

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

257 the last exception. 

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

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

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

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

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

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

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

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

266 Defaults to 10. 

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

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

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

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

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

272 

273 """ 

274 

275 LEXERS = { 

276 "": "text", 

277 ".py": "python", 

278 ".pxd": "cython", 

279 ".pyx": "cython", 

280 ".pxi": "pyrex", 

281 } 

282 

283 def __init__( 

284 self, 

285 trace: Optional[Trace] = None, 

286 *, 

287 width: Optional[int] = 100, 

288 code_width: Optional[int] = 88, 

289 extra_lines: int = 3, 

290 theme: Optional[str] = None, 

291 word_wrap: bool = False, 

292 show_locals: bool = False, 

293 locals_max_length: int = LOCALS_MAX_LENGTH, 

294 locals_max_string: int = LOCALS_MAX_STRING, 

295 locals_hide_dunder: bool = True, 

296 locals_hide_sunder: bool = False, 

297 indent_guides: bool = True, 

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

299 max_frames: int = 100, 

300 ): 

301 if trace is None: 

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

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

304 raise ValueError( 

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

306 ) 

307 trace = self.extract( 

308 exc_type, exc_value, traceback, show_locals=show_locals 

309 ) 

310 self.trace = trace 

311 self.width = width 

312 self.code_width = code_width 

313 self.extra_lines = extra_lines 

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

315 self.word_wrap = word_wrap 

316 self.show_locals = show_locals 

317 self.indent_guides = indent_guides 

318 self.locals_max_length = locals_max_length 

319 self.locals_max_string = locals_max_string 

320 self.locals_hide_dunder = locals_hide_dunder 

321 self.locals_hide_sunder = locals_hide_sunder 

322 

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

324 for suppress_entity in suppress: 

325 if not isinstance(suppress_entity, str): 

326 assert ( 

327 suppress_entity.__file__ is not None 

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

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

330 else: 

331 path = suppress_entity 

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

333 self.suppress.append(path) 

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

335 

336 @classmethod 

337 def from_exception( 

338 cls, 

339 exc_type: Type[Any], 

340 exc_value: BaseException, 

341 traceback: Optional[TracebackType], 

342 *, 

343 width: Optional[int] = 100, 

344 code_width: Optional[int] = 88, 

345 extra_lines: int = 3, 

346 theme: Optional[str] = None, 

347 word_wrap: bool = False, 

348 show_locals: bool = False, 

349 locals_max_length: int = LOCALS_MAX_LENGTH, 

350 locals_max_string: int = LOCALS_MAX_STRING, 

351 locals_hide_dunder: bool = True, 

352 locals_hide_sunder: bool = False, 

353 indent_guides: bool = True, 

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

355 max_frames: int = 100, 

356 ) -> "Traceback": 

357 """Create a traceback from exception info 

358 

359 Args: 

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

361 exc_value (BaseException): Exception value. 

362 traceback (TracebackType): Python Traceback object. 

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

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

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

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

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

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

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

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

371 Defaults to 10. 

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

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

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

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

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

377 

378 Returns: 

379 Traceback: A Traceback instance that may be printed. 

380 """ 

381 rich_traceback = cls.extract( 

382 exc_type, 

383 exc_value, 

384 traceback, 

385 show_locals=show_locals, 

386 locals_max_length=locals_max_length, 

387 locals_max_string=locals_max_string, 

388 locals_hide_dunder=locals_hide_dunder, 

389 locals_hide_sunder=locals_hide_sunder, 

390 ) 

391 

392 return cls( 

393 rich_traceback, 

394 width=width, 

395 code_width=code_width, 

396 extra_lines=extra_lines, 

397 theme=theme, 

398 word_wrap=word_wrap, 

399 show_locals=show_locals, 

400 indent_guides=indent_guides, 

401 locals_max_length=locals_max_length, 

402 locals_max_string=locals_max_string, 

403 locals_hide_dunder=locals_hide_dunder, 

404 locals_hide_sunder=locals_hide_sunder, 

405 suppress=suppress, 

406 max_frames=max_frames, 

407 ) 

408 

409 @classmethod 

410 def extract( 

411 cls, 

412 exc_type: Type[BaseException], 

413 exc_value: BaseException, 

414 traceback: Optional[TracebackType], 

415 *, 

416 show_locals: bool = False, 

417 locals_max_length: int = LOCALS_MAX_LENGTH, 

418 locals_max_string: int = LOCALS_MAX_STRING, 

419 locals_hide_dunder: bool = True, 

420 locals_hide_sunder: bool = False, 

421 ) -> Trace: 

422 """Extract traceback information. 

423 

424 Args: 

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

426 exc_value (BaseException): Exception value. 

427 traceback (TracebackType): Python Traceback object. 

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

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

430 Defaults to 10. 

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

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

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

434 

435 Returns: 

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

437 """ 

438 

439 stacks: List[Stack] = [] 

440 is_cause = False 

441 

442 from rich import _IMPORT_CWD 

443 

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

445 

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

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

448 try: 

449 return str(_object) 

450 except Exception: 

451 return "<exception str() failed>" 

452 

453 while True: 

454 stack = Stack( 

455 exc_type=safe_str(exc_type.__name__), 

456 exc_value=safe_str(exc_value), 

457 is_cause=is_cause, 

458 notes=notes, 

459 ) 

460 

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

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

463 stack.is_group = True 

464 for exception in exc_value.exceptions: 

465 stack.exceptions.append( 

466 Traceback.extract( 

467 type(exception), 

468 exception, 

469 exception.__traceback__, 

470 show_locals=show_locals, 

471 locals_max_length=locals_max_length, 

472 locals_hide_dunder=locals_hide_dunder, 

473 locals_hide_sunder=locals_hide_sunder, 

474 ) 

475 ) 

476 

477 if isinstance(exc_value, SyntaxError): 

478 stack.syntax_error = _SyntaxError( 

479 offset=exc_value.offset or 0, 

480 filename=exc_value.filename or "?", 

481 lineno=exc_value.lineno or 0, 

482 line=exc_value.text or "", 

483 msg=exc_value.msg, 

484 notes=notes, 

485 ) 

486 

487 stacks.append(stack) 

488 append = stack.frames.append 

489 

490 def get_locals( 

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

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

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

494 if not (locals_hide_dunder or locals_hide_sunder): 

495 yield from iter_locals 

496 return 

497 for key, value in iter_locals: 

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

499 continue 

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

501 continue 

502 yield key, value 

503 

504 for frame_summary, line_no in walk_tb(traceback): 

505 filename = frame_summary.f_code.co_filename 

506 

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

508 last_instruction = None 

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

510 instruction_index = frame_summary.f_lasti // 2 

511 instruction_position = next( 

512 islice( 

513 frame_summary.f_code.co_positions(), 

514 instruction_index, 

515 instruction_index + 1, 

516 ) 

517 ) 

518 ( 

519 start_line, 

520 end_line, 

521 start_column, 

522 end_column, 

523 ) = instruction_position 

524 if ( 

525 start_line is not None 

526 and end_line is not None 

527 and start_column is not None 

528 and end_column is not None 

529 ): 

530 last_instruction = ( 

531 (start_line, start_column), 

532 (end_line, end_column), 

533 ) 

534 

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

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

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

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

539 continue 

540 

541 frame = Frame( 

542 filename=filename or "?", 

543 lineno=line_no, 

544 name=frame_summary.f_code.co_name, 

545 locals=( 

546 { 

547 key: pretty.traverse( 

548 value, 

549 max_length=locals_max_length, 

550 max_string=locals_max_string, 

551 ) 

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

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

554 } 

555 if show_locals 

556 else None 

557 ), 

558 last_instruction=last_instruction, 

559 ) 

560 append(frame) 

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

562 del stack.frames[:] 

563 

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

565 if cause: 

566 exc_type = cause.__class__ 

567 exc_value = cause 

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

569 # 'multiprocessing' module 

570 traceback = cause.__traceback__ 

571 is_cause = True 

572 continue 

573 

574 cause = exc_value.__context__ 

575 if cause and not getattr(exc_value, "__suppress_context__", False): 

576 exc_type = cause.__class__ 

577 exc_value = cause 

578 traceback = cause.__traceback__ 

579 is_cause = False 

580 continue 

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

582 break # pragma: no cover 

583 

584 trace = Trace(stacks=stacks) 

585 

586 return trace 

587 

588 def __rich_console__( 

589 self, console: Console, options: ConsoleOptions 

590 ) -> RenderResult: 

591 theme = self.theme 

592 background_style = theme.get_background_style() 

593 token_style = theme.get_style_for_token 

594 

595 traceback_theme = Theme( 

596 { 

597 "pretty": token_style(TextToken), 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

613 }, 

614 inherit=False, 

615 ) 

616 

617 highlighter = ReprHighlighter() 

618 

619 @group() 

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

621 if stack.frames: 

622 stack_renderable: ConsoleRenderable = Panel( 

623 self._render_stack(stack), 

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

625 style=background_style, 

626 border_style="traceback.border", 

627 expand=True, 

628 padding=(0, 1), 

629 ) 

630 stack_renderable = Constrain(stack_renderable, self.width) 

631 with console.use_theme(traceback_theme): 

632 yield stack_renderable 

633 

634 if stack.syntax_error is not None: 

635 with console.use_theme(traceback_theme): 

636 yield Constrain( 

637 Panel( 

638 self._render_syntax_error(stack.syntax_error), 

639 style=background_style, 

640 border_style="traceback.border.syntax_error", 

641 expand=True, 

642 padding=(0, 1), 

643 width=self.width, 

644 ), 

645 self.width, 

646 ) 

647 yield Text.assemble( 

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

649 highlighter(stack.syntax_error.msg), 

650 ) 

651 elif stack.exc_value: 

652 yield Text.assemble( 

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

654 highlighter(stack.exc_value), 

655 ) 

656 else: 

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

658 

659 for note in stack.notes: 

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

661 

662 if stack.is_group: 

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

664 grouped_exceptions: List[Group] = [] 

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

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

667 yield "" 

668 yield Constrain( 

669 Panel( 

670 Group(*grouped_exceptions), 

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

672 border_style="traceback.group.border", 

673 ), 

674 self.width, 

675 ) 

676 

677 if not last: 

678 if stack.is_cause: 

679 yield Text.from_markup( 

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

681 ) 

682 else: 

683 yield Text.from_markup( 

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

685 ) 

686 

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

688 yield render_stack(stack, last) 

689 

690 @group() 

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

692 highlighter = ReprHighlighter() 

693 path_highlighter = PathHighlighter() 

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

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

696 text = Text.assemble( 

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

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

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

700 style="pygments.text", 

701 ) 

702 yield path_highlighter(text) 

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

704 syntax_error_text.no_wrap = True 

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

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

707 syntax_error_text += Text.from_markup( 

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

709 style="pygments.text", 

710 ) 

711 yield syntax_error_text 

712 

713 @classmethod 

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

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

716 if not ext: 

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

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

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

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

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

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

723 return "python" 

724 try: 

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

726 except ClassNotFound: 

727 return "text" 

728 

729 @group() 

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

731 path_highlighter = PathHighlighter() 

732 theme = self.theme 

733 

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

735 if frame.locals: 

736 yield render_scope( 

737 frame.locals, 

738 title="locals", 

739 indent_guides=self.indent_guides, 

740 max_length=self.locals_max_length, 

741 max_string=self.locals_max_string, 

742 ) 

743 

744 exclude_frames: Optional[range] = None 

745 if self.max_frames != 0: 

746 exclude_frames = range( 

747 self.max_frames // 2, 

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

749 ) 

750 

751 excluded = False 

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

753 if exclude_frames and frame_index in exclude_frames: 

754 excluded = True 

755 continue 

756 

757 if excluded: 

758 assert exclude_frames is not None 

759 yield Text( 

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

761 justify="center", 

762 style="traceback.error", 

763 ) 

764 excluded = False 

765 

766 first = frame_index == 0 

767 frame_filename = frame.filename 

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

769 

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

771 text = Text.assemble( 

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

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

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

775 " in ", 

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

777 style="pygments.text", 

778 ) 

779 else: 

780 text = Text.assemble( 

781 "in ", 

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

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

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

785 style="pygments.text", 

786 ) 

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

788 yield "" 

789 yield text 

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

791 yield from render_locals(frame) 

792 continue 

793 if not suppressed: 

794 try: 

795 code_lines = linecache.getlines(frame.filename) 

796 code = "".join(code_lines) 

797 if not code: 

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

799 # if the traceback filename is generated dynamically 

800 continue 

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

802 syntax = Syntax( 

803 code, 

804 lexer_name, 

805 theme=theme, 

806 line_numbers=True, 

807 line_range=( 

808 frame.lineno - self.extra_lines, 

809 frame.lineno + self.extra_lines, 

810 ), 

811 highlight_lines={frame.lineno}, 

812 word_wrap=self.word_wrap, 

813 code_width=self.code_width, 

814 indent_guides=self.indent_guides, 

815 dedent=False, 

816 ) 

817 yield "" 

818 except Exception as error: 

819 yield Text.assemble( 

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

821 ) 

822 else: 

823 if frame.last_instruction is not None: 

824 start, end = frame.last_instruction 

825 

826 # Stylize a line at a time 

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

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

829 try: 

830 if column1 == 0: 

831 line = code_lines[line1 - 1] 

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

833 if column2 == -1: 

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

835 except IndexError: 

836 # Being defensive here 

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

838 continue 

839 

840 syntax.stylize_range( 

841 style="traceback.error_range", 

842 start=(line1, column1), 

843 end=(line1, column2), 

844 ) 

845 yield ( 

846 Columns( 

847 [ 

848 syntax, 

849 *render_locals(frame), 

850 ], 

851 padding=1, 

852 ) 

853 if frame.locals 

854 else syntax 

855 ) 

856 

857 

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

859 install(show_locals=True) 

860 import sys 

861 

862 def bar( 

863 a: Any, 

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

865 one = 1 

866 print(one / a) 

867 

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

869 _rich_traceback_guard = True 

870 zed = { 

871 "characters": { 

872 "Paul Atreides", 

873 "Vladimir Harkonnen", 

874 "Thufir Hawat", 

875 "Duncan Idaho", 

876 }, 

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

878 } 

879 bar(a) 

880 

881 def error() -> None: 

882 foo(0) 

883 

884 error()