Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7410dd1

Browse files
committed
#809887: improve pdb feedback for breakpoint-related actions. Also add a functional test for these commands.
1 parent a074523 commit 7410dd1

5 files changed

Lines changed: 159 additions & 82 deletions

File tree

Doc/library/bdb.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,15 @@ The :mod:`bdb` module also defines two classes:
267267

268268
Delete all existing breakpoints.
269269

270+
.. method:: get_bpbynumber(arg)
271+
272+
Return a breakpoint specified by the given number. If *arg* is a string,
273+
it will be converted to a number. If *arg* is a non-numeric string, if
274+
the given breakpoint never existed or has been deleted, a
275+
:exc:`ValueError` is raised.
276+
277+
.. versionadded:: 3.2
278+
270279
.. method:: get_break(filename, lineno)
271280

272281
Check if there is a breakpoint for *lineno* of *filename*.

Lib/bdb.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -270,15 +270,9 @@ def clear_break(self, filename, lineno):
270270

271271
def clear_bpbynumber(self, arg):
272272
try:
273-
number = int(arg)
274-
except:
275-
return 'Non-numeric breakpoint number (%s)' % arg
276-
try:
277-
bp = Breakpoint.bpbynumber[number]
278-
except IndexError:
279-
return 'Breakpoint number (%d) out of range' % number
280-
if not bp:
281-
return 'Breakpoint (%d) already deleted' % number
273+
bp = self.get_bpbynumber(arg)
274+
except ValueError as err:
275+
return str(err)
282276
self.clear_break(bp.file, bp.line)
283277

284278
def clear_all_file_breaks(self, filename):
@@ -299,6 +293,21 @@ def clear_all_breaks(self):
299293
bp.deleteMe()
300294
self.breaks = {}
301295

296+
def get_bpbynumber(self, arg):
297+
if not arg:
298+
raise ValueError('Breakpoint number expected')
299+
try:
300+
number = int(arg)
301+
except ValueError:
302+
raise ValueError('Non-numeric breakpoint number %s' % arg)
303+
try:
304+
bp = Breakpoint.bpbynumber[number]
305+
except IndexError:
306+
raise ValueError('Breakpoint number %d out of range' % number)
307+
if bp is None:
308+
raise ValueError('Breakpoint %d already deleted' % number)
309+
return bp
310+
302311
def get_break(self, filename, lineno):
303312
filename = self.canonic(filename)
304313
return filename in self.breaks and \
@@ -510,6 +519,9 @@ def bpprint(self, out=None):
510519
print(('\tbreakpoint already hit %d time%s' %
511520
(self.hits, ss)), file=out)
512521

522+
def __str__(self):
523+
return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line)
524+
513525
# -----------end of Breakpoint class----------
514526

515527
def checkfuncname(b, frame):

Lib/pdb.py

Lines changed: 46 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -552,12 +552,12 @@ def do_commands(self, arg):
552552
Those commands will be executed whenever the breakpoint causes
553553
the program to stop execution."""
554554
if not arg:
555-
bnum = len(bdb.Breakpoint.bpbynumber)-1
555+
bnum = len(bdb.Breakpoint.bpbynumber) - 1
556556
else:
557557
try:
558558
bnum = int(arg)
559559
except:
560-
print("Usage : commands [bnum]\n ...\n end",
560+
print("Usage: commands [bnum]\n ...\n end",
561561
file=self.stdout)
562562
return
563563
self.commands_bnum = bnum
@@ -634,10 +634,9 @@ def do_break(self, arg, temporary = 0):
634634
# last thing to try
635635
(ok, filename, ln) = self.lineinfo(arg)
636636
if not ok:
637-
print('*** The specified object', end=' ', file=self.stdout)
638-
print(repr(arg), end=' ', file=self.stdout)
639-
print('is not a function', file=self.stdout)
640-
print('or was not found along sys.path.', file=self.stdout)
637+
print('*** The specified object %r is not a function '
638+
'or was not found along sys.path.' % arg,
639+
file=self.stdout)
641640
return
642641
funcname = ok # ok contains a function name
643642
lineno = int(ln)
@@ -726,92 +725,67 @@ def do_enable(self, arg):
726725
args = arg.split()
727726
for i in args:
728727
try:
729-
i = int(i)
730-
except ValueError:
731-
print('Breakpoint index %r is not a number' % i, file=self.stdout)
732-
continue
733-
734-
if not (0 <= i < len(bdb.Breakpoint.bpbynumber)):
735-
print('No breakpoint numbered', i, file=self.stdout)
736-
continue
737-
738-
bp = bdb.Breakpoint.bpbynumber[i]
739-
if bp:
728+
bp = self.get_bpbynumber(i)
729+
except ValueError as err:
730+
print('***', err, file=self.stdout)
731+
else:
740732
bp.enable()
733+
print('Enabled %s' % bp, file=self.stdout)
741734

742735
def do_disable(self, arg):
743736
args = arg.split()
744737
for i in args:
745738
try:
746-
i = int(i)
747-
except ValueError:
748-
print('Breakpoint index %r is not a number' % i, file=self.stdout)
749-
continue
750-
751-
if not (0 <= i < len(bdb.Breakpoint.bpbynumber)):
752-
print('No breakpoint numbered', i, file=self.stdout)
753-
continue
754-
755-
bp = bdb.Breakpoint.bpbynumber[i]
756-
if bp:
739+
bp = self.get_bpbynumber(i)
740+
except ValueError as err:
741+
print('***', err, file=self.stdout)
742+
else:
757743
bp.disable()
744+
print('Disabled %s' % bp, file=self.stdout)
758745

759746
def do_condition(self, arg):
760747
# arg is breakpoint number and condition
761748
args = arg.split(' ', 1)
762-
try:
763-
bpnum = int(args[0].strip())
764-
except ValueError:
765-
# something went wrong
766-
print('Breakpoint index %r is not a number' % args[0], file=self.stdout)
767-
return
768749
try:
769750
cond = args[1]
770-
except:
751+
except IndexError:
771752
cond = None
772753
try:
773-
bp = bdb.Breakpoint.bpbynumber[bpnum]
774-
except IndexError:
775-
print('Breakpoint index %r is not valid' % args[0],
776-
file=self.stdout)
777-
return
778-
if bp:
754+
bp = self.get_bpbynumber(args[0].strip())
755+
except ValueError as err:
756+
print('***', err, file=self.stdout)
757+
else:
779758
bp.cond = cond
780759
if not cond:
781-
print('Breakpoint', bpnum, end=' ', file=self.stdout)
782-
print('is now unconditional.', file=self.stdout)
760+
print('Breakpoint %d is now unconditional.' % bp.number,
761+
file=self.stdout)
762+
else:
763+
print('New condition set for breakpoint %d.' % bp.number,
764+
file=self.stdout)
783765

784-
def do_ignore(self,arg):
766+
def do_ignore(self, arg):
785767
"""arg is bp number followed by ignore count."""
786768
args = arg.split()
787-
try:
788-
bpnum = int(args[0].strip())
789-
except ValueError:
790-
# something went wrong
791-
print('Breakpoint index %r is not a number' % args[0], file=self.stdout)
792-
return
793769
try:
794770
count = int(args[1].strip())
795771
except:
796772
count = 0
797773
try:
798-
bp = bdb.Breakpoint.bpbynumber[bpnum]
799-
except IndexError:
800-
print('Breakpoint index %r is not valid' % args[0],
801-
file=self.stdout)
802-
return
803-
if bp:
774+
bp = self.get_bpbynumber(args[0].strip())
775+
except ValueError as err:
776+
print('***', err, file=self.stdout)
777+
else:
804778
bp.ignore = count
805779
if count > 0:
806780
reply = 'Will ignore next '
807781
if count > 1:
808782
reply = reply + '%d crossings' % count
809783
else:
810784
reply = reply + '1 crossing'
811-
print(reply + ' of breakpoint %d.' % bpnum, file=self.stdout)
785+
print(reply + ' of breakpoint %d.' % bp.number, file=self.stdout)
812786
else:
813-
print('Will stop next time breakpoint', end=' ', file=self.stdout)
814-
print(bpnum, 'is reached.', file=self.stdout)
787+
print('Will stop next time breakpoint %d is reached.'
788+
% bp.number, file=self.stdout)
815789

816790
def do_clear(self, arg):
817791
"""Three possibilities, tried in this order:
@@ -825,7 +799,10 @@ def do_clear(self, arg):
825799
reply = 'no'
826800
reply = reply.strip().lower()
827801
if reply in ('y', 'yes'):
802+
bplist = [bp for bp in bdb.Breakpoint.bpbynumber if bp]
828803
self.clear_all_breaks()
804+
for bp in bplist:
805+
print('Deleted %s' % bp, file=self.stdout)
829806
return
830807
if ':' in arg:
831808
# Make sure it works for "clear C:\foo\bar.py:12"
@@ -837,25 +814,23 @@ def do_clear(self, arg):
837814
except ValueError:
838815
err = "Invalid line number (%s)" % arg
839816
else:
817+
bplist = self.get_breaks(filename, lineno)
840818
err = self.clear_break(filename, lineno)
841-
if err: print('***', err, file=self.stdout)
819+
if err:
820+
print('***', err, file=self.stdout)
821+
else:
822+
for bp in bplist:
823+
print('Deleted %s' % bp, file=self.stdout)
842824
return
843825
numberlist = arg.split()
844826
for i in numberlist:
845827
try:
846-
i = int(i)
847-
except ValueError:
848-
print('Breakpoint index %r is not a number' % i, file=self.stdout)
849-
continue
850-
851-
if not (0 <= i < len(bdb.Breakpoint.bpbynumber)):
852-
print('No breakpoint numbered', i, file=self.stdout)
853-
continue
854-
err = self.clear_bpbynumber(i)
855-
if err:
828+
bp = self.get_bpbynumber(i)
829+
except ValueError as err:
856830
print('***', err, file=self.stdout)
857831
else:
858-
print('Deleted breakpoint', i, file=self.stdout)
832+
self.clear_break(bp.file, bp.line)
833+
print('Deleted %s' % bp, file=self.stdout)
859834
do_cl = do_clear # 'c' is already an abbreviation for 'continue'
860835

861836
def do_where(self, arg):

Lib/test/test_pdb.py

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def test_pdb_continue_in_bottomframe():
134134
... print(3)
135135
... print(4)
136136
137-
>>> with PdbTestInput([
137+
>>> with PdbTestInput([ # doctest: +ELLIPSIS
138138
... 'next',
139139
... 'break 7',
140140
... 'continue',
@@ -149,7 +149,7 @@ def test_pdb_continue_in_bottomframe():
149149
> <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>(5)test_function()
150150
-> print(1)
151151
(Pdb) break 7
152-
Breakpoint 1 at <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>:7
152+
Breakpoint ... at <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>:7
153153
(Pdb) continue
154154
1
155155
2
@@ -164,6 +164,84 @@ def test_pdb_continue_in_bottomframe():
164164
"""
165165

166166

167+
def test_pdb_breakpoints():
168+
"""Test handling of breakpoints.
169+
170+
>>> def test_function():
171+
... import pdb; pdb.Pdb().set_trace()
172+
... print(1)
173+
... print(2)
174+
... print(3)
175+
... print(4)
176+
177+
First, need to clear bdb state that might be left over from previous tests.
178+
Otherwise, the new breakpoints might get assigned different numbers.
179+
180+
>>> from bdb import Breakpoint
181+
>>> Breakpoint.next = 1
182+
>>> Breakpoint.bplist = {}
183+
>>> Breakpoint.bpbynumber = [None]
184+
185+
Now test the breakpoint commands. NORMALIZE_WHITESPACE is needed because
186+
the breakpoint list outputs a tab for the "stop only" and "ignore next"
187+
lines, which we don't want to put in here.
188+
189+
>>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE
190+
... 'break 3',
191+
... 'disable 1',
192+
... 'ignore 1 10',
193+
... 'condition 1 1 < 2',
194+
... 'break 4',
195+
... 'break',
196+
... 'condition 1',
197+
... 'enable 1',
198+
... 'clear 1',
199+
... 'commands 2',
200+
... 'print 42',
201+
... 'end',
202+
... 'continue', # will stop at breakpoint 2
203+
... 'continue',
204+
... ]):
205+
... test_function()
206+
> <doctest test.test_pdb.test_pdb_breakpoints[0]>(3)test_function()
207+
-> print(1)
208+
(Pdb) break 3
209+
Breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoints[0]>:3
210+
(Pdb) disable 1
211+
Disabled breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoints[0]>:3
212+
(Pdb) ignore 1 10
213+
Will ignore next 10 crossings of breakpoint 1.
214+
(Pdb) condition 1 1 < 2
215+
New condition set for breakpoint 1.
216+
(Pdb) break 4
217+
Breakpoint 2 at <doctest test.test_pdb.test_pdb_breakpoints[0]>:4
218+
(Pdb) break
219+
Num Type Disp Enb Where
220+
1 breakpoint keep no at <doctest test.test_pdb.test_pdb_breakpoints[0]>:3
221+
stop only if 1 < 2
222+
ignore next 10 hits
223+
2 breakpoint keep yes at <doctest test.test_pdb.test_pdb_breakpoints[0]>:4
224+
(Pdb) condition 1
225+
Breakpoint 1 is now unconditional.
226+
(Pdb) enable 1
227+
Enabled breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoints[0]>:3
228+
(Pdb) clear 1
229+
Deleted breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoints[0]>:3
230+
(Pdb) commands 2
231+
(com) print 42
232+
(com) end
233+
(Pdb) continue
234+
1
235+
42
236+
> <doctest test.test_pdb.test_pdb_breakpoints[0]>(4)test_function()
237+
-> print(2)
238+
(Pdb) continue
239+
2
240+
3
241+
4
242+
"""
243+
244+
167245
def pdb_invoke(method, arg):
168246
"""Run pdb.method(arg)."""
169247
import pdb; getattr(pdb, method)(arg)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,9 @@ C-API
475475
Library
476476
-------
477477

478+
- Issue #809887: Make the output of pdb's breakpoint deletions more
479+
consistent; emit a message when a breakpoint is enabled or disabled.
480+
478481
- Issue #5294: Fix the behavior of pdb's "continue" command when called
479482
in the top-level debugged frame.
480483

0 commit comments

Comments
 (0)