diff --git a/Lib/bdb.py b/Lib/bdb.py --- a/Lib/bdb.py +++ b/Lib/bdb.py @@ -133,12 +133,14 @@ if lineno not in self.breaks[filename]: return False - # flag says ok to delete temp. bp - (bp, flag) = effective(filename, lineno, frame) - if bp: - self.currentbp = bp.number - if (flag and bp.temporary): - self.do_clear(str(bp.number)) + bp_flag_list = effective(filename, lineno, frame) + if bp_flag_list: + self.effective_bp_list = [] + for bp, flag in bp_flag_list: + self.effective_bp_list.append(bp.number) + # flag says ok to delete temp. bp + if (flag and bp.temporary): + self.do_clear(str(bp.number)) return True else: return False @@ -565,16 +567,15 @@ return False return True -# Determines if there is an effective (active) breakpoint at this -# line of code. Returns breakpoint number or 0 if none def effective(file, line, frame): - """Determine which breakpoint for this file:line is to be acted upon. + """Determine which breakpoints for this file:line are to be acted upon. - Called only if we know there is a bpt at this - location. Returns breakpoint that was triggered and a flag - that indicates if it is ok to delete a temporary bp. + Called only if we know there is a bpt at this location. Returns a list of + tuples: a breakpoint that was triggered and a flag that indicates if it is + ok to delete a temporary bp. """ + bp_flag_list = [] possibles = Breakpoint.bplist[file, line] for b in possibles: if not b.enabled: @@ -590,7 +591,7 @@ continue else: # breakpoint and marker that it's ok to delete if temporary - return (b, True) + bp_flag_list.append((b, True)) else: # Conditional bp. # Ignore count applies only to those bpt hits where the @@ -602,15 +603,15 @@ b.ignore -= 1 # continue else: - return (b, True) + bp_flag_list.append((b, True)) # else: # continue except: # if eval fails, most conservative thing is to stop on # breakpoint regardless of ignore count. Don't delete # temporary, as another hint to user. - return (b, False) - return (None, None) + bp_flag_list.append((b, False)) + return bp_flag_list # -------------------- testing -------------------- diff --git a/Lib/pdb.py b/Lib/pdb.py --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -266,27 +266,38 @@ self.interaction(frame, None) def bp_commands(self, frame): - """Call every command that was set for the current active breakpoint - (if there is one). + """Call every command that was set for the current active breakpoints. Returns True if the normal interaction function must be called, False otherwise.""" - # self.currentbp is set in bdb in Bdb.break_here if a breakpoint was hit - if getattr(self, "currentbp", False) and \ - self.currentbp in self.commands: - currentbp = self.currentbp - self.currentbp = 0 - lastcmd_back = self.lastcmd - self.setup(frame, None) - for line in self.commands[currentbp]: - self.onecmd(line) - self.lastcmd = lastcmd_back - if not self.commands_silent[currentbp]: - self.print_stack_entry(self.stack[self.curindex]) - if self.commands_doprompt[currentbp]: - self._cmdloop() - self.forget() - return + # self.effective_bp_list is set in bdb in Bdb.break_here if breakpoints + # were hit + if getattr(self, "effective_bp_list", False) and \ + self.effective_bp_list: + silent = True + doprompt = False + atleast_one_cmd = False + for bp in self.effective_bp_list: + if bp in self.commands: + atleast_one_cmd = True + lastcmd_back = self.lastcmd + self.setup(frame, None) + for line in self.commands[bp]: + self.onecmd(line) + self.lastcmd = lastcmd_back + if not self.commands_silent[bp]: + silent = False + if self.commands_doprompt[bp]: + doprompt = True + + self.effective_bp_list = [] + if atleast_one_cmd: + if not silent: + self.print_stack_entry(self.stack[self.curindex]) + if doprompt: + self._cmdloop() + self.forget() + return return 1 def user_return(self, frame, return_value): diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -275,6 +275,77 @@ """ +def test_multiple_bp_on_same_line(): + """Test the hit count, ignore count and commands of breakpoints set on the + same line. + +>>> def test_function(): + ... def bar(a): + ... x = 1 + ... x = 2 + ... + ... import pdb; pdb.Pdb(nosigint=True).set_trace() + ... bar(1) + ... bar(2) + + First, need to clear bdb state that might be left over from previous tests. + Otherwise, the new breakpoints might get assigned different numbers. + +>>> from bdb import Breakpoint +>>> Breakpoint.next = 1 +>>> Breakpoint.bplist = {} +>>> Breakpoint.bpbynumber = [None] + + Now test the breakpoint commands. NORMALIZE_WHITESPACE is needed because + the breakpoint list outputs tabs. + +>>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE + ... 'break bar', + ... 'commands 1', + ... 'print a', + ... 'end', + ... 'ignore 1 1', + ... 'break bar', + ... 'commands 2', + ... 'print a + 1', + ... 'end', + ... 'ignore 2 1', + ... 'continue', + ... 'break', + ... 'continue', + ... ]): + ... test_function() +> (7)test_function() + -> bar(1) + (Pdb) break bar + Breakpoint 1 at :2 + (Pdb) commands 1 + (com) print a + (com) end + (Pdb) ignore 1 1 + Will ignore next 1 crossing of breakpoint 1. + (Pdb) break bar + Breakpoint 2 at :2 + (Pdb) commands 2 + (com) print a + 1 + (com) end + (Pdb) ignore 2 1 + Will ignore next 1 crossing of breakpoint 2. + (Pdb) continue + 2 + 3 +> (3)bar() + -> x = 1 + (Pdb) break + Num Type Disp Enb Where + 1 breakpoint keep yes at :2 + breakpoint already hit 2 times + 2 breakpoint keep yes at :2 + breakpoint already hit 2 times + (Pdb) continue + """ + + def do_nothing(): pass

AltStyle によって変換されたページ (->オリジナル) /