]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-45075: distinguish between frame and FrameSummary in traceback mo… (GH-28112)
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>
Fri, 3 Sep 2021 21:39:23 +0000 (22:39 +0100)
committerGitHub <noreply@github.com>
Fri, 3 Sep 2021 21:39:23 +0000 (22:39 +0100)
Doc/library/traceback.rst
Lib/test/test_traceback.py
Lib/traceback.py
Misc/NEWS.d/next/Library/2021-09-01-15-27-00.bpo-45075.9xUpvt.rst [new file with mode: 0644]

index 7b230495b7b564af5dddf2f02c27065867517dfe..df4a38c955511b5dc8214d9bbbf1ba36693b4dec 100644 (file)
@@ -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)
 
index ee2896c5f4867c54ff33c20575b61674e2507aee..949adefd76faac27dabfeec3915bf56355218097 100644 (file)
@@ -1515,8 +1515,8 @@ class TestStack(unittest.TestCase):
 
     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 @@ class TestStack(unittest.TestCase):
          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()
index 8d83fd9b517e6cad61a8ad93bde55ab6819a771a..d51c2010005b768b924dbe3bc0e6e610ff149ed5 100644 (file)
@@ -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 @@ class StackSummary(list):
             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 @@ 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.
+    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 @@ class StackSummary(list):
 
                 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 @@ class StackSummary(list):
         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 (file)
index 0000000..369b450
--- /dev/null
@@ -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.