Richard Wolff's changes:

bdb.py  now has a class definition called Breakpoint along with
	associated methods.  There's no reason why this class has to
	be there; if you prefer it elsewhere, 'tis easily done.

(Minor reformatting by GvR; e.g. moved Breakpoint's doc string to
proper point.)
diff --git a/Lib/bdb.py b/Lib/bdb.py
index e4b6d39..3ca25ad 100644
--- a/Lib/bdb.py
+++ b/Lib/bdb.py
@@ -16,7 +16,6 @@
 	
 	def __init__(self):
 		self.breaks = {}
-		self.cbreaks = {}
 	
 	def reset(self):
 		import linecache
@@ -84,7 +83,7 @@
 				return 1
 			frame = frame.f_back
 		return 0
-	
+
 	def break_here(self, frame):
 		filename=frame.f_code.co_filename
 		if not self.breaks.has_key(filename):
@@ -92,11 +91,15 @@
 		lineno=frame.f_lineno
 		if not lineno in self.breaks[filename]:
 			return 0
-		if self.cbreaks.has_key((filename, lineno)):
-			cond=self.cbreaks[filename, lineno]
-			return eval(cond, frame.f_globals,
-				    frame.f_locals)
-		return 1
+		# 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_delete(str(bp.number))
+			return 1
+		else:
+			return 0
 	
 	def break_anywhere(self, frame):
 		return self.breaks.has_key(frame.f_code.co_filename)
@@ -182,9 +185,11 @@
 	# Derived classes and clients can call the following methods
 	# to manipulate breakpoints.  These methods return an
 	# error message is something went wrong, None if all is well.
-	# Call self.get_*break*() to see the breakpoints.
+	# Set_break prints out the breakpoint line and file:lineno.
+	# Call self.get_*break*() to see the breakpoints or better
+	# for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
 	
-	def set_break(self, filename, lineno, cond=None):
+	def set_break(self, filename, lineno, temporary=0, cond = None):
 		import linecache # Import as late as possible
 		line = linecache.getline(filename, lineno)
 		if not line:
@@ -192,34 +197,49 @@
 		if not self.breaks.has_key(filename):
 			self.breaks[filename] = []
 		list = self.breaks[filename]
-		if lineno in list:
-			return 'There is already a breakpoint there!'
-		list.append(lineno)
-		if cond is not None: self.cbreaks[filename, lineno]=cond
-	
-	def clear_break(self, filename, lineno):
+		if not lineno in list:
+			list.append(lineno)
+		bp = Breakpoint(filename, lineno, temporary, cond)
+		print 'Breakpoint %d, at %s:%d.' %(bp.number, filename, lineno)
+
+	def clear_break(self, arg):
+		try:
+			number = int(arg)
+			bp = Breakpoint.bpbynumber[int(arg)]
+		except:
+			return 'Invalid argument'
+		if not bp:
+			return 'Breakpoint already deleted'
+		filename = bp.file
+		lineno = bp.line
 		if not self.breaks.has_key(filename):
 			return 'There are no breakpoints in that file!'
 		if lineno not in self.breaks[filename]:
 			return 'There is no breakpoint there!'
-		self.breaks[filename].remove(lineno)
+		# If there's only one bp in the list for that file,line
+		# pair, then remove the breaks entry
+		if len(Breakpoint.bplist[filename, lineno]) == 1:
+			self.breaks[filename].remove(lineno)
 		if not self.breaks[filename]:
 			del self.breaks[filename]
-		try: del self.cbreaks[filename, lineno]
-		except: pass
+		bp.deleteMe()
 	
 	def clear_all_file_breaks(self, filename):
 		if not self.breaks.has_key(filename):
 			return 'There are no breakpoints in that file!'
+		for line in self.breaks[filename]:
+			blist = Breakpoint.bplist[filename, line]
+			for bp in blist:
+				bp.deleteMe()
 		del self.breaks[filename]
-		for f,l in self.cbreaks.keys():
-		    if f==filename: del self.cbreaks[f,l]
 	
 	def clear_all_breaks(self):
 		if not self.breaks:
 			return 'There are no breakpoints!'
+		for bp in Breakpoint.bpbynumber:
+			if bp:
+				bp.deleteMe()
 		self.breaks = {}
-		self.cbreaks = {}
 	
 	def get_break(self, filename, lineno):
 		return self.breaks.has_key(filename) and \
@@ -261,9 +281,9 @@
 		filename = frame.f_code.co_filename
 		s = filename + '(' + `lineno` + ')'
 		if frame.f_code.co_name:
-		    s = s + frame.f_code.co_name
+			s = s + frame.f_code.co_name
 		else:
-		    s = s + "<lambda>"
+			s = s + "<lambda>"
 		if frame.f_locals.has_key('__args__'):
 			args = frame.f_locals['__args__']
 		else:
@@ -345,6 +365,135 @@
 def set_trace():
 	Bdb().set_trace()
 
+
+class Breakpoint:
+
+	"""Breakpoint class
+
+	Implements temporary breakpoints, ignore counts, disabling and
+	(re)-enabling, and conditionals.
+
+	Breakpoints are indexed by number through bpbynumber and by
+	the file,line tuple using bplist.  The former points to a
+	single instance of class Breakpoint.  The latter points to a
+	list of such instances since there may be more than one
+	breakpoint per line.
+
+	"""
+
+
+	next = 1		# Next bp to be assigned
+	bplist = {}		# indexed by (file, lineno) tuple
+	bpbynumber = [None]	# Each entry is None or an instance of Bpt
+				# index 0 is unused, except for marking an
+				# effective break .... see effective()
+
+	def __init__(self, file, line, temporary=0, cond = None):
+		self.file = file
+		self.line = line
+		self.temporary = temporary
+		self.cond = cond
+		self.enabled = 1
+		self.ignore = 0
+		self.hits = 0
+		self.number = Breakpoint.next
+		Breakpoint.next = Breakpoint.next + 1
+		# Build the two lists
+		self.bpbynumber.append(self)
+		if self.bplist.has_key((file, line)):
+			self.bplist[file, line].append(self)
+		else:
+			self.bplist[file, line] = [self]
+
+		
+	def deleteMe(self):
+		index = (self.file, self.line)
+		self.bpbynumber[self.number] = None   # No longer in list
+		self.bplist[index].remove(self)
+		if not self.bplist[index]:
+			# No more bp for this f:l combo
+			del self.bplist[index]
+
+	def enable(self):
+		self.enabled = 1
+
+	def disable(self):
+		self.enabled = 0
+
+	def bpprint(self):
+		if self.temporary:
+		   disp = 'del  '
+		else:
+		   disp = 'keep '
+		if self.enabled:
+		   disp = disp + 'yes'
+		else:
+		   disp = disp + 'no '
+		print '%-4dbreakpoint	 %s at %s:%d' % (self.number, disp,
+							 self.file, self.line)
+		if self.cond:
+			print '\tstop only if %s' % (self.cond,)
+		if self.ignore:
+			print '\tignore next %d hits' % (self.ignore)
+		if (self.hits):
+			if (self.hits > 1): ss = 's'
+			else: ss = ''
+			print ('\tbreakpoint already hit %d time%s' %
+			       (self.hits, ss))
+
+# -----------end of Breakpoint class----------
+
+# 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.
+
+	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.
+
+	"""
+	possibles = Breakpoint.bplist[file,line]
+	for i in range(0, len(possibles)):
+		b = possibles[i]
+		if b.enabled == 0:
+			continue
+		# Count every hit when bp is enabled
+		b.hits = b.hits + 1
+		if not b.cond:
+			# If unconditional, and ignoring,
+			# go on to next, else break
+			if b.ignore > 0:
+				b.ignore = b.ignore -1
+				continue
+			else:
+				# breakpoint and marker that's ok
+				# to delete if temporary
+				return (b,1)
+		else:
+			# Conditional bp.
+			# Ignore count applies only to those bpt hits where the
+			# condition evaluates to true.
+			try:
+				val = eval(b.cond, frame.f_globals,
+					   frame.f_locals) 
+				if val:
+					if b.ignore > 0:
+						b.ignore = b.ignore -1
+						# continue
+					else:
+						return (b,1)
+				# 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,0)
+	return (None, None)
+
 # -------------------- testing --------------------
 
 class Tdb(Bdb):