11from __future__ import absolute_import
22
3+ import linecache
34import os
45import platform
56import sys
67from dataclasses import dataclass , field
7- from pathlib import Path
88from traceback import walk_tb
99from types import ModuleType , TracebackType
10- from typing import Any , Callable , Dict , Iterable , List , Optional , Sequence , Type , Union
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+ )
1122
1223from pygments .lexers import guess_lexer_for_filename
1324from pygments .token import Comment , Keyword , Name , Number , Operator , String
@@ -42,6 +53,10 @@ def install(
4253 theme : Optional [str ] = None ,
4354 word_wrap : bool = False ,
4455 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 ,
4560 indent_guides : bool = True ,
4661 suppress : Iterable [Union [str , ModuleType ]] = (),
4762 max_frames : int = 100 ,
@@ -59,6 +74,11 @@ def install(
5974 a theme appropriate for the platform.
6075 word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False.
6176 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.
6282 indent_guides (bool, optional): Enable indent guides in code and locals. Defaults to True.
6383 suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
6484
@@ -68,6 +88,12 @@ def install(
6888 """
6989 traceback_console = Console (file = sys .stderr ) if console is None else console
7090
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+
7197 def excepthook (
7298 type_ : Type [BaseException ],
7399 value : BaseException ,
@@ -83,6 +109,10 @@ def excepthook(
83109 theme = theme ,
84110 word_wrap = word_wrap ,
85111 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 ),
86116 indent_guides = indent_guides ,
87117 suppress = suppress ,
88118 max_frames = max_frames ,
@@ -193,6 +223,8 @@ class Traceback:
193223 locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
194224 Defaults to 10.
195225 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.
196228 suppress (Sequence[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
197229 max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
198230
@@ -209,14 +241,17 @@ class Traceback:
209241 def __init__ (
210242 self ,
211243 trace : Optional [Trace ] = None ,
244+ * ,
212245 width : Optional [int ] = 100 ,
213246 extra_lines : int = 3 ,
214247 theme : Optional [str ] = None ,
215248 word_wrap : bool = False ,
216249 show_locals : bool = False ,
217- indent_guides : bool = True ,
218250 locals_max_length : int = LOCALS_MAX_LENGTH ,
219251 locals_max_string : int = LOCALS_MAX_STRING ,
252+ locals_hide_dunder : bool = True ,
253+ locals_hide_sunder : bool = False ,
254+ indent_guides : bool = True ,
220255 suppress : Iterable [Union [str , ModuleType ]] = (),
221256 max_frames : int = 100 ,
222257 ):
@@ -238,6 +273,8 @@ def __init__(
238273 self .indent_guides = indent_guides
239274 self .locals_max_length = locals_max_length
240275 self .locals_max_string = locals_max_string
276+ self .locals_hide_dunder = locals_hide_dunder
277+ self .locals_hide_sunder = locals_hide_sunder
241278
242279 self .suppress : Sequence [str ] = []
243280 for suppress_entity in suppress :
@@ -258,14 +295,17 @@ def from_exception(
258295 exc_type : Type [Any ],
259296 exc_value : BaseException ,
260297 traceback : Optional [TracebackType ],
298+ * ,
261299 width : Optional [int ] = 100 ,
262300 extra_lines : int = 3 ,
263301 theme : Optional [str ] = None ,
264302 word_wrap : bool = False ,
265303 show_locals : bool = False ,
266- indent_guides : bool = True ,
267304 locals_max_length : int = LOCALS_MAX_LENGTH ,
268305 locals_max_string : int = LOCALS_MAX_STRING ,
306+ locals_hide_dunder : bool = True ,
307+ locals_hide_sunder : bool = False ,
308+ indent_guides : bool = True ,
269309 suppress : Iterable [Union [str , ModuleType ]] = (),
270310 max_frames : int = 100 ,
271311 ) -> "Traceback" :
@@ -284,6 +324,8 @@ def from_exception(
284324 locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
285325 Defaults to 10.
286326 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.
287329 suppress (Iterable[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback.
288330 max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100.
289331
@@ -297,6 +339,8 @@ def from_exception(
297339 show_locals = show_locals ,
298340 locals_max_length = locals_max_length ,
299341 locals_max_string = locals_max_string ,
342+ locals_hide_dunder = locals_hide_dunder ,
343+ locals_hide_sunder = locals_hide_sunder ,
300344 )
301345 return cls (
302346 rich_traceback ,
@@ -308,6 +352,8 @@ def from_exception(
308352 indent_guides = indent_guides ,
309353 locals_max_length = locals_max_length ,
310354 locals_max_string = locals_max_string ,
355+ locals_hide_dunder = locals_hide_dunder ,
356+ locals_hide_sunder = locals_hide_sunder ,
311357 suppress = suppress ,
312358 max_frames = max_frames ,
313359 )
@@ -318,9 +364,12 @@ def extract(
318364 exc_type : Type [BaseException ],
319365 exc_value : BaseException ,
320366 traceback : Optional [TracebackType ],
367+ * ,
321368 show_locals : bool = False ,
322369 locals_max_length : int = LOCALS_MAX_LENGTH ,
323370 locals_max_string : int = LOCALS_MAX_STRING ,
371+ locals_hide_dunder : bool = True ,
372+ locals_hide_sunder : bool = False ,
324373 ) -> Trace :
325374 """Extract traceback information.
326375
@@ -332,6 +381,8 @@ def extract(
332381 locals_max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
333382 Defaults to 10.
334383 locals_max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to 80.
384+ locals_hide_dunder (bool, optional): Hide locals prefixed with double underscore. Defaults to True.
385+ locals_hide_sunder (bool, optional): Hide locals prefixed with single underscore. Defaults to False.
335386
336387 Returns:
337388 Trace: A Trace instance which you can use to construct a `Traceback`.
@@ -368,13 +419,28 @@ def safe_str(_object: Any) -> str:
368419 stacks .append (stack )
369420 append = stack .frames .append
370421
422+ def get_locals (
423+ iter_locals : Iterable [Tuple [str , object ]]
424+ ) -> Iterable [Tuple [str , object ]]:
425+ """Extract locals from an iterator of key pairs."""
426+ if not (locals_hide_dunder or locals_hide_sunder ):
427+ yield from iter_locals
428+ return
429+ for key , value in iter_locals :
430+ if locals_hide_dunder and key .startswith ("__" ):
431+ continue
432+ if locals_hide_sunder and key .startswith ("_" ):
433+ continue
434+ yield key , value
435+
371436 for frame_summary , line_no in walk_tb (traceback ):
372437 filename = frame_summary .f_code .co_filename
373438 if filename and not filename .startswith ("<" ):
374439 if not os .path .isabs (filename ):
375440 filename = os .path .join (_IMPORT_CWD , filename )
376441 if frame_summary .f_locals .get ("_rich_traceback_omit" , False ):
377442 continue
443+
378444 frame = Frame (
379445 filename = filename or "?" ,
380446 lineno = line_no ,
@@ -385,7 +451,7 @@ def safe_str(_object: Any) -> str:
385451 max_length = locals_max_length ,
386452 max_string = locals_max_string ,
387453 )
388- for key , value in frame_summary .f_locals .items ()
454+ for key , value in get_locals ( frame_summary .f_locals .items () )
389455 }
390456 if show_locals
391457 else None ,
@@ -500,13 +566,14 @@ def _render_syntax_error(self, syntax_error: _SyntaxError) -> RenderResult:
500566 highlighter = ReprHighlighter ()
501567 path_highlighter = PathHighlighter ()
502568 if syntax_error .filename != "<stdin>" :
503- text = Text .assemble (
504- (f" { syntax_error .filename } " , "pygments.string" ),
505- (":" , "pygments.text" ),
506- (str (syntax_error .lineno ), "pygments.number" ),
507- style = "pygments.text" ,
508- )
509- yield path_highlighter (text )
569+ if os .path .exists (syntax_error .filename ):
570+ text = Text .assemble (
571+ (f" { syntax_error .filename } " , "pygments.string" ),
572+ (":" , "pygments.text" ),
573+ (str (syntax_error .lineno ), "pygments.number" ),
574+ style = "pygments.text" ,
575+ )
576+ yield path_highlighter (text )
510577 syntax_error_text = highlighter (syntax_error .line .rstrip ())
511578 syntax_error_text .no_wrap = True
512579 offset = min (syntax_error .offset - 1 , len (syntax_error_text ))
@@ -537,7 +604,6 @@ def _guess_lexer(cls, filename: str, code: str) -> str:
537604 def _render_stack (self , stack : Stack ) -> RenderResult :
538605 path_highlighter = PathHighlighter ()
539606 theme = self .theme
540- code_cache : Dict [str , str ] = {}
541607
542608 def read_code (filename : str ) -> str :
543609 """Read files, and cache results on filename.
@@ -548,11 +614,7 @@ def read_code(filename: str) -> str:
548614 Returns:
549615 str: Contents of file
550616 """
551- code = code_cache .get (filename )
552- if code is None :
553- code = Path (filename ).read_text (encoding = "utf-8" , errors = "replace" )
554- code_cache [filename ] = code
555- return code
617+ return "" .join (linecache .getlines (filename ))
556618
557619 def render_locals (frame : Frame ) -> Iterable [ConsoleRenderable ]:
558620 if frame .locals :
@@ -591,14 +653,17 @@ def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]:
591653 frame_filename = frame .filename
592654 suppressed = any (frame_filename .startswith (path ) for path in self .suppress )
593655
594- text = Text .assemble (
595- path_highlighter (Text (frame .filename , style = "pygments.string" )),
596- (":" , "pygments.text" ),
597- (str (frame .lineno ), "pygments.number" ),
598- " in " ,
599- (frame .name , "pygments.function" ),
600- style = "pygments.text" ,
601- )
656+ if os .path .exists (frame .filename ):
657+ text = Text .assemble (
658+ path_highlighter (Text (frame .filename , style = "pygments.string" )),
659+ (":" , "pygments.text" ),
660+ (str (frame .lineno ), "pygments.number" ),
661+ " in " ,
662+ (frame .name , "pygments.function" ),
663+ style = "pygments.text" ,
664+ )
665+ else :
666+ text = Text .assemble ("in " , (frame .name , "pygments.function" ))
602667 if not frame .filename .startswith ("<" ) and not first :
603668 yield ""
604669 yield text
0 commit comments