# (See pdb.doc for documentation.)
class Restart(Exception):
"""Causes a debugger to be restarted for the debugged python program."""
# Create a custom safe Repr instance and increase its maxstring.
# The default of 30 truncates error messages too easily.
__all__ = ["run", "pm", "Pdb", "runeval", "runctx", "runcall", "set_trace",
def find_function(funcname, filename):
cre = re.compile(r'def\s+%s\s*[(]' % re.escape(funcname))
# consumer of this info expects the first line to be 1
answer = funcname, filename, lineno
# 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
class Pdb(bdb.Bdb, cmd.Cmd):
def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None):
bdb.Bdb.__init__(self, skip=skip)
cmd.Cmd.__init__(self, completekey, stdin, stdout)
self._wait_for_mainpyfile = 0
# Try to load readline if it exists
# Read $HOME/.pdbrc and ./.pdbrc
envHome = os.environ['HOME']
rcFile = open(os.path.join(envHome, ".pdbrc"))
for line in rcFile.readlines():
self.rcLines.append(line)
for line in rcFile.readlines():
self.rcLines.append(line)
self.commands = {} # associates a command list to breakpoint numbers
self.commands_doprompt = {} # for each bp num, tells if the prompt
# must be disp. after execing the cmd list
self.commands_silent = {} # for each bp num, tells if the stack trace
# must be disp. after execing the cmd list
self.commands_defining = False # True while in the process of defining
self.commands_bnum = None # The breakpoint number for which we are
self.stack, self.curindex = self.get_stack(f, t)
self.curframe = self.stack[self.curindex][0]
# The f_locals dictionary is updated from the actual frame
# locals whenever the .f_locals accessor is called, so we
# cache it here to ensure that modifications are not overwritten.
self.curframe_locals = self.curframe.f_locals
# Can be executed earlier than 'setup' if desired
# Make local copy because of recursion
if len(line) > 0 and line[0] != '#':
def user_call(self, frame, argument_list):
"""This method is called when there is the remote possibility
that we ever need to stop in this function."""
if self._wait_for_mainpyfile:
if self.stop_here(frame):
print >>self.stdout, '--Call--'
self.interaction(frame, None)
def user_line(self, frame):
"""This function is called when we stop or break at this line."""
if self._wait_for_mainpyfile:
if (self.mainpyfile != self.canonic(frame.f_code.co_filename)
self._wait_for_mainpyfile = 0
if self.bp_commands(frame):
self.interaction(frame, None)
def bp_commands(self,frame):
"""Call every command that was set for the current active breakpoint
Returns True if the normal interaction function must be called,
# 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
lastcmd_back = self.lastcmd
for line in self.commands[currentbp]:
self.lastcmd = lastcmd_back
if not self.commands_silent[currentbp]:
self.print_stack_entry(self.stack[self.curindex])
if self.commands_doprompt[currentbp]:
def user_return(self, frame, return_value):
"""This function is called when a return trap is set here."""
if self._wait_for_mainpyfile:
frame.f_locals['__return__'] = return_value
print >>self.stdout, '--Return--'
self.interaction(frame, None)
def user_exception(self, frame, exc_info):
"""This function is called if an exception occurs,
but only if we are to stop at or just below this level."""
if self._wait_for_mainpyfile:
exc_type, exc_value, exc_traceback = exc_info
frame.f_locals['__exception__'] = exc_type, exc_value
if type(exc_type) == type(''):
else: exc_type_name = exc_type.__name__
print >>self.stdout, exc_type_name + ':', _saferepr(exc_value)
self.interaction(frame, exc_traceback)
# General interaction function
def interaction(self, frame, traceback):
self.setup(frame, traceback)
self.print_stack_entry(self.stack[self.curindex])
def displayhook(self, obj):
"""Custom displayhook for the exec in default(), which prevents
assignment of the _ variable in the builtins.
# reproduce the behavior of the standard displayhook, not printing None
if line[:1] == '!': line = line[1:]
locals = self.curframe_locals
globals = self.curframe.f_globals
code = compile(line + '\n', '<stdin>', 'single')
save_displayhook = sys.displayhook
sys.displayhook = self.displayhook
exec code in globals, locals
sys.displayhook = save_displayhook
t, v = sys.exc_info()[:2]
else: exc_type_name = t.__name__
print >>self.stdout, '***', exc_type_name + ':', v
"""Handle alias expansion and ';;' separator."""
while args[0] in self.aliases:
line = self.aliases[args[0]]
line = line.replace("%" + str(ii),
line = line.replace("%*", ' '.join(args[1:]))
# split into ';;' separated commands
# unless it's an alias command
# queue up everything after marker
next = line[marker+2:].lstrip()
self.cmdqueue.append(next)
line = line[:marker].rstrip()
"""Interpret the argument as though it had been typed in response
Checks whether this line is typed at the normal prompt or in
a breakpoint command list definition.
if not self.commands_defining:
return cmd.Cmd.onecmd(self, line)
return self.handle_command_def(line)
def handle_command_def(self,line):
"""Handles one command line during command list definition."""
cmd, arg, line = self.parseline(line)
self.commands_silent[self.commands_bnum] = True
return # continue to handle other cmd def in the cmd list
return 1 # end of cmd list
cmdlist = self.commands[self.commands_bnum]
cmdlist.append(cmd+' '+arg)
# Determine if we must stop
func = getattr(self, 'do_' + cmd)
# one of the resuming commands
if func.func_name in self.commands_resuming:
self.commands_doprompt[self.commands_bnum] = False
# Command definitions, called by cmdloop()
# The argument is the remaining string on the command line
# Return true to exit from the command loop
def do_commands(self, arg):
"""Defines a list of commands associated to a breakpoint.
Those commands will be executed whenever the breakpoint causes
the program to stop execution."""
bnum = len(bdb.Breakpoint.bpbynumber)-1
print >>self.stdout, "Usage : commands [bnum]\n ..." \
self.commands_bnum = bnum
self.commands_doprompt[bnum] = True
self.commands_silent[bnum] = False
prompt_back = self.prompt
self.commands_defining = True
self.commands_defining = False
self.prompt = prompt_back
def do_break(self, arg, temporary = 0):
# break [ ([filename:]lineno | function) [, "condition"] ]
if self.breaks: # There's at least one
print >>self.stdout, "Num Type Disp Enb Where"
for bp in bdb.Breakpoint.bpbynumber:
# parse arguments; comma has lowest precedence
# and cannot occur in filename
# parse stuff after comma: "condition"
cond = arg[comma+1:].lstrip()
arg = arg[:comma].rstrip()
# parse stuff before comma: [filename:]lineno | function
filename = arg[:colon].rstrip()
f = self.lookupmodule(filename)
print >>self.stdout, '*** ', repr(filename),
print >>self.stdout, 'not found from sys.path'
arg = arg[colon+1:].lstrip()
print >>self.stdout, '*** Bad lineno:', arg
# no colon; can be lineno or function
if hasattr(func, 'im_func'):
#use co_name to identify the bkpt (function names
#could be aliased, but co_name is invariant)
lineno = code.co_firstlineno
filename = code.co_filename
(ok, filename, ln) = self.lineinfo(arg)
print >>self.stdout, '*** The specified object',
print >>self.stdout, repr(arg),
print >>self.stdout, 'is not a function'
print >>self.stdout, 'or was not found along sys.path.'
funcname = ok # ok contains a function name
filename = self.defaultFile()
# Check for reasonable breakpoint
line = self.checkline(filename, lineno)
# now set the break point
err = self.set_break(filename, line, temporary, cond, funcname)
if err: print >>self.stdout, '***', err
bp = self.get_breaks(filename, line)[-1]
print >>self.stdout, "Breakpoint %d at %s:%d" % (bp.number,
# To be overridden in derived debuggers
"""Produce a reasonable default."""
filename = self.curframe.f_code.co_filename
if filename == '<string>' and self.mainpyfile:
filename = self.mainpyfile
def do_tbreak(self, arg):
def lineinfo(self, identifier):
failed = (None, None, None)
# Input is identifier, may be in single quotes
idstring = identifier.split("'")
if id == '': return failed
# Protection for derived debuggers
# Best first guess at file to look at
fname = self.defaultFile()
# First is module, second is method/class
f = self.lookupmodule(parts[0])
answer = find_function(item, fname)
def checkline(self, filename, lineno):
"""Check whether specified line seems to be executable.
Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank
line or EOF). Warning: testing is not comprehensive.
# this method should be callable before starting debugging, so default
# to "no globals" if there is no current frame
globs = self.curframe.f_globals if hasattr(self, 'curframe') else None
line = linecache.getline(filename, lineno, globs)
print >>self.stdout, 'End of file'
# Don't allow setting breakpoint at a blank line
if (not line or (line[0] == '#') or
(line[:3] == '"""') or line[:3] == "'''"):
print >>self.stdout, '*** Blank or comment'
def do_enable(self, arg):
print >>self.stdout, 'Breakpoint index %r is not a number' % i