mirror of https://github.com/python/cpython
bpo-44569: Decouple frame formatting in traceback.py (GH-27038)
This commit is contained in:
parent
a283ef116b
commit
8ce3008585
|
@ -353,6 +353,14 @@ capture data for later printing in a lightweight fashion.
|
|||
.. versionchanged:: 3.6
|
||||
Long sequences of repeated frames are now abbreviated.
|
||||
|
||||
.. method:: format_frame(frame)
|
||||
|
||||
Returns a string for printing one of the frames involved in the stack.
|
||||
This method gets called for each frame object to be printed in the
|
||||
:class:`StackSummary`.
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
|
||||
:class:`FrameSummary` Objects
|
||||
-----------------------------
|
||||
|
|
|
@ -1429,6 +1429,21 @@ class TestStack(unittest.TestCase):
|
|||
' v = 4\n' % (__file__, some_inner.__code__.co_firstlineno + 3)
|
||||
], s.format())
|
||||
|
||||
def test_custom_format_frame(self):
|
||||
class CustomStackSummary(traceback.StackSummary):
|
||||
def format_frame(self, frame):
|
||||
return f'{frame.filename}:{frame.lineno}'
|
||||
|
||||
def some_inner():
|
||||
return CustomStackSummary.extract(
|
||||
traceback.walk_stack(None), limit=1)
|
||||
|
||||
s = some_inner()
|
||||
self.assertEqual(
|
||||
s.format(),
|
||||
[f'{__file__}:{some_inner.__code__.co_firstlineno + 1}'])
|
||||
|
||||
|
||||
class TestTracebackException(unittest.TestCase):
|
||||
|
||||
def test_smoke(self):
|
||||
|
|
|
@ -449,6 +449,48 @@ class StackSummary(list):
|
|||
result.append(FrameSummary(filename, lineno, name, line=line))
|
||||
return result
|
||||
|
||||
def format_frame(self, frame):
|
||||
"""Format the lines for a single frame.
|
||||
|
||||
Returns a string representing one frame involved in the stack. This
|
||||
gets called for every frame to be printed in the stack summary.
|
||||
"""
|
||||
row = []
|
||||
row.append(' File "{}", line {}, in {}\n'.format(
|
||||
frame.filename, frame.lineno, frame.name))
|
||||
if frame.line:
|
||||
row.append(' {}\n'.format(frame.line.strip()))
|
||||
|
||||
stripped_characters = len(frame._original_line) - len(frame.line.lstrip())
|
||||
if frame.end_lineno == frame.lineno and frame.end_colno != 0:
|
||||
colno = _byte_offset_to_character_offset(frame._original_line, frame.colno)
|
||||
end_colno = _byte_offset_to_character_offset(frame._original_line, frame.end_colno)
|
||||
|
||||
try:
|
||||
anchors = _extract_caret_anchors_from_line_segment(
|
||||
frame._original_line[colno - 1:end_colno - 1]
|
||||
)
|
||||
except Exception:
|
||||
anchors = None
|
||||
|
||||
row.append(' ')
|
||||
row.append(' ' * (colno - stripped_characters))
|
||||
|
||||
if anchors:
|
||||
row.append(anchors.primary_char * (anchors.left_end_offset))
|
||||
row.append(anchors.secondary_char * (anchors.right_start_offset - anchors.left_end_offset))
|
||||
row.append(anchors.primary_char * (end_colno - colno - anchors.right_start_offset))
|
||||
else:
|
||||
row.append('^' * (end_colno - colno))
|
||||
|
||||
row.append('\n')
|
||||
|
||||
if frame.locals:
|
||||
for name, value in sorted(frame.locals.items()):
|
||||
row.append(' {name} = {value}\n'.format(name=name, value=value))
|
||||
|
||||
return ''.join(row)
|
||||
|
||||
def format(self):
|
||||
"""Format the stack ready for printing.
|
||||
|
||||
|
@ -483,40 +525,8 @@ class StackSummary(list):
|
|||
count += 1
|
||||
if count > _RECURSIVE_CUTOFF:
|
||||
continue
|
||||
row = []
|
||||
row.append(' File "{}", line {}, in {}\n'.format(
|
||||
frame.filename, frame.lineno, frame.name))
|
||||
if frame.line:
|
||||
row.append(' {}\n'.format(frame.line.strip()))
|
||||
result.append(self.format_frame(frame))
|
||||
|
||||
stripped_characters = len(frame._original_line) - len(frame.line.lstrip())
|
||||
if frame.end_lineno == frame.lineno and frame.end_colno != 0:
|
||||
colno = _byte_offset_to_character_offset(frame._original_line, frame.colno)
|
||||
end_colno = _byte_offset_to_character_offset(frame._original_line, frame.end_colno)
|
||||
|
||||
try:
|
||||
anchors = _extract_caret_anchors_from_line_segment(
|
||||
frame._original_line[colno - 1:end_colno - 1]
|
||||
)
|
||||
except Exception:
|
||||
anchors = None
|
||||
|
||||
row.append(' ')
|
||||
row.append(' ' * (colno - stripped_characters))
|
||||
|
||||
if anchors:
|
||||
row.append(anchors.primary_char * (anchors.left_end_offset))
|
||||
row.append(anchors.secondary_char * (anchors.right_start_offset - anchors.left_end_offset))
|
||||
row.append(anchors.primary_char * (end_colno - colno - anchors.right_start_offset))
|
||||
else:
|
||||
row.append('^' * (end_colno - colno))
|
||||
|
||||
row.append('\n')
|
||||
|
||||
if frame.locals:
|
||||
for name, value in sorted(frame.locals.items()):
|
||||
row.append(' {name} = {value}\n'.format(name=name, value=value))
|
||||
result.append(''.join(row))
|
||||
if count > _RECURSIVE_CUTOFF:
|
||||
count -= _RECURSIVE_CUTOFF
|
||||
result.append(
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Added the :func:`StackSummary.format_frame` function in :mod:`traceback`.
|
||||
This allows users to customize the way individual lines are formatted in
|
||||
tracebacks without re-implementing logic to handle recursive tracebacks.
|
Loading…
Reference in New Issue