Skip to content

Commit 7beb7e8

Browse files
[3.13] gh-136057: Allow step and next to step over for loops (GH-136160) (#141641)
(cherry picked from commit 8be3b2f)
1 parent e6f429c commit 7beb7e8

File tree

3 files changed

+50
-4
lines changed

3 files changed

+50
-4
lines changed

Lib/bdb.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ def __init__(self, skip=None):
3737
self.frame_returning = None
3838
self.trace_opcodes = False
3939
self.enterframe = None
40+
self.cmdframe = None
41+
self.cmdlineno = None
4042

4143
self._load_breaks()
4244

@@ -124,7 +126,12 @@ def dispatch_line(self, frame):
124126
self.user_line(). Raise BdbQuit if self.quitting is set.
125127
Return self.trace_dispatch to continue tracing in this scope.
126128
"""
127-
if self.stop_here(frame) or self.break_here(frame):
129+
# GH-136057
130+
# For line events, we don't want to stop at the same line where
131+
# the latest next/step command was issued.
132+
if (self.stop_here(frame) or self.break_here(frame)) and not (
133+
self.cmdframe == frame and self.cmdlineno == frame.f_lineno
134+
):
128135
self.user_line(frame)
129136
if self.quitting: raise BdbQuit
130137
return self.trace_dispatch
@@ -316,7 +323,8 @@ def _set_trace_opcodes(self, trace_opcodes):
316323
break
317324
frame = frame.f_back
318325

319-
def _set_stopinfo(self, stopframe, returnframe, stoplineno=0, opcode=False):
326+
def _set_stopinfo(self, stopframe, returnframe, stoplineno=0, opcode=False,
327+
cmdframe=None, cmdlineno=None):
320328
"""Set the attributes for stopping.
321329
322330
If stoplineno is greater than or equal to 0, then stop at line
@@ -329,6 +337,10 @@ def _set_stopinfo(self, stopframe, returnframe, stoplineno=0, opcode=False):
329337
# stoplineno >= 0 means: stop at line >= the stoplineno
330338
# stoplineno -1 means: don't stop at all
331339
self.stoplineno = stoplineno
340+
# cmdframe/cmdlineno is the frame/line number when the user issued
341+
# step/next commands.
342+
self.cmdframe = cmdframe
343+
self.cmdlineno = cmdlineno
332344
self._set_trace_opcodes(opcode)
333345

334346
def _set_caller_tracefunc(self, current_frame):
@@ -354,15 +366,17 @@ def set_until(self, frame, lineno=None):
354366

355367
def set_step(self):
356368
"""Stop after one line of code."""
357-
self._set_stopinfo(None, None)
369+
# set_step() could be called from signal handler so enterframe might be None
370+
self._set_stopinfo(None, None, cmdframe=self.enterframe,
371+
cmdlineno=getattr(self.enterframe, 'f_lineno', None))
358372

359373
def set_stepinstr(self):
360374
"""Stop before the next instruction."""
361375
self._set_stopinfo(None, None, opcode=True)
362376

363377
def set_next(self, frame):
364378
"""Stop on the next line in or below the given frame."""
365-
self._set_stopinfo(frame, None)
379+
self._set_stopinfo(frame, None, cmdframe=frame, cmdlineno=frame.f_lineno)
366380

367381
def set_return(self, frame):
368382
"""Stop when returning from the given frame."""

Lib/test/test_pdb.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2736,6 +2736,37 @@ def test_pdb_issue_gh_127321():
27362736
"""
27372737

27382738

2739+
def test_pdb_issue_gh_136057():
2740+
"""See GH-136057
2741+
"step" and "next" commands should be able to get over list comprehensions
2742+
>>> def test_function():
2743+
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
2744+
... lst = [i for i in range(10)]
2745+
... for i in lst: pass
2746+
2747+
>>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE
2748+
... 'next',
2749+
... 'next',
2750+
... 'step',
2751+
... 'continue',
2752+
... ]):
2753+
... test_function()
2754+
> <doctest test.test_pdb.test_pdb_issue_gh_136057[0]>(2)test_function()
2755+
-> import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
2756+
(Pdb) next
2757+
> <doctest test.test_pdb.test_pdb_issue_gh_136057[0]>(3)test_function()
2758+
-> lst = [i for i in range(10)]
2759+
(Pdb) next
2760+
> <doctest test.test_pdb.test_pdb_issue_gh_136057[0]>(4)test_function()
2761+
-> for i in lst: pass
2762+
(Pdb) step
2763+
--Return--
2764+
> <doctest test.test_pdb.test_pdb_issue_gh_136057[0]>(4)test_function()->None
2765+
-> for i in lst: pass
2766+
(Pdb) continue
2767+
"""
2768+
2769+
27392770
def test_pdb_issue_gh_80731():
27402771
"""See GH-80731
27412772
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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.

0 commit comments

Comments
 (0)