@@ -16,7 +16,6 @@ class Bdb: # Basic Debugger
1616
1717 def __init__ (self ):
1818 self .breaks = {}
19- self .cbreaks = {}
2019
2120 def reset (self ):
2221 import linecache
@@ -84,19 +83,23 @@ def stop_here(self, frame):
8483 return 1
8584 frame = frame .f_back
8685 return 0
87-
86+
8887 def break_here (self , frame ):
8988 filename = frame .f_code .co_filename
9089 if not self .breaks .has_key (filename ):
9190 return 0
9291 lineno = frame .f_lineno
9392 if not lineno in self .breaks [filename ]:
9493 return 0
95- if self .cbreaks .has_key ((filename , lineno )):
96- cond = self .cbreaks [filename , lineno ]
97- return eval (cond , frame .f_globals ,
98- frame .f_locals )
99- return 1
94+ # flag says ok to delete temp. bp
95+ (bp , flag ) = effective (filename , lineno , frame )
96+ if bp :
97+ self .currentbp = bp .number
98+ if (flag and bp .temporary ):
99+ self .do_delete (str (bp .number ))
100+ return 1
101+ else :
102+ return 0
100103
101104 def break_anywhere (self , frame ):
102105 return self .breaks .has_key (frame .f_code .co_filename )
@@ -182,44 +185,61 @@ def set_quit(self):
182185 # Derived classes and clients can call the following methods
183186 # to manipulate breakpoints. These methods return an
184187 # error message is something went wrong, None if all is well.
185- # Call self.get_*break*() to see the breakpoints.
188+ # Set_break prints out the breakpoint line and file:lineno.
189+ # Call self.get_*break*() to see the breakpoints or better
190+ # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
186191
187- def set_break (self , filename , lineno , cond = None ):
192+ def set_break (self , filename , lineno , temporary = 0 , cond = None ):
188193 import linecache # Import as late as possible
189194 line = linecache .getline (filename , lineno )
190195 if not line :
191196 return 'That line does not exist!'
192197 if not self .breaks .has_key (filename ):
193198 self .breaks [filename ] = []
194199 list = self .breaks [filename ]
195- if lineno in list :
196- return 'There is already a breakpoint there!'
197- list .append (lineno )
198- if cond is not None : self .cbreaks [filename , lineno ]= cond
199-
200- def clear_break (self , filename , lineno ):
200+ if not lineno in list :
201+ list .append (lineno )
202+ bp = Breakpoint (filename , lineno , temporary , cond )
203+ print 'Breakpoint %d, at %s:%d.' % (bp .number , filename , lineno )
204+
205+ def clear_break (self , arg ):
206+ try :
207+ number = int (arg )
208+ bp = Breakpoint .bpbynumber [int (arg )]
209+ except :
210+ return 'Invalid argument'
211+ if not bp :
212+ return 'Breakpoint already deleted'
213+ filename = bp .file
214+ lineno = bp .line
201215 if not self .breaks .has_key (filename ):
202216 return 'There are no breakpoints in that file!'
203217 if lineno not in self .breaks [filename ]:
204218 return 'There is no breakpoint there!'
205- self .breaks [filename ].remove (lineno )
219+ # If there's only one bp in the list for that file,line
220+ # pair, then remove the breaks entry
221+ if len (Breakpoint .bplist [filename , lineno ]) == 1 :
222+ self .breaks [filename ].remove (lineno )
206223 if not self .breaks [filename ]:
207224 del self .breaks [filename ]
208- try : del self .cbreaks [filename , lineno ]
209- except : pass
225+ bp .deleteMe ()
210226
211227 def clear_all_file_breaks (self , filename ):
212228 if not self .breaks .has_key (filename ):
213229 return 'There are no breakpoints in that file!'
230+ for line in self .breaks [filename ]:
231+ blist = Breakpoint .bplist [filename , line ]
232+ for bp in blist :
233+ bp .deleteMe ()
214234 del self .breaks [filename ]
215- for f ,l in self .cbreaks .keys ():
216- if f == filename : del self .cbreaks [f ,l ]
217235
218236 def clear_all_breaks (self ):
219237 if not self .breaks :
220238 return 'There are no breakpoints!'
239+ for bp in Breakpoint .bpbynumber :
240+ if bp :
241+ bp .deleteMe ()
221242 self .breaks = {}
222- self .cbreaks = {}
223243
224244 def get_break (self , filename , lineno ):
225245 return self .breaks .has_key (filename ) and \
@@ -261,9 +281,9 @@ def format_stack_entry(self, frame_lineno, lprefix=': '):
261281 filename = frame .f_code .co_filename
262282 s = filename + '(' + `lineno` + ')'
263283 if frame .f_code .co_name :
264- s = s + frame .f_code .co_name
284+ s = s + frame .f_code .co_name
265285 else :
266- s = s + "<lambda>"
286+ s = s + "<lambda>"
267287 if frame .f_locals .has_key ('__args__' ):
268288 args = frame .f_locals ['__args__' ]
269289 else :
@@ -345,6 +365,135 @@ def runcall(self, func, *args):
345365def set_trace ():
346366 Bdb ().set_trace ()
347367
368+
369+ class Breakpoint :
370+
371+ """Breakpoint class
372+
373+ Implements temporary breakpoints, ignore counts, disabling and
374+ (re)-enabling, and conditionals.
375+
376+ Breakpoints are indexed by number through bpbynumber and by
377+ the file,line tuple using bplist. The former points to a
378+ single instance of class Breakpoint. The latter points to a
379+ list of such instances since there may be more than one
380+ breakpoint per line.
381+
382+ """
383+
384+
385+ next = 1 # Next bp to be assigned
386+ bplist = {} # indexed by (file, lineno) tuple
387+ bpbynumber = [None ] # Each entry is None or an instance of Bpt
388+ # index 0 is unused, except for marking an
389+ # effective break .... see effective()
390+
391+ def __init__ (self , file , line , temporary = 0 , cond = None ):
392+ self .file = file
393+ self .line = line
394+ self .temporary = temporary
395+ self .cond = cond
396+ self .enabled = 1
397+ self .ignore = 0
398+ self .hits = 0
399+ self .number = Breakpoint .next
400+ Breakpoint .next = Breakpoint .next + 1
401+ # Build the two lists
402+ self .bpbynumber .append (self )
403+ if self .bplist .has_key ((file , line )):
404+ self .bplist [file , line ].append (self )
405+ else :
406+ self .bplist [file , line ] = [self ]
407+
408+
409+ def deleteMe (self ):
410+ index = (self .file , self .line )
411+ self .bpbynumber [self .number ] = None # No longer in list
412+ self .bplist [index ].remove (self )
413+ if not self .bplist [index ]:
414+ # No more bp for this f:l combo
415+ del self .bplist [index ]
416+
417+ def enable (self ):
418+ self .enabled = 1
419+
420+ def disable (self ):
421+ self .enabled = 0
422+
423+ def bpprint (self ):
424+ if self .temporary :
425+ disp = 'del '
426+ else :
427+ disp = 'keep '
428+ if self .enabled :
429+ disp = disp + 'yes'
430+ else :
431+ disp = disp + 'no '
432+ print '%-4dbreakpoint %s at %s:%d' % (self .number , disp ,
433+ self .file , self .line )
434+ if self .cond :
435+ print '\t stop only if %s' % (self .cond ,)
436+ if self .ignore :
437+ print '\t ignore next %d hits' % (self .ignore )
438+ if (self .hits ):
439+ if (self .hits > 1 ): ss = 's'
440+ else : ss = ''
441+ print ('\t breakpoint already hit %d time%s' %
442+ (self .hits , ss ))
443+
444+ # -----------end of Breakpoint class----------
445+
446+ # Determines if there is an effective (active) breakpoint at this
447+ # line of code. Returns breakpoint number or 0 if none
448+ def effective (file , line , frame ):
449+ """Determine which breakpoint for this file:line is to be acted upon.
450+
451+ Called only if we know there is a bpt at this
452+ location. Returns breakpoint that was triggered and a flag
453+ that indicates if it is ok to delete a temporary bp.
454+
455+ """
456+ possibles = Breakpoint .bplist [file ,line ]
457+ for i in range (0 , len (possibles )):
458+ b = possibles [i ]
459+ if b .enabled == 0 :
460+ continue
461+ # Count every hit when bp is enabled
462+ b .hits = b .hits + 1
463+ if not b .cond :
464+ # If unconditional, and ignoring,
465+ # go on to next, else break
466+ if b .ignore > 0 :
467+ b .ignore = b .ignore - 1
468+ continue
469+ else :
470+ # breakpoint and marker that's ok
471+ # to delete if temporary
472+ return (b ,1 )
473+ else :
474+ # Conditional bp.
475+ # Ignore count applies only to those bpt hits where the
476+ # condition evaluates to true.
477+ try :
478+ val = eval (b .cond , frame .f_globals ,
479+ frame .f_locals )
480+ if val :
481+ if b .ignore > 0 :
482+ b .ignore = b .ignore - 1
483+ # continue
484+ else :
485+ return (b ,1 )
486+ # else:
487+ # continue
488+ except :
489+ # if eval fails, most conservative
490+ # thing is to stop on breakpoint
491+ # regardless of ignore count.
492+ # Don't delete temporary,
493+ # as another hint to user.
494+ return (b ,0 )
495+ return (None , None )
496+
348497# -------------------- testing --------------------
349498
350499class Tdb (Bdb ):
0 commit comments