Skip to content

Commit 425c874

Browse files
committed
address pr comments
1 parent 3152844 commit 425c874

File tree

2 files changed

+51
-52
lines changed

2 files changed

+51
-52
lines changed

Lib/test/test_traceback.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ def f_with_binary_operator():
797797
' callable()\n'
798798
' ~~~~~~~~^^\n'
799799
f' File "{__file__}", line {lineno_f+3}, in f_with_binary_operator\n'
800-
' a = b \\\n'
800+
' a = b \\\n'
801801
' ~~~~~~\n'
802802
' +\\\n'
803803
' ^~\n'
@@ -2914,7 +2914,7 @@ def test_basics(self):
29142914
def test_lazy_lines(self):
29152915
linecache.clearcache()
29162916
f = traceback.FrameSummary("f", 1, "dummy", lookup_line=False)
2917-
self.assertEqual(None, f._line)
2917+
self.assertEqual(None, f._lines)
29182918
linecache.lazycache("f", globals())
29192919
self.assertEqual(
29202920
'"""Test cases for traceback module"""',

Lib/traceback.py

Lines changed: 49 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ class FrameSummary:
273273
"""
274274

275275
__slots__ = ('filename', 'lineno', 'end_lineno', 'colno', 'end_colno',
276-
'name', '_line', '_line_dedented', 'locals')
276+
'name', '_lines', '_lines_dedented', 'locals')
277277

278278
def __init__(self, filename, lineno, name, *, lookup_line=True,
279279
locals=None, line=None,
@@ -293,15 +293,12 @@ def __init__(self, filename, lineno, name, *, lookup_line=True,
293293
self.colno = colno
294294
self.end_colno = end_colno
295295
self.name = name
296-
self._line = line
297-
self._line_dedented = None
296+
self._lines = line
297+
self._lines_dedented = None
298298
if lookup_line:
299299
self.line
300300
self.locals = {k: _safe_string(v, 'local', func=repr)
301301
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
305302

306303
def __eq__(self, other):
307304
if isinstance(other, FrameSummary):
@@ -326,33 +323,39 @@ def __repr__(self):
326323
def __len__(self):
327324
return 4
328325

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+
329338
@property
330-
def _original_line(self):
339+
def _original_lines(self):
331340
# 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
334343

335344
@property
336345
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
343351

344352
@property
345353
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()
356359

357360

358361
def walk_stack(f):
@@ -513,7 +516,7 @@ def format_frame_summary(self, frame_summary):
513516
row.append(textwrap.indent(frame_summary.line, ' ') + "\n")
514517
else:
515518
# get first and last line
516-
all_lines_original = frame_summary._original_line.splitlines()
519+
all_lines_original = frame_summary._original_lines.splitlines()
517520
first_line = all_lines_original[0]
518521
last_line = all_lines_original[frame_summary.end_lineno - frame_summary.lineno]
519522

@@ -527,10 +530,8 @@ def format_frame_summary(self, frame_summary):
527530

528531
# adjust start/end offset based on dedent
529532
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)
534535

535536
# When showing this on a terminal, some of the non-ASCII characters
536537
# might be rendered as double-width characters, so we need to take
@@ -543,11 +544,9 @@ def format_frame_summary(self, frame_summary):
543544
segment = segment[start_offset:len(segment) - (len(all_lines[-1]) - end_offset)]
544545

545546
# attempt to parse for anchors
546-
anchors: Optional[_Anchors] = None
547-
try:
547+
anchors = None
548+
with suppress(Exception):
548549
anchors = _extract_caret_anchors_from_line_segment(segment)
549-
except Exception:
550-
pass
551550

552551
# only use carets if there are anchors or the carets do not span all lines
553552
show_carets = False
@@ -594,8 +593,8 @@ def format_frame_summary(self, frame_summary):
594593
significant_lines.discard(-1)
595594
significant_lines.discard(len(all_lines))
596595

597-
# output all_lines[lineno] along with carets
598596
def output_line(lineno):
597+
"""output all_lines[lineno] along with carets"""
599598
result.append(all_lines[lineno] + "\n")
600599
if not show_carets:
601600
return
@@ -731,34 +730,35 @@ def _extract_caret_anchors_from_line_segment(segment):
731730

732731
lines = segment.splitlines()
733732

734-
# get character index given byte offset
735733
def normalize(lineno, offset):
734+
"""Get character index given byte offset"""
736735
return _byte_offset_to_character_offset(lines[lineno], offset)
737736

738-
# Gets the next valid character index in `lines`, if
739-
# the current location is not valid. Handles empty lines.
740737
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+
"""
741741
while lineno < len(lines) and col >= len(lines[lineno]):
742742
col = 0
743743
lineno += 1
744744
assert lineno < len(lines) and col < len(lines[lineno])
745745
return lineno, col
746746

747-
# Get the next valid character index in `lines`.
748747
def increment(lineno, col):
748+
"""Get the next valid character index in `lines`."""
749749
col += 1
750750
lineno, col = next_valid_char(lineno, col)
751751
return lineno, col
752752

753-
# Get the next valid character at least on the next line
754753
def nextline(lineno, col):
754+
"""Get the next valid character at least on the next line"""
755755
col = 0
756756
lineno += 1
757757
lineno, col = next_valid_char(lineno, col)
758758
return lineno, col
759759

760-
# Get the next valid non-"\#" character that satisfies the `stop` predicate
761760
def increment_until(lineno, col, stop):
761+
"""Get the next valid non-"\\#" character that satisfies the `stop` predicate"""
762762
while True:
763763
ch = lines[lineno][col]
764764
if ch in "\\#":
@@ -769,10 +769,11 @@ def increment_until(lineno, col, stop):
769769
break
770770
return lineno, col
771771

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)
775772
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+
"""
776777
# -2 since end_lineno is 1-indexed and because we added an extra
777778
# bracket + newline to `segment` when calling ast.parse
778779
lineno = expr.end_lineno - 2
@@ -843,20 +844,18 @@ def _display_width(line, offset=None):
843844
code segment might take if it were to be displayed on a fixed
844845
width output device. Supports wide unicode characters and emojis."""
845846

847+
if offset is None:
848+
offset = len(line)
849+
846850
# Fast track for ASCII-only strings
847851
if line.isascii():
848-
if offset is None:
849-
return len(line)
850852
return offset
851853

852854
import unicodedata
853855

854-
if offset is not None:
855-
line = line[:offset]
856-
857856
return sum(
858857
2 if unicodedata.east_asian_width(char) in _WIDE_CHAR_SPECIFIERS else 1
859-
for char in line
858+
for char in line[:offset]
860859
)
861860

862861

0 commit comments

Comments
 (0)