pdb.py 12 KB
Newer Older
1
# pdb.py -- finally, a Python debugger!
2

3
# (See pdb.doc for documentation.)
Guido van Rossum's avatar
Guido van Rossum committed
4 5 6 7

import string
import sys
import linecache
8 9 10
import cmd
import bdb
import repr
Guido van Rossum's avatar
Guido van Rossum committed
11 12


13 14 15 16 17 18 19
# Interaction prompt line will separate file and call info from code
# text using value of line_prefix string.  A newline and arrow may
# be to your liking.  You can set it once pdb is imported using the
# command "pdb.line_prefix = '\n% '".
# line_prefix = ': '	# Use this to get the old situation back
line_prefix = '\n-> '	# Probably a better default

20
class Pdb(bdb.Bdb, cmd.Cmd):
Guido van Rossum's avatar
Guido van Rossum committed
21
	
22 23 24
	def __init__(self):
		bdb.Bdb.__init__(self)
		cmd.Cmd.__init__(self)
Guido van Rossum's avatar
Guido van Rossum committed
25 26 27
		self.prompt = '(Pdb) '
	
	def reset(self):
28
		bdb.Bdb.reset(self)
Guido van Rossum's avatar
Guido van Rossum committed
29 30 31 32
		self.forget()
	
	def forget(self):
		self.lineno = None
33
		self.stack = []
34 35
		self.curindex = 0
		self.curframe = None
36
	
37 38 39 40
	def setup(self, f, t):
		self.forget()
		self.stack, self.curindex = self.get_stack(f, t)
		self.curframe = self.stack[self.curindex][0]
41
	
42
	# Override Bdb methods (except user_call, for now)
43
	
44 45 46
	def user_line(self, frame):
		# This function is called when we stop or break at this line
		self.interaction(frame, None)
Guido van Rossum's avatar
Guido van Rossum committed
47
	
48 49 50 51 52
	def user_return(self, frame, return_value):
		# This function is called when a return trap is set here
		frame.f_locals['__return__'] = return_value
		print '--Return--'
		self.interaction(frame, None)
53
	
54 55 56 57
	def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
		# This function is called if an exception occurs,
		# but only if we are to stop at or just below this level
		frame.f_locals['__exception__'] = exc_type, exc_value
58 59 60 61
		if type(exc_type) == type(''):
			exc_type_name = exc_type
		else: exc_type_name = exc_type.__name__
		print exc_type_name + ':', repr.repr(exc_value)
62
		self.interaction(frame, exc_traceback)
63
	
64
	# General interaction function
65
	
66
	def interaction(self, frame, traceback):
67
		self.setup(frame, traceback)
68
		self.print_stack_entry(self.stack[self.curindex])
69
		self.cmdloop()
70
		self.forget()
71

Guido van Rossum's avatar
Guido van Rossum committed
72
	def default(self, line):
73 74 75
		if line[:1] == '!': line = line[1:]
		locals = self.curframe.f_locals
		globals = self.curframe.f_globals
76
		globals['__privileged__'] = 1
77
		code = compile(line + '\n', '<stdin>', 'single')
78
		try:
79
			exec code in globals, locals
80
		except:
81 82 83 84
			if type(sys.exc_type) == type(''):
				exc_type_name = sys.exc_type
			else: exc_type_name = sys.exc_type.__name__
			print '***', exc_type_name + ':', sys.exc_value
85 86 87 88

	# Command definitions, called by cmdloop()
	# The argument is the remaining string on the command line
	# Return true to exit from the command loop 
Guido van Rossum's avatar
Guido van Rossum committed
89
	
90
	do_h = cmd.Cmd.do_help
91

Guido van Rossum's avatar
Guido van Rossum committed
92 93
	def do_break(self, arg):
		if not arg:
94
			print self.get_all_breaks() # XXX
Guido van Rossum's avatar
Guido van Rossum committed
95
			return
96 97
		# Try line number as argument
		try:	
Guido van Rossum's avatar
Guido van Rossum committed
98
			lineno = int(eval(arg))
99
			filename = self.curframe.f_code.co_filename
Guido van Rossum's avatar
Guido van Rossum committed
100
		except:
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
			# Try function name as the argument
			import codehack
			try:
				func = eval(arg, self.curframe.f_globals,
					    self.curframe.f_locals)
				if hasattr(func, 'im_func'):
					func = func.im_func
				code = func.func_code
			except:
				print '*** Could not eval argument:', arg
				return
			lineno = codehack.getlineno(code)
			filename = code.co_filename

		# now set the break point
116 117
		err = self.set_break(filename, lineno)
		if err: print '***', err
Guido van Rossum's avatar
Guido van Rossum committed
118 119 120 121
	do_b = do_break
	
	def do_clear(self, arg):
		if not arg:
122 123 124 125 126 127
			try:
				reply = raw_input('Clear all breaks? ')
			except EOFError:
				reply = 'no'
			reply = string.lower(string.strip(reply))
			if reply in ('y', 'yes'):
128
				self.clear_all_breaks()
Guido van Rossum's avatar
Guido van Rossum committed
129 130 131 132 133 134 135
			return
		try:
			lineno = int(eval(arg))
		except:
			print '*** Error in argument:', `arg`
			return
		filename = self.curframe.f_code.co_filename
136
		err = self.clear_break(filename, lineno)
137
		if err: print '***', err
138
	do_cl = do_clear # 'c' is already an abbreviation for 'continue'
Guido van Rossum's avatar
Guido van Rossum committed
139 140
	
	def do_where(self, arg):
141
		self.print_stack_trace()
Guido van Rossum's avatar
Guido van Rossum committed
142 143 144
	do_w = do_where
	
	def do_up(self, arg):
145 146
		if self.curindex == 0:
			print '*** Oldest frame'
Guido van Rossum's avatar
Guido van Rossum committed
147
		else:
148 149
			self.curindex = self.curindex - 1
			self.curframe = self.stack[self.curindex][0]
150
			self.print_stack_entry(self.stack[self.curindex])
151
			self.lineno = None
Guido van Rossum's avatar
Guido van Rossum committed
152 153 154
	do_u = do_up
	
	def do_down(self, arg):
155 156
		if self.curindex + 1 == len(self.stack):
			print '*** Newest frame'
Guido van Rossum's avatar
Guido van Rossum committed
157
		else:
158 159
			self.curindex = self.curindex + 1
			self.curframe = self.stack[self.curindex][0]
160
			self.print_stack_entry(self.stack[self.curindex])
161
			self.lineno = None
Guido van Rossum's avatar
Guido van Rossum committed
162 163 164
	do_d = do_down
	
	def do_step(self, arg):
165
		self.set_step()
166
		return 1
Guido van Rossum's avatar
Guido van Rossum committed
167 168 169
	do_s = do_step
	
	def do_next(self, arg):
170
		self.set_next(self.curframe)
171
		return 1
Guido van Rossum's avatar
Guido van Rossum committed
172 173
	do_n = do_next
	
174
	def do_return(self, arg):
175
		self.set_return(self.curframe)
176 177 178
		return 1
	do_r = do_return
	
Guido van Rossum's avatar
Guido van Rossum committed
179
	def do_continue(self, arg):
180
		self.set_continue()
181
		return 1
Guido van Rossum's avatar
Guido van Rossum committed
182 183 184
	do_c = do_cont = do_continue
	
	def do_quit(self, arg):
185 186
		self.set_quit()
		return 1
Guido van Rossum's avatar
Guido van Rossum committed
187 188
	do_q = do_quit
	
189
	def do_args(self, arg):
190 191
		if self.curframe.f_locals.has_key('__args__'):
			print `self.curframe.f_locals['__args__']`
192
		else:
193
			print '*** No arguments?!'
194 195 196 197 198 199 200 201 202 203
	do_a = do_args
	
	def do_retval(self, arg):
		if self.curframe.f_locals.has_key('__return__'):
			print self.curframe.f_locals['__return__']
		else:
			print '*** Not yet returned!'
	do_rv = do_retval
	
	def do_p(self, arg):
204
		self.curframe.f_globals['__privileged__'] = 1
205 206 207 208
		try:
			value = eval(arg, self.curframe.f_globals, \
					self.curframe.f_locals)
		except:
209 210 211 212
			if type(sys.exc_type) == type(''):
				exc_type_name = sys.exc_type
			else: exc_type_name = sys.exc_type.__name__
			print '***', exc_type_name + ':', `sys.exc_value`
213
			return
214

215 216
		print `value`

Guido van Rossum's avatar
Guido van Rossum committed
217
	def do_list(self, arg):
218
		self.lastcmd = 'list'
Guido van Rossum's avatar
Guido van Rossum committed
219 220 221 222 223 224 225 226 227 228 229 230
		last = None
		if arg:
			try:
				x = eval(arg, {}, {})
				if type(x) == type(()):
					first, last = x
					first = int(first)
					last = int(last)
					if last < first:
						# Assume it's a count
						last = first + last
				else:
231
					first = max(1, int(x) - 5)
Guido van Rossum's avatar
Guido van Rossum committed
232 233 234 235 236 237 238
			except:
				print '*** Error in argument:', `arg`
				return
		elif self.lineno is None:
			first = max(1, self.curframe.f_lineno - 5)
		else:
			first = self.lineno + 1
239
		if last == None:
Guido van Rossum's avatar
Guido van Rossum committed
240 241
			last = first + 10
		filename = self.curframe.f_code.co_filename
242
		breaklist = self.get_file_breaks(filename)
Guido van Rossum's avatar
Guido van Rossum committed
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
		try:
			for lineno in range(first, last+1):
				line = linecache.getline(filename, lineno)
				if not line:
					print '[EOF]'
					break
				else:
					s = string.rjust(`lineno`, 3)
					if len(s) < 4: s = s + ' '
					if lineno in breaklist: s = s + 'B'
					else: s = s + ' '
					if lineno == self.curframe.f_lineno:
						s = s + '->'
					print s + '\t' + line,
					self.lineno = lineno
		except KeyboardInterrupt:
			pass
	do_l = do_list
261 262 263 264 265 266

	def do_whatis(self, arg):
		try:
			value = eval(arg, self.curframe.f_globals, \
					self.curframe.f_locals)
		except:
267 268 269 270
			if type(sys.exc_type) == type(''):
				exc_type_name = sys.exc_type
			else: exc_type_name = sys.exc_type.__name__
			print '***', exc_type_name + ':', `sys.exc_value`
271 272 273 274 275 276
			return
		code = None
		# Is it a function?
		try: code = value.func_code
		except: pass
		if code:
277
			print 'Function', code.co_name
278 279 280 281 282
			return
		# Is it an instance method?
		try: code = value.im_func.func_code
		except: pass
		if code:
283
			print 'Method', code.co_name
284 285 286
			return
		# None of the above...
		print type(value)
287
	
288
	# Print a traceback starting at the top stack frame.
289
	# The most recently entered frame is printed last;
290 291 292 293 294
	# this is different from dbx and gdb, but consistent with
	# the Python interpreter's stack trace.
	# It is also consistent with the up/down commands (which are
	# compatible with dbx and gdb: up moves towards 'main()'
	# and down moves towards the most recent stack frame).
Guido van Rossum's avatar
Guido van Rossum committed
295
	
296 297 298 299 300 301
	def print_stack_trace(self):
		try:
			for frame_lineno in self.stack:
				self.print_stack_entry(frame_lineno)
		except KeyboardInterrupt:
			pass
302
	
303
	def print_stack_entry(self, frame_lineno, prompt_prefix=line_prefix):
304 305 306 307 308
		frame, lineno = frame_lineno
		if frame is self.curframe:
			print '>',
		else:
			print ' ',
309
		print self.format_stack_entry(frame_lineno, prompt_prefix)
Guido van Rossum's avatar
Guido van Rossum committed
310 311


312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
	# Help methods (derived from pdb.doc)

	def help_help(self):
		self.help_h()

	def help_h(self):
		print """h(elp)
	Without argument, print the list of available commands.
	With a command name as argument, print help about that command
	"help pdb" pipes the full documentation file to the $PAGER
	"help exec" gives help on the ! command"""

	def help_where(self):
		self.help_w()

	def help_w(self):
		print """w(here)
	Print a stack trace, with the most recent frame at the bottom.
	An arrow indicates the "current frame", which determines the
	context of most commands."""

	def help_down(self):
		self.help_d()

	def help_d(self):
		print """d(own)
	Move the current frame one level down in the stack trace
	(to an older frame)."""

	def help_up(self):
		self.help_u()

	def help_u(self):
		print """u(p)
	Move the current frame one level up in the stack trace
	(to a newer frame)."""

	def help_break(self):
		self.help_b()

	def help_b(self):
		print """b(reak) [lineno | function]
	With a line number argument, set a break there in the current
	file.  With a function name, set a break at the entry of that
	function.  Without argument, list all breaks."""

	def help_clear(self):
		self.help_cl()

	def help_cl(self):
		print """cl(ear) [lineno]
	With a line number argument, clear that break in the current file.
	Without argument, clear all breaks (but first ask confirmation)."""

	def help_step(self):
		self.help_s()

	def help_s(self):
		print """s(tep)
	Execute the current line, stop at the first possible occasion
	(either in a function that is called or in the current function)."""

	def help_next(self):
		self.help_n()

	def help_n(self):
		print """n(ext)
	Continue execution until the next line in the current function
	is reached or it returns."""

	def help_return(self):
		self.help_r()

	def help_r(self):
		print """r(eturn)
	Continue execution until the current function returns."""

	def help_continue(self):
		self.help_c()

	def help_cont(self):
		self.help_c()

	def help_c(self):
		print """c(ont(inue))
	Continue execution, only stop when a breakpoint is encountered."""

	def help_list(self):
		self.help_l()

	def help_l(self):
		print """l(ist) [first [,last]]
	List source code for the current file.
	Without arguments, list 11 lines around the current line
	or continue the previous listing.
	With one argument, list 11 lines starting at that line.
	With two arguments, list the given range;
	if the second argument is less than the first, it is a count."""

	def help_args(self):
		self.help_a()

	def help_a(self):
		print """a(rgs)
	Print the argument list of the current function."""

	def help_p(self):
		print """p expression
	Print the value of the expression."""

	def help_exec(self):
		print """(!) statement
	Execute the (one-line) statement in the context of
	the current stack frame.
	The exclamation point can be omitted unless the first word
	of the statement resembles a debugger command.
	To assign to a global variable you must always prefix the
	command with a 'global' command, e.g.:
	(Pdb) global list_options; list_options = ['-l']
	(Pdb)"""

	def help_quit(self):
		self.help_q()

	def help_q(self):
		print """q(uit)	Quit from the debugger.
	The program being executed is aborted."""

	def help_pdb(self):
		help()

443 444
# Simplified interface

445 446 447 448 449
def run(statement, globals=None, locals=None):
	Pdb().run(statement, globals, locals)

def runeval(expression, globals=None, locals=None):
	return Pdb().runeval(expression, globals, locals)
450 451

def runctx(statement, globals, locals):
452 453
	# B/W compatibility
	run(statement, globals, locals)
454

455
def runcall(*args):
456
	return apply(Pdb().runcall, args)
457

458 459
def set_trace():
	Pdb().set_trace()
460 461 462 463

# Post-Mortem interface

def post_mortem(t):
464
	p = Pdb()
465 466 467 468 469 470 471 472 473 474 475
	p.reset()
	while t.tb_next <> None: t = t.tb_next
	p.interaction(t.tb_frame, t)

def pm():
	import sys
	post_mortem(sys.last_traceback)


# Main program for testing

476
TESTCMD = 'import x; x.main()'
477

Guido van Rossum's avatar
Guido van Rossum committed
478
def test():
479
	run(TESTCMD)
480 481 482

# print help
def help():
483
	import os
484 485 486 487 488 489 490 491 492
	for dirname in sys.path:
		fullname = os.path.join(dirname, 'pdb.doc')
		if os.path.exists(fullname):
			sts = os.system('${PAGER-more} '+fullname)
			if sts: print '*** Pager exit status:', sts
			break
	else:
		print 'Sorry, can\'t find the help file "pdb.doc"',
		print 'along the Python search path'