]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-136057: Allow step and next to step over for loops (GH-136160) (#141640)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Sun, 16 Nov 2025 22:22:11 +0000 (23:22 +0100)
committerGitHub <noreply@github.com>
Sun, 16 Nov 2025 22:22:11 +0000 (22:22 +0000)
gh-136057: Allow step and next to step over for loops (GH-136160)
(cherry picked from commit 8be3b2f479431f670f2e81e41b52e698c0806289)

Co-authored-by: Tian Gao <gaogaotiantian@hotmail.com>
Lib/bdb.py
Lib/test/test_pdb.py
Misc/NEWS.d/next/Library/2025-07-01-04-57-57.gh-issue-136057.4-t596.rst [new file with mode: 0644]

index 4290ef22302a4261cc052a8df33b2935a6e4c5bb..79da4bab9c903472d7b8bcb46b362c28cb0061eb 100644 (file)
@@ -199,6 +199,8 @@ class Bdb:
         self.frame_returning = None
         self.trace_opcodes = False
         self.enterframe = None
+        self.cmdframe = None
+        self.cmdlineno = None
         self.code_linenos = weakref.WeakKeyDictionary()
         self.backend = backend
         if backend == 'monitoring':
@@ -306,7 +308,12 @@ class Bdb:
         self.user_line(). Raise BdbQuit if self.quitting is set.
         Return self.trace_dispatch to continue tracing in this scope.
         """
-        if self.stop_here(frame) or self.break_here(frame):
+        # GH-136057
+        # For line events, we don't want to stop at the same line where
+        # the latest next/step command was issued.
+        if (self.stop_here(frame) or self.break_here(frame)) and not (
+            self.cmdframe == frame and self.cmdlineno == frame.f_lineno
+        ):
             self.user_line(frame)
             self.restart_events()
             if self.quitting: raise BdbQuit
@@ -535,7 +542,8 @@ class Bdb:
             if self.monitoring_tracer:
                 self.monitoring_tracer.update_local_events()
 
-    def _set_stopinfo(self, stopframe, returnframe, stoplineno=0, opcode=False):
+    def _set_stopinfo(self, stopframe, returnframe, stoplineno=0, opcode=False,
+                      cmdframe=None, cmdlineno=None):
         """Set the attributes for stopping.
 
         If stoplineno is greater than or equal to 0, then stop at line
@@ -548,6 +556,10 @@ class Bdb:
         # stoplineno >= 0 means: stop at line >= the stoplineno
         # stoplineno -1 means: don't stop at all
         self.stoplineno = stoplineno
+        # cmdframe/cmdlineno is the frame/line number when the user issued
+        # step/next commands.
+        self.cmdframe = cmdframe
+        self.cmdlineno = cmdlineno
         self._set_trace_opcodes(opcode)
 
     def _set_caller_tracefunc(self, current_frame):
@@ -573,7 +585,9 @@ class Bdb:
 
     def set_step(self):
         """Stop after one line of code."""
-        self._set_stopinfo(None, None)
+        # set_step() could be called from signal handler so enterframe might be None
+        self._set_stopinfo(None, None, cmdframe=self.enterframe,
+                           cmdlineno=getattr(self.enterframe, 'f_lineno', None))
 
     def set_stepinstr(self):
         """Stop before the next instruction."""
@@ -581,7 +595,7 @@ class Bdb:
 
     def set_next(self, frame):
         """Stop on the next line in or below the given frame."""
-        self._set_stopinfo(frame, None)
+        self._set_stopinfo(frame, None, cmdframe=frame, cmdlineno=frame.f_lineno)
 
     def set_return(self, frame):
         """Stop when returning from the given frame."""
index 2ca689e0adf7106afe4e7f767b9dc8b4028792c4..9d89008756a1d33798bcc7911e774735463183e6 100644 (file)
@@ -3232,6 +3232,37 @@ def test_pdb_issue_gh_127321():
     """
 
 
+def test_pdb_issue_gh_136057():
+    """See GH-136057
+    "step" and "next" commands should be able to get over list comprehensions
+    >>> def test_function():
+    ...     import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
+    ...     lst = [i for i in range(10)]
+    ...     for i in lst: pass
+
+    >>> with PdbTestInput([  # doctest: +NORMALIZE_WHITESPACE
+    ...     'next',
+    ...     'next',
+    ...     'step',
+    ...     'continue',
+    ... ]):
+    ...     test_function()
+    > <doctest test.test_pdb.test_pdb_issue_gh_136057[0]>(2)test_function()
+    -> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
+    (Pdb) next
+    > <doctest test.test_pdb.test_pdb_issue_gh_136057[0]>(3)test_function()
+    -> lst = [i for i in range(10)]
+    (Pdb) next
+    > <doctest test.test_pdb.test_pdb_issue_gh_136057[0]>(4)test_function()
+    -> for i in lst: pass
+    (Pdb) step
+    --Return--
+    > <doctest test.test_pdb.test_pdb_issue_gh_136057[0]>(4)test_function()->None
+    -> for i in lst: pass
+    (Pdb) continue
+    """
+
+
 def test_pdb_issue_gh_80731():
     """See GH-80731
 
diff --git a/Misc/NEWS.d/next/Library/2025-07-01-04-57-57.gh-issue-136057.4-t596.rst b/Misc/NEWS.d/next/Library/2025-07-01-04-57-57.gh-issue-136057.4-t596.rst
new file mode 100644 (file)
index 0000000..e237a0e
--- /dev/null
@@ -0,0 +1 @@
+Fixed the bug in :mod:`pdb` and :mod:`bdb` where ``next`` and ``step`` can't go over the line if a loop exists in the line.