bpo-45075: distinguish between frame and FrameSummary in traceback mo… (GH-28112)
This commit is contained in:
parent
6f8bc464e0
commit
0b58e863df
@ -353,12 +353,12 @@ capture data for later printing in a lightweight fashion.
|
|||||||
.. versionchanged:: 3.6
|
.. versionchanged:: 3.6
|
||||||
Long sequences of repeated frames are now abbreviated.
|
Long sequences of repeated frames are now abbreviated.
|
||||||
|
|
||||||
.. method:: format_frame(frame)
|
.. method:: format_frame_summary(frame_summary)
|
||||||
|
|
||||||
Returns a string for printing one of the frames involved in the stack.
|
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
|
This method is called for each :class:`FrameSummary` object to be
|
||||||
:class:`StackSummary`. If it returns ``None``, the frame is omitted
|
printed by :meth:`StackSummary.format`. If it returns ``None``, the
|
||||||
from the output.
|
frame is omitted from the output.
|
||||||
|
|
||||||
.. versionadded:: 3.11
|
.. versionadded:: 3.11
|
||||||
|
|
||||||
@ -368,7 +368,7 @@ capture data for later printing in a lightweight fashion.
|
|||||||
|
|
||||||
.. versionadded:: 3.5
|
.. versionadded:: 3.5
|
||||||
|
|
||||||
:class:`FrameSummary` objects represent a single frame in a traceback.
|
A :class:`FrameSummary` object represents a single frame in a traceback.
|
||||||
|
|
||||||
.. class:: FrameSummary(filename, lineno, name, lookup_line=True, locals=None, line=None)
|
.. class:: FrameSummary(filename, lineno, name, lookup_line=True, locals=None, line=None)
|
||||||
|
|
||||||
|
@ -1515,8 +1515,8 @@ class TestStack(unittest.TestCase):
|
|||||||
|
|
||||||
def test_custom_format_frame(self):
|
def test_custom_format_frame(self):
|
||||||
class CustomStackSummary(traceback.StackSummary):
|
class CustomStackSummary(traceback.StackSummary):
|
||||||
def format_frame(self, frame):
|
def format_frame_summary(self, frame_summary):
|
||||||
return f'{frame.filename}:{frame.lineno}'
|
return f'{frame_summary.filename}:{frame_summary.lineno}'
|
||||||
|
|
||||||
def some_inner():
|
def some_inner():
|
||||||
return CustomStackSummary.extract(
|
return CustomStackSummary.extract(
|
||||||
@ -1540,10 +1540,10 @@ class TestStack(unittest.TestCase):
|
|||||||
exc_info = g()
|
exc_info = g()
|
||||||
|
|
||||||
class Skip_G(traceback.StackSummary):
|
class Skip_G(traceback.StackSummary):
|
||||||
def format_frame(self, frame):
|
def format_frame_summary(self, frame_summary):
|
||||||
if frame.name == 'g':
|
if frame_summary.name == 'g':
|
||||||
return None
|
return None
|
||||||
return super().format_frame(frame)
|
return super().format_frame_summary(frame_summary)
|
||||||
|
|
||||||
stack = Skip_G.extract(
|
stack = Skip_G.extract(
|
||||||
traceback.walk_tb(exc_info[2])).format()
|
traceback.walk_tb(exc_info[2])).format()
|
||||||
|
@ -240,7 +240,7 @@ def clear_frames(tb):
|
|||||||
|
|
||||||
|
|
||||||
class FrameSummary:
|
class FrameSummary:
|
||||||
"""A single frame from a traceback.
|
"""Information about a single frame from a traceback.
|
||||||
|
|
||||||
- :attr:`filename` The filename for the frame.
|
- :attr:`filename` The filename for the frame.
|
||||||
- :attr:`lineno` The line within filename for the frame that was
|
- :attr:`lineno` The line within filename for the frame that was
|
||||||
@ -365,15 +365,15 @@ def _get_code_position(code, instruction_index):
|
|||||||
_RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c.
|
_RECURSIVE_CUTOFF = 3 # Also hardcoded in traceback.c.
|
||||||
|
|
||||||
class StackSummary(list):
|
class StackSummary(list):
|
||||||
"""A stack of frames."""
|
"""A list of FrameSummary objects, representing a stack of frames."""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def extract(klass, frame_gen, *, limit=None, lookup_lines=True,
|
def extract(klass, frame_gen, *, limit=None, lookup_lines=True,
|
||||||
capture_locals=False):
|
capture_locals=False):
|
||||||
"""Create a StackSummary from a traceback or stack object.
|
"""Create a StackSummary from a traceback or stack object.
|
||||||
|
|
||||||
:param frame_gen: A generator that yields (frame, lineno) tuples to
|
:param frame_gen: A generator that yields (frame, lineno) tuples
|
||||||
include in the stack.
|
whose summaries are to be included in the stack.
|
||||||
:param limit: None to include all frames or the number of frames to
|
:param limit: None to include all frames or the number of frames to
|
||||||
include.
|
include.
|
||||||
:param lookup_lines: If True, lookup lines for each frame immediately,
|
:param lookup_lines: If True, lookup lines for each frame immediately,
|
||||||
@ -394,7 +394,7 @@ class StackSummary(list):
|
|||||||
lookup_lines=True, capture_locals=False):
|
lookup_lines=True, capture_locals=False):
|
||||||
# Same as extract but operates on a frame generator that yields
|
# Same as extract but operates on a frame generator that yields
|
||||||
# (frame, (lineno, end_lineno, colno, end_colno)) in the stack.
|
# (frame, (lineno, end_lineno, colno, end_colno)) in the stack.
|
||||||
# Only lineno is required, the remaining fields can be empty if the
|
# Only lineno is required, the remaining fields can be None if the
|
||||||
# information is not available.
|
# information is not available.
|
||||||
if limit is None:
|
if limit is None:
|
||||||
limit = getattr(sys, 'tracebacklimit', None)
|
limit = getattr(sys, 'tracebacklimit', None)
|
||||||
@ -450,34 +450,38 @@ class StackSummary(list):
|
|||||||
result.append(FrameSummary(filename, lineno, name, line=line))
|
result.append(FrameSummary(filename, lineno, name, line=line))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def format_frame(self, frame):
|
def format_frame_summary(self, frame_summary):
|
||||||
"""Format the lines for a single frame.
|
"""Format the lines for a single FrameSummary.
|
||||||
|
|
||||||
Returns a string representing one frame involved in the stack. This
|
Returns a string representing one frame involved in the stack. This
|
||||||
gets called for every frame to be printed in the stack summary.
|
gets called for every frame to be printed in the stack summary.
|
||||||
"""
|
"""
|
||||||
row = []
|
row = []
|
||||||
row.append(' File "{}", line {}, in {}\n'.format(
|
row.append(' File "{}", line {}, in {}\n'.format(
|
||||||
frame.filename, frame.lineno, frame.name))
|
frame_summary.filename, frame_summary.lineno, frame_summary.name))
|
||||||
if frame.line:
|
if frame_summary.line:
|
||||||
row.append(' {}\n'.format(frame.line.strip()))
|
row.append(' {}\n'.format(frame_summary.line.strip()))
|
||||||
|
|
||||||
stripped_characters = len(frame._original_line) - len(frame.line.lstrip())
|
orig_line_len = len(frame_summary._original_line)
|
||||||
|
frame_line_len = len(frame_summary.line.lstrip())
|
||||||
|
stripped_characters = orig_line_len - frame_line_len
|
||||||
if (
|
if (
|
||||||
frame.colno is not None
|
frame_summary.colno is not None
|
||||||
and frame.end_colno is not None
|
and frame_summary.end_colno is not None
|
||||||
):
|
):
|
||||||
colno = _byte_offset_to_character_offset(frame._original_line, frame.colno)
|
colno = _byte_offset_to_character_offset(
|
||||||
end_colno = _byte_offset_to_character_offset(frame._original_line, frame.end_colno)
|
frame_summary._original_line, frame_summary.colno)
|
||||||
|
end_colno = _byte_offset_to_character_offset(
|
||||||
|
frame_summary._original_line, frame_summary.end_colno)
|
||||||
|
|
||||||
anchors = None
|
anchors = None
|
||||||
if frame.lineno == frame.end_lineno:
|
if frame_summary.lineno == frame_summary.end_lineno:
|
||||||
with suppress(Exception):
|
with suppress(Exception):
|
||||||
anchors = _extract_caret_anchors_from_line_segment(
|
anchors = _extract_caret_anchors_from_line_segment(
|
||||||
frame._original_line[colno - 1:end_colno - 1]
|
frame_summary._original_line[colno - 1:end_colno - 1]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
end_colno = stripped_characters + len(frame.line.strip())
|
end_colno = stripped_characters + len(frame_summary.line.strip())
|
||||||
|
|
||||||
row.append(' ')
|
row.append(' ')
|
||||||
row.append(' ' * (colno - stripped_characters))
|
row.append(' ' * (colno - stripped_characters))
|
||||||
@ -491,8 +495,8 @@ class StackSummary(list):
|
|||||||
|
|
||||||
row.append('\n')
|
row.append('\n')
|
||||||
|
|
||||||
if frame.locals:
|
if frame_summary.locals:
|
||||||
for name, value in sorted(frame.locals.items()):
|
for name, value in sorted(frame_summary.locals.items()):
|
||||||
row.append(' {name} = {value}\n'.format(name=name, value=value))
|
row.append(' {name} = {value}\n'.format(name=name, value=value))
|
||||||
|
|
||||||
return ''.join(row)
|
return ''.join(row)
|
||||||
@ -514,27 +518,27 @@ class StackSummary(list):
|
|||||||
last_line = None
|
last_line = None
|
||||||
last_name = None
|
last_name = None
|
||||||
count = 0
|
count = 0
|
||||||
for frame in self:
|
for frame_summary in self:
|
||||||
formatted_frame = self.format_frame(frame)
|
formatted_frame = self.format_frame_summary(frame_summary)
|
||||||
if formatted_frame is None:
|
if formatted_frame is None:
|
||||||
continue
|
continue
|
||||||
if (last_file is None or last_file != frame.filename or
|
if (last_file is None or last_file != frame_summary.filename or
|
||||||
last_line is None or last_line != frame.lineno or
|
last_line is None or last_line != frame_summary.lineno or
|
||||||
last_name is None or last_name != frame.name):
|
last_name is None or last_name != frame_summary.name):
|
||||||
if count > _RECURSIVE_CUTOFF:
|
if count > _RECURSIVE_CUTOFF:
|
||||||
count -= _RECURSIVE_CUTOFF
|
count -= _RECURSIVE_CUTOFF
|
||||||
result.append(
|
result.append(
|
||||||
f' [Previous line repeated {count} more '
|
f' [Previous line repeated {count} more '
|
||||||
f'time{"s" if count > 1 else ""}]\n'
|
f'time{"s" if count > 1 else ""}]\n'
|
||||||
)
|
)
|
||||||
last_file = frame.filename
|
last_file = frame_summary.filename
|
||||||
last_line = frame.lineno
|
last_line = frame_summary.lineno
|
||||||
last_name = frame.name
|
last_name = frame_summary.name
|
||||||
count = 0
|
count = 0
|
||||||
count += 1
|
count += 1
|
||||||
if count > _RECURSIVE_CUTOFF:
|
if count > _RECURSIVE_CUTOFF:
|
||||||
continue
|
continue
|
||||||
result.append(self.format_frame(frame))
|
result.append(formatted_frame)
|
||||||
|
|
||||||
if count > _RECURSIVE_CUTOFF:
|
if count > _RECURSIVE_CUTOFF:
|
||||||
count -= _RECURSIVE_CUTOFF
|
count -= _RECURSIVE_CUTOFF
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
Rename :meth:`traceback.StackSummary.format_frame` to
|
||||||
|
:meth:`traceback.StackSummary.format_frame_summary`. This method was added
|
||||||
|
for 3.11 so it was not released yet.
|
||||||
|
|
||||||
|
Updated code and docs to better distinguish frame and FrameSummary.
|
Loading…
x
Reference in New Issue
Block a user