[Python-checkins] bpo-45075: distinguish between frame and FrameSummary in traceback mo… (GH-28112)

iritkatriel webhook-mailer at python.org
Fri Sep 3 17:39:32 EDT 2021


https://github.com/python/cpython/commit/0b58e863df9970b290a4de90c67f9ac30c443817
commit: 0b58e863df9970b290a4de90c67f9ac30c443817
branch: main
author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com>
committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com>
date: 2021-09-03T22:39:23+01:00
summary:

bpo-45075: distinguish between frame and FrameSummary in traceback mo… (GH-28112)

files:
A Misc/NEWS.d/next/Library/2021-09-01-15-27-00.bpo-45075.9xUpvt.rst
M Doc/library/traceback.rst
M Lib/test/test_traceback.py
M Lib/traceback.py

diff --git a/Doc/library/traceback.rst b/Doc/library/traceback.rst
index 7b230495b7b56..df4a38c955511 100644
--- a/Doc/library/traceback.rst
+++ b/Doc/library/traceback.rst
@@ -353,12 +353,12 @@ capture data for later printing in a lightweight fashion.
       .. versionchanged:: 3.6
          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.
-      This method gets called for each frame object to be printed in the
-      :class:`StackSummary`. If it returns ``None``, the frame is omitted
-      from the output.
+      This method is called for each :class:`FrameSummary` object to be
+      printed by :meth:`StackSummary.format`. If it returns ``None``, the
+      frame is omitted from the output.
 
       .. versionadded:: 3.11
 
@@ -368,7 +368,7 @@ capture data for later printing in a lightweight fashion.
 
 .. 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)
 
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index ee2896c5f4867..949adefd76faa 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -1515,8 +1515,8 @@ def some_inner(k, v):
 
     def test_custom_format_frame(self):
         class CustomStackSummary(traceback.StackSummary):
-            def format_frame(self, frame):
-                return f'{frame.filename}:{frame.lineno}'
+            def format_frame_summary(self, frame_summary):
+                return f'{frame_summary.filename}:{frame_summary.lineno}'
 
         def some_inner():
             return CustomStackSummary.extract(
@@ -1540,10 +1540,10 @@ def g():
          exc_info = g()
 
          class Skip_G(traceback.StackSummary):
-             def format_frame(self, frame):
-                 if frame.name == 'g':
+             def format_frame_summary(self, frame_summary):
+                 if frame_summary.name == 'g':
                      return None
-                 return super().format_frame(frame)
+                 return super().format_frame_summary(frame_summary)
 
          stack = Skip_G.extract(
              traceback.walk_tb(exc_info[2])).format()
diff --git a/Lib/traceback.py b/Lib/traceback.py
index 8d83fd9b517e6..d51c2010005b7 100644
--- a/Lib/traceback.py
+++ b/Lib/traceback.py
@@ -240,7 +240,7 @@ def clear_frames(tb):
 
 
 class FrameSummary:
-    """A single frame from a traceback.
+    """Information about a single frame from a traceback.
 
     - :attr:`filename` The filename for the frame.
     - :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.
 
 class StackSummary(list):
-    """A stack of frames."""
+    """A list of FrameSummary objects, representing a stack of frames."""
 
     @classmethod
     def extract(klass, frame_gen, *, limit=None, lookup_lines=True,
             capture_locals=False):
         """Create a StackSummary from a traceback or stack object.
 
-        :param frame_gen: A generator that yields (frame, lineno) tuples to
-            include in the stack.
+        :param frame_gen: A generator that yields (frame, lineno) tuples
+            whose summaries are to be included in the stack.
         :param limit: None to include all frames or the number of frames to
             include.
         :param lookup_lines: If True, lookup lines for each frame immediately,
@@ -394,7 +394,7 @@ def _extract_from_extended_frame_gen(klass, frame_gen, *, limit=None,
             lookup_lines=True, capture_locals=False):
         # Same as extract but operates on a frame generator that yields
         # (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.
         if limit is None:
             limit = getattr(sys, 'tracebacklimit', None)
@@ -450,34 +450,38 @@ def from_list(klass, a_list):
                 result.append(FrameSummary(filename, lineno, name, line=line))
         return result
 
-    def format_frame(self, frame):
-        """Format the lines for a single frame.
+    def format_frame_summary(self, frame_summary):
+        """Format the lines for a single FrameSummary.
 
         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()))
+            frame_summary.filename, frame_summary.lineno, frame_summary.name))
+        if frame_summary.line:
+            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 (
-                frame.colno is not None
-                and frame.end_colno is not None
+                frame_summary.colno is not None
+                and frame_summary.end_colno is not None
             ):
-                colno = _byte_offset_to_character_offset(frame._original_line, frame.colno)
-                end_colno = _byte_offset_to_character_offset(frame._original_line, frame.end_colno)
+                colno = _byte_offset_to_character_offset(
+                    frame_summary._original_line, frame_summary.colno)
+                end_colno = _byte_offset_to_character_offset(
+                    frame_summary._original_line, frame_summary.end_colno)
 
                 anchors = None
-                if frame.lineno == frame.end_lineno:
+                if frame_summary.lineno == frame_summary.end_lineno:
                     with suppress(Exception):
                         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:
-                    end_colno = stripped_characters + len(frame.line.strip())
+                    end_colno = stripped_characters + len(frame_summary.line.strip())
 
                 row.append('    ')
                 row.append(' ' * (colno - stripped_characters))
@@ -491,8 +495,8 @@ def format_frame(self, frame):
 
                 row.append('\n')
 
-        if frame.locals:
-            for name, value in sorted(frame.locals.items()):
+        if frame_summary.locals:
+            for name, value in sorted(frame_summary.locals.items()):
                 row.append('    {name} = {value}\n'.format(name=name, value=value))
 
         return ''.join(row)
@@ -514,27 +518,27 @@ def format(self):
         last_line = None
         last_name = None
         count = 0
-        for frame in self:
-            formatted_frame = self.format_frame(frame)
+        for frame_summary in self:
+            formatted_frame = self.format_frame_summary(frame_summary)
             if formatted_frame is None:
                 continue
-            if (last_file is None or last_file != frame.filename or
-                last_line is None or last_line != frame.lineno or
-                last_name is None or last_name != frame.name):
+            if (last_file is None or last_file != frame_summary.filename or
+                last_line is None or last_line != frame_summary.lineno or
+                last_name is None or last_name != frame_summary.name):
                 if count > _RECURSIVE_CUTOFF:
                     count -= _RECURSIVE_CUTOFF
                     result.append(
                         f'  [Previous line repeated {count} more '
                         f'time{"s" if count > 1 else ""}]\n'
                     )
-                last_file = frame.filename
-                last_line = frame.lineno
-                last_name = frame.name
+                last_file = frame_summary.filename
+                last_line = frame_summary.lineno
+                last_name = frame_summary.name
                 count = 0
             count += 1
             if count > _RECURSIVE_CUTOFF:
                 continue
-            result.append(self.format_frame(frame))
+            result.append(formatted_frame)
 
         if count > _RECURSIVE_CUTOFF:
             count -= _RECURSIVE_CUTOFF
diff --git a/Misc/NEWS.d/next/Library/2021-09-01-15-27-00.bpo-45075.9xUpvt.rst b/Misc/NEWS.d/next/Library/2021-09-01-15-27-00.bpo-45075.9xUpvt.rst
new file mode 100644
index 0000000000000..369b4506066e5
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-09-01-15-27-00.bpo-45075.9xUpvt.rst
@@ -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.



More information about the Python-checkins mailing list