@@ -273,7 +273,7 @@ class FrameSummary:
273
273
"""
274
274
275
275
__slots__ = ('filename' , 'lineno' , 'end_lineno' , 'colno' , 'end_colno' ,
276
- 'name' , '_line ' , '_line_dedented ' , 'locals' )
276
+ 'name' , '_lines ' , '_lines_dedented ' , 'locals' )
277
277
278
278
def __init__ (self , filename , lineno , name , * , lookup_line = True ,
279
279
locals = None , line = None ,
@@ -293,15 +293,12 @@ def __init__(self, filename, lineno, name, *, lookup_line=True,
293
293
self .colno = colno
294
294
self .end_colno = end_colno
295
295
self .name = name
296
- self ._line = line
297
- self ._line_dedented = None
296
+ self ._lines = line
297
+ self ._lines_dedented = None
298
298
if lookup_line :
299
299
self .line
300
300
self .locals = {k : _safe_string (v , 'local' , func = repr )
301
301
for k , v in locals .items ()} if locals else None
302
- self .end_lineno = end_lineno
303
- self .colno = colno
304
- self .end_colno = end_colno
305
302
306
303
def __eq__ (self , other ):
307
304
if isinstance (other , FrameSummary ):
@@ -326,33 +323,39 @@ def __repr__(self):
326
323
def __len__ (self ):
327
324
return 4
328
325
326
+ def _set_lines (self ):
327
+ if (
328
+ self ._lines is None
329
+ and self .lineno is not None
330
+ and self .end_lineno is not None
331
+ ):
332
+ lines = []
333
+ for lineno in range (self .lineno , self .end_lineno + 1 ):
334
+ # treat errors (empty string) and empty lines (newline) as the same
335
+ lines .append (linecache .getline (self .filename , lineno ).rstrip ())
336
+ self ._lines = "\n " .join (lines ) + "\n "
337
+
329
338
@property
330
- def _original_line (self ):
339
+ def _original_lines (self ):
331
340
# Returns the line as-is from the source, without modifying whitespace.
332
- self .line
333
- return self ._line
341
+ self ._set_lines ()
342
+ return self ._lines
334
343
335
344
@property
336
345
def _dedented_lines (self ):
337
- # Returns _original_line, but dedented
338
- self .line
339
- if self ._line_dedented is None :
340
- if self ._line is not None :
341
- self ._line_dedented = textwrap .dedent (self ._line ).rstrip ()
342
- return self ._line_dedented
346
+ # Returns _original_lines, but dedented (and rstripped)
347
+ self ._set_lines ()
348
+ if self ._lines_dedented is None and self ._lines is not None :
349
+ self ._lines_dedented = textwrap .dedent (self ._lines ).rstrip ()
350
+ return self ._lines_dedented
343
351
344
352
@property
345
353
def line (self ):
346
- if self ._line is None :
347
- if self .lineno is None :
348
- return None
349
- end_lineno = self .lineno if self .end_lineno is None else self .end_lineno
350
- self ._line = ""
351
- for lineno in range (self .lineno , end_lineno + 1 ):
352
- # treat errors and empty lines as the same
353
- self ._line += linecache .getline (self .filename , lineno ).rstrip () + "\n "
354
- # return only the first line
355
- return self ._line .partition ("\n " )[0 ].strip ()
354
+ self ._set_lines ()
355
+ if self ._lines is None :
356
+ return None
357
+ # return only the first line, stripped
358
+ return self ._lines .partition ("\n " )[0 ].strip ()
356
359
357
360
358
361
def walk_stack (f ):
@@ -513,7 +516,7 @@ def format_frame_summary(self, frame_summary):
513
516
row .append (textwrap .indent (frame_summary .line , ' ' ) + "\n " )
514
517
else :
515
518
# get first and last line
516
- all_lines_original = frame_summary ._original_line .splitlines ()
519
+ all_lines_original = frame_summary ._original_lines .splitlines ()
517
520
first_line = all_lines_original [0 ]
518
521
last_line = all_lines_original [frame_summary .end_lineno - frame_summary .lineno ]
519
522
@@ -527,10 +530,8 @@ def format_frame_summary(self, frame_summary):
527
530
528
531
# adjust start/end offset based on dedent
529
532
dedent_characters = len (first_line ) - len (all_lines [0 ])
530
- start_offset -= dedent_characters
531
- end_offset -= dedent_characters
532
- start_offset = max (0 , start_offset )
533
- end_offset = max (0 , end_offset )
533
+ start_offset = max (0 , start_offset - dedent_characters )
534
+ end_offset = max (0 , end_offset - dedent_characters )
534
535
535
536
# When showing this on a terminal, some of the non-ASCII characters
536
537
# might be rendered as double-width characters, so we need to take
@@ -543,11 +544,9 @@ def format_frame_summary(self, frame_summary):
543
544
segment = segment [start_offset :len (segment ) - (len (all_lines [- 1 ]) - end_offset )]
544
545
545
546
# attempt to parse for anchors
546
- anchors : Optional [ _Anchors ] = None
547
- try :
547
+ anchors = None
548
+ with suppress ( Exception ) :
548
549
anchors = _extract_caret_anchors_from_line_segment (segment )
549
- except Exception :
550
- pass
551
550
552
551
# only use carets if there are anchors or the carets do not span all lines
553
552
show_carets = False
@@ -594,8 +593,8 @@ def format_frame_summary(self, frame_summary):
594
593
significant_lines .discard (- 1 )
595
594
significant_lines .discard (len (all_lines ))
596
595
597
- # output all_lines[lineno] along with carets
598
596
def output_line (lineno ):
597
+ """output all_lines[lineno] along with carets"""
599
598
result .append (all_lines [lineno ] + "\n " )
600
599
if not show_carets :
601
600
return
@@ -731,34 +730,35 @@ def _extract_caret_anchors_from_line_segment(segment):
731
730
732
731
lines = segment .splitlines ()
733
732
734
- # get character index given byte offset
735
733
def normalize (lineno , offset ):
734
+ """Get character index given byte offset"""
736
735
return _byte_offset_to_character_offset (lines [lineno ], offset )
737
736
738
- # Gets the next valid character index in `lines`, if
739
- # the current location is not valid. Handles empty lines.
740
737
def next_valid_char (lineno , col ):
738
+ """Gets the next valid character index in `lines`, if
739
+ the current location is not valid. Handles empty lines.
740
+ """
741
741
while lineno < len (lines ) and col >= len (lines [lineno ]):
742
742
col = 0
743
743
lineno += 1
744
744
assert lineno < len (lines ) and col < len (lines [lineno ])
745
745
return lineno , col
746
746
747
- # Get the next valid character index in `lines`.
748
747
def increment (lineno , col ):
748
+ """Get the next valid character index in `lines`."""
749
749
col += 1
750
750
lineno , col = next_valid_char (lineno , col )
751
751
return lineno , col
752
752
753
- # Get the next valid character at least on the next line
754
753
def nextline (lineno , col ):
754
+ """Get the next valid character at least on the next line"""
755
755
col = 0
756
756
lineno += 1
757
757
lineno , col = next_valid_char (lineno , col )
758
758
return lineno , col
759
759
760
- # Get the next valid non-"\#" character that satisfies the `stop` predicate
761
760
def increment_until (lineno , col , stop ):
761
+ """Get the next valid non-"\\ #" character that satisfies the `stop` predicate"""
762
762
while True :
763
763
ch = lines [lineno ][col ]
764
764
if ch in "\\ #" :
@@ -769,10 +769,11 @@ def increment_until(lineno, col, stop):
769
769
break
770
770
return lineno , col
771
771
772
- # Get the lineno/col position of the end of `expr`. If `force_valid` is True,
773
- # forces the position to be a valid character (e.g. if the position is beyond the
774
- # end of the line, move to the next line)
775
772
def setup_positions (expr , force_valid = True ):
773
+ """Get the lineno/col position of the end of `expr`. If `force_valid` is True,
774
+ forces the position to be a valid character (e.g. if the position is beyond the
775
+ end of the line, move to the next line)
776
+ """
776
777
# -2 since end_lineno is 1-indexed and because we added an extra
777
778
# bracket + newline to `segment` when calling ast.parse
778
779
lineno = expr .end_lineno - 2
@@ -843,20 +844,18 @@ def _display_width(line, offset=None):
843
844
code segment might take if it were to be displayed on a fixed
844
845
width output device. Supports wide unicode characters and emojis."""
845
846
847
+ if offset is None :
848
+ offset = len (line )
849
+
846
850
# Fast track for ASCII-only strings
847
851
if line .isascii ():
848
- if offset is None :
849
- return len (line )
850
852
return offset
851
853
852
854
import unicodedata
853
855
854
- if offset is not None :
855
- line = line [:offset ]
856
-
857
856
return sum (
858
857
2 if unicodedata .east_asian_width (char ) in _WIDE_CHAR_SPECIFIERS else 1
859
- for char in line
858
+ for char in line [: offset ]
860
859
)
861
860
862
861
0 commit comments