#! /usr/bin/env python"""A Python debugger."""# (See pdb.doc for documentation.)importsysimportlinecacheimportcmdimportbdbfromreprimportrepras_safereprimportosimportre__all__=["run","pm","Pdb","runeval","runctx","runcall","set_trace","post_mortem","help"]deffind_function(funcname,filename):cre=re.compile(r'def\s+%s\s*[(]'%funcname)try:fp=open(filename)exceptIOError:returnNone# consumer of this info expects the first line to be 1lineno=1answer=Nonewhile1:line=fp.readline()ifline=='':breakifcre.match(line):answer=funcname,filename,linenobreaklineno=lineno+1fp.close()returnanswer# 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 backline_prefix='\n-> '# Probably a better defaultclassPdb(bdb.Bdb,cmd.Cmd):def__init__(self):bdb.Bdb.__init__(self)cmd.Cmd.__init__(self)self.prompt='(Pdb) 'self.aliases={}# Try to load readline if it existstry:importreadlineexceptImportError:pass# Read $HOME/.pdbrc and ./.pdbrcself.rcLines=[]ifos.environ.has_key('HOME'):envHome=os.environ['HOME']try:rcFile=open(os.path.join(envHome,".pdbrc"))exceptIOError:passelse:forlineinrcFile.readlines():self.rcLines.append(line)rcFile.close()try:rcFile=open(".pdbrc")exceptIOError:passelse:forlineinrcFile.readlines():self.rcLines.append(line)rcFile.close()defreset(self):bdb.Bdb.reset(self)self.forget()defforget(self):self.lineno=Noneself.stack=[]self.curindex=0self.curframe=Nonedefsetup(self,f,t):self.forget()self.stack,self.curindex=self.get_stack(f,t)self.curframe=self.stack[self.curindex][0]self.execRcLines()# Can be executed earlier than 'setup' if desireddefexecRcLines(self):ifself.rcLines:# Make local copy because of recursionrcLines=self.rcLines# executed only onceself.rcLines=[]forlineinrcLines:line=line[:-1]iflen(line)>0andline[0]!='#':self.onecmd(line)# Override Bdb methods (except user_call, for now)defuser_line(self,frame):"""This function is called when we stop or break at this line."""self.interaction(frame,None)defuser_return(self,frame,return_value):"""This function is called when a return trap is set here."""frame.f_locals['__return__']=return_valueprint'--Return--'self.interaction(frame,None)defuser_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_valueiftype(exc_type)==type(''):exc_type_name=exc_typeelse:exc_type_name=exc_type.__name__printexc_type_name+':',_saferepr(exc_value)self.interaction(frame,exc_traceback)# General interaction functiondefinteraction(self,frame,traceback):self.setup(frame,traceback)self.print_stack_entry(self.stack[self.curindex])self.cmdloop()self.forget()defdefault(self,line):ifline[:1]=='!':line=line[1:]locals=self.curframe.f_localsglobals=self.curframe.f_globalstry:code=compile(line+'\n','<stdin>','single')execcodeinglobals,localsexcept:t,v=sys.exc_info()[:2]iftype(t)==type(''):exc_type_name=telse:exc_type_name=t.__name__print'***',exc_type_name+':',vdefprecmd(self,line):"""Handle alias expansion and ';;' separator."""ifnotline:returnlineargs=line.split()whileself.aliases.has_key(args[0]):line=self.aliases[args[0]]ii=1fortmpArginargs[1:]:line=line.replace("%"+str(ii),tmpArg)ii=ii+1line=line.replace("%*",' '.join(args[1:]))args=line.split()# split into ';;' separated commands# unless it's an alias commandifargs[0]!='alias':marker=line.find(';;')ifmarker>=0:# queue up everything after markernext=line[marker+2:].lstrip()self.cmdqueue.append(next)line=line[:marker].rstrip()returnline# Command definitions, called by cmdloop()# The argument is the remaining string on the command line# Return true to exit from the command loopdo_h=cmd.Cmd.do_helpdefdo_EOF(self,arg):return0# Don't die on EOFdefdo_break(self,arg,temporary=0):# break [ ([filename:]lineno | function) [, "condition"] ]ifnotarg:ifself.breaks:# There's at least oneprint"Num Type Disp Enb Where"forbpinbdb.Breakpoint.bpbynumber:ifbp:bp.bpprint()return# parse arguments; comma has lowest precedence# and cannot occur in filenamefilename=Nonelineno=Nonecond=Nonecomma=arg.find(',')ifcomma>0:# parse stuff after comma: "condition"cond=arg[comma+1:].lstrip()arg=arg[:comma].rstrip()# parse stuff before comma: [filename:]lineno | functioncolon=arg.rfind(':')ifcolon>=0:filename=arg[:colon].rstrip()f=self.lookupmodule(filename)ifnotf:print'*** ',`filename`,print'not found from sys.path'returnelse:filename=farg=arg[colon+1:].lstrip()try:lineno=int(arg)exceptValueError,msg:print'*** Bad lineno:',argreturnelse:# no colon; can be lineno or functiontry:lineno=int(arg)exceptValueError:try:func=eval(arg,self.curframe.f_globals,self.curframe.f_locals)except:func=argtry:ifhasattr(func,'im_func'):func=func.im_funccode=func.func_codelineno=code.co_firstlinenofilename=code.co_filenameexcept:# last thing to try(ok,filename,ln)=self.lineinfo(arg)ifnotok:print'*** The specified object',print`arg`,print'is not a function'print('or was not found ''along sys.path.')returnlineno=int(ln)ifnotfilename:filename=self.defaultFile()# Check for reasonable breakpointline=self.checkline(filename,lineno)ifline:# now set the break pointerr=self.set_break(filename,line,temporary,cond)iferr:print'***',errelse:bp=self.get_breaks(filename,line)[-1]print"Breakpoint %d at %s:%d"%(bp.number,bp.file,bp.line)# To be overridden in derived debuggersdefdefaultFile(self):"""Produce a reasonable default."""filename=self.curframe.f_code.co_filenameiffilename=='<string>'andmainpyfile:filename=mainpyfilereturnfilenamedo_b=do_breakdefdo_tbreak(self,arg):self.do_break(arg,1)deflineinfo(self,identifier):failed=(None,None,None)# Input is identifier, may be in single quotesidstring=identifier.split("'")iflen(idstring)==1:# not in single quotesid=idstring[0].strip()eliflen(idstring)==3:# quotedid=idstring[1].strip()else:returnfailedifid=='':returnfailedparts=id.split('.')# Protection for derived debuggersifparts[0]=='self':delparts[0]iflen(parts)==0:returnfailed# Best first guess at file to look atfname=self.defaultFile()iflen(parts)==1:item=parts[0]else:# More than one part.# First is module, second is method/classf=self.lookupmodule(parts[0])iff:fname=fitem=parts[1]answer=find_function(item,fname)returnanswerorfaileddefcheckline(self,filename,lineno):"""Return line number of first line at or after input argument such that if the input points to a 'def', the returned line number is the first non-blank/non-comment line to follow. If the input points to a blank or comment line, return 0. At end of file, also return 0."""line=linecache.getline(filename,lineno)ifnotline:print'End of file'return0line=line.strip()# Don't allow setting breakpoint at a blank lineif(notlineor(line[0]=='#')or(line[:3]=='"""')orline[:3]=="'''"):print'*** Blank or comment'return0# When a file is read in and a breakpoint is at# the 'def' statement, the system stops there at# code parse time. We don't want that, so all breakpoints# set at 'def' statements are moved one line onwardifline[:3]=='def':instr=''brackets=0while1:skipone=0forcinline:ifinstr:ifskipone:skipone=0elifc=='\\':skipone=1elifc==instr:instr=''elifc=='#':breakelifcin('"',"'"):instr=celifcin('(','{','['):brackets=brackets+1elifcin(')','}',']'):brackets=brackets-1lineno=lineno+1line=linecache.getline(filename,lineno)ifnotline:print'end of file'return0line=line.strip()ifnotline:continue# Blank lineifbrackets<=0andline[0]notin('#','"',"'"):breakreturnlinenodefdo_enable(self,arg):args=arg.split()foriinargs:bp=bdb.Breakpoint.bpbynumber[int(i)]ifbp:bp.enable()defdo_disable(self,arg):args=arg.split()foriinargs:bp=bdb.Breakpoint.bpbynumber[int(i)]ifbp:bp.disable()defdo_condition(self,arg):# arg is breakpoint number and conditionargs=arg.split(' ',1)bpnum=int(args[0].strip())try:cond=args[1]except:cond=Nonebp=bdb.Breakpoint.bpbynumber[bpnum]ifbp:bp.cond=condifnotcond:print'Breakpoint',bpnum,print'is now unconditional.'defdo_ignore(self,arg):"""arg is bp number followed by ignore count."""args=arg.split()bpnum=int(args[0].strip())try:count=int(args[1].strip())except:count=0bp=bdb.Breakpoint.bpbynumber[bpnum]ifbp:bp.ignore=countif(count>0):reply='Will ignore next 'if(count>1):reply=reply+'%d crossings'%countelse:reply=reply+'1 crossing'printreply+' of breakpoint %d.'%bpnumelse:print'Will stop next time breakpoint',printbpnum,'is reached.'defdo_clear(self,arg):"""Three possibilities, tried in this order: clear -> clear all breaks, ask for confirmation clear file:lineno -> clear all breaks at file:lineno clear bpno bpno ... -> clear breakpoints by number"""ifnotarg:try:reply=raw_input('Clear all breaks? ')exceptEOFError:reply='no'reply=reply.strip().lower()ifreplyin('y','yes'):self.clear_all_breaks()returnif':'inarg:# Make sure it works for "clear C:\foo\bar.py:12"i=arg.rfind(':')filename=arg[:i]arg=arg[i+1:]try:lineno=int(arg)except:err="Invalid line number (%s)"%argelse:err=self.clear_break(filename,lineno)iferr:print'***',errreturnnumberlist=arg.split()foriinnumberlist:err=self.clear_bpbynumber(i)iferr:print'***',errelse:print'Deleted breakpoint %s '%(i,)do_cl=do_clear# 'c' is already an abbreviation for 'continue'defdo_where(self,arg):self.print_stack_trace()do_w=do_wheredo_bt=do_wheredefdo_up(self,arg):ifself.curindex==0:print'*** Oldest frame'else:self.curindex=self.curindex-1self.curframe=self.stack[self.curindex][0]self.print_stack_entry(self.stack[self.curindex])self.lineno=Nonedo_u=do_updefdo_down(self,arg):ifself.curindex+1==len(self.stack):print'*** Newest frame'else:self.curindex=self.curindex+1self.curframe=self.stack[self.curindex][0]self.print_stack_entry(self.stack[self.curindex])self.lineno=Nonedo_d=do_downdefdo_step(self,arg):self.set_step()return1do_s=do_stepdefdo_next(self,arg):self.set_next(self.curframe)return1do_n=do_nextdefdo_return(self,arg):self.set_return(self.curframe)return1do_r=do_returndefdo_continue(self,arg):self.set_continue()return1do_c=do_cont=do_continuedefdo_quit(self,arg):self.set_quit()return1do_q=do_quitdefdo_args(self,arg):f=self.curframeco=f.f_codedict=f.f_localsn=co.co_argcountifco.co_flags&4:n=n+1ifco.co_flags&8:n=n+1foriinrange(n):name=co.co_varnames[i]printname,'=',ifdict.has_key(name):printdict[name]else:print"*** undefined ***"do_a=do_argsdefdo_retval(self,arg):ifself.curframe.f_locals.has_key('__return__'):printself.curframe.f_locals['__return__']else:print'*** Not yet returned!'do_rv=do_retvaldefdo_p(self,arg):try:value=eval(arg,self.curframe.f_globals,self.curframe.f_locals)except:t,v=sys.exc_info()[:2]iftype(t)==type(''):exc_type_name=telse:exc_type_name=t.__name__print'***',exc_type_name+':',`v`returnprint`value`defdo_list(self,arg):self.lastcmd='list'last=Noneifarg:try:x=eval(arg,{},{})iftype(x)==type(()):first,last=xfirst=int(first)last=int(last)iflast<first:# Assume it's a countlast=first+lastelse:first=max(1,int(x)-5)except:print'*** Error in argument:',`arg`returnelifself.linenoisNone:first=max(1,self.curframe.f_lineno-5)else:first=self.lineno+1iflastisNone:last=first+10filename=self.curframe.f_code.co_filenamebreaklist=self.get_file_breaks(filename)try:forlinenoinrange(first,last+1):line=linecache.getline(filename,lineno)ifnotline:print'[EOF]'breakelse:s=`lineno`.rjust(3)iflen(s)<4:s=s+' 'iflinenoinbreaklist:s=s+'B'else:s=s+' 'iflineno==self.curframe.f_lineno:s=s+'->'prints+'\t'+line,self.lineno=linenoexceptKeyboardInterrupt:passdo_l=do_listdefdo_whatis(self,arg):try:value=eval(arg,self.curframe.f_globals,self.curframe.f_locals)except:t,v=sys.exc_info()[:2]iftype(t)==type(''):exc_type_name=telse:exc_type_name=t.__name__print'***',exc_type_name+':',`v`returncode=None# Is it a function?try:code=value.func_codeexcept:passifcode:print'Function',code.co_namereturn# Is it an instance method?try:code=value.im_func.func_codeexcept:passifcode:print'Method',code.co_namereturn# None of the above...printtype(value)defdo_alias(self,arg):args=arg.split()iflen(args)==0:keys=self.aliases.keys()keys.sort()foraliasinkeys:print"%s = %s"%(alias,self.aliases[alias])returnifself.aliases.has_key(args[0])andlen(args)==1:print"%s = %s"%(args[0],self.aliases[args[0]])else:self.aliases[args[0]]=' '.join(args[1:])defdo_unalias(self,arg):args=arg.split()iflen(args)==0:returnifself.aliases.has_key(args[0]):delself.aliases[args[0]]# Print a traceback starting at the top stack frame.# The most recently entered frame is printed last;# 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).defprint_stack_trace(self):try:forframe_linenoinself.stack:self.print_stack_entry(frame_lineno)exceptKeyboardInterrupt:passdefprint_stack_entry(self,frame_lineno,prompt_prefix=line_prefix):frame,lineno=frame_linenoifframeisself.curframe:print'>',else:print' ',printself.format_stack_entry(frame_lineno,prompt_prefix)# Help methods (derived from pdb.doc)defhelp_help(self):self.help_h()defhelp_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"""defhelp_where(self):self.help_w()defhelp_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 thecontext of most commands. 'bt' is an alias for this command."""help_bt=help_wdefhelp_down(self):self.help_d()defhelp_d(self):print"""d(own)Move the current frame one level down in the stack trace(to an older frame)."""defhelp_up(self):self.help_u()defhelp_u(self):print"""u(p)Move the current frame one level up in the stack trace(to a newer frame)."""defhelp_break(self):self.help_b()defhelp_b(self):print"""b(reak) ([file:]lineno | function) [, condition]With a line number argument, set a break there in the currentfile. With a function name, set a break at first executable lineof that function. Without argument, list all breaks. If a secondargument is present, it is a string specifying an expressionwhich must evaluate to true before the breakpoint is honored.The line number may be prefixed with a filename and a colon,to specify a breakpoint in another file (probably one thathasn't been loaded yet). The file is searched for on sys.path;the .py suffix may be omitted."""defhelp_clear(self):self.help_cl()defhelp_cl(self):print"cl(ear) filename:lineno"print"""cl(ear) [bpnumber [bpnumber...]]With a space separated list of breakpoint numbers, clearthose breakpoints. Without argument, clear all breaks (butfirst ask confirmation). With a filename:lineno argument,clear all breaks at that line in that file.Note that the argument is different from previous versions ofthe debugger (in python distributions 1.5.1 and before) wherea linenumber was used instead of either filename:lineno orbreakpoint numbers."""defhelp_tbreak(self):print"""tbreak same arguments as break, but breakpoint isremoved when first hit."""defhelp_enable(self):print"""enable bpnumber [bpnumber ...]Enables the breakpoints given as a space separated list ofbp numbers."""defhelp_disable(self):print"""disable bpnumber [bpnumber ...]Disables the breakpoints given as a space separated list ofbp numbers."""defhelp_ignore(self):print"""ignore bpnumber countSets the ignore count for the given breakpoint number. A breakpointbecomes active when the ignore count is zero. When non-zero, thecount is decremented each time the breakpoint is reached and thebreakpoint is not disabled and any associated condition evaluatesto true."""defhelp_condition(self):print"""condition bpnumber str_conditionstr_condition is a string specifying an expression whichmust evaluate to true before the breakpoint is honored.If str_condition is absent, any existing condition is removed;i.e., the breakpoint is made unconditional."""defhelp_step(self):self.help_s()defhelp_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)."""defhelp_next(self):self.help_n()defhelp_n(self):print"""n(ext)Continue execution until the next line in the current functionis reached or it returns."""defhelp_return(self):self.help_r()defhelp_r(self):print"""r(eturn)Continue execution until the current function returns."""defhelp_continue(self):self.help_c()defhelp_cont(self):self.help_c()defhelp_c(self):print"""c(ont(inue))Continue execution, only stop when a breakpoint is encountered."""defhelp_list(self):self.help_l()defhelp_l(self):print"""l(ist) [first [,last]]List source code for the current file.Without arguments, list 11 lines around the current lineor 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."""defhelp_args(self):self.help_a()defhelp_a(self):print"""a(rgs)Print the arguments of the current function."""defhelp_p(self):print"""p expressionPrint the value of the expression."""defhelp_exec(self):print"""(!) statementExecute the (one-line) statement in the context ofthe current stack frame.The exclamation point can be omitted unless the first wordof the statement resembles a debugger command.To assign to a global variable you must always prefix thecommand with a 'global' command, e.g.:(Pdb) global list_options; list_options = ['-l'](Pdb)"""defhelp_quit(self):self.help_q()defhelp_q(self):print"""q(uit) Quit from the debugger.The program being executed is aborted."""defhelp_whatis(self):print"""whatis argPrints the type of the argument."""defhelp_EOF(self):print"""EOFHandles the receipt of EOF as a command."""defhelp_alias(self):print"""alias [name [command [parameter parameter ...] ]]Creates an alias called 'name' the executes 'command'. The commandmust *not* be enclosed in quotes. Replaceable parameters areindicated by %1, %2, and so on, while %* is replaced by all theparameters. If no command is given, the current alias for nameis shown. If no name is given, all aliases are listed.Aliases may be nested and can contain anything that can belegally typed at the pdb prompt. Note! You *can* overrideinternal pdb commands with aliases! Those internal commandsare then hidden until the alias is removed. Aliasing is recursivelyapplied to the first word of the command line; all other wordsin the line are left alone.Some useful aliases (especially when placed in the .pdbrc file) are:#Print instance variables (usage "pi classInst")alias pi for k in %1.__dict__.keys(): print "%1.",k,"=",%1.__dict__[k]#Print instance variables in selfalias ps pi self"""defhelp_unalias(self):print"""unalias nameDeletes the specified alias."""defhelp_pdb(self):help()deflookupmodule(self,filename):"""Helper function for break/clear parsing -- may be overridden."""root,ext=os.path.splitext(filename)ifext=='':filename=filename+'.py'ifos.path.isabs(filename):returnfilenamefordirnameinsys.path:whileos.path.islink(dirname):dirname=os.readlink(dirname)fullname=os.path.join(dirname,filename)ifos.path.exists(fullname):returnfullnamereturnNone# Simplified interfacedefrun(statement,globals=None,locals=None):Pdb().run(statement,globals,locals)defruneval(expression,globals=None,locals=None):returnPdb().runeval(expression,globals,locals)defrunctx(statement,globals,locals):# B/W compatibilityrun(statement,globals,locals)defruncall(*args):returnapply(Pdb().runcall,args)defset_trace():Pdb().set_trace()# Post-Mortem interfacedefpost_mortem(t):p=Pdb()p.reset()whilet.tb_nextisnotNone:t=t.tb_nextp.interaction(t.tb_frame,t)defpm():post_mortem(sys.last_traceback)# Main program for testingTESTCMD='import x; x.main()'deftest():run(TESTCMD)# print helpdefhelp():fordirnameinsys.path:fullname=os.path.join(dirname,'pdb.doc')ifos.path.exists(fullname):sts=os.system('${PAGER-more} '+fullname)ifsts:print'*** Pager exit status:',stsbreakelse:print'Sorry, can\'t find the help file "pdb.doc"',print'along the Python search path'mainmodule=''mainpyfile=''# When invoked as main program, invoke the debugger on a scriptif__name__=='__main__':ifnotsys.argv[1:]:print"usage: pdb.py scriptfile [arg] ..."sys.exit(2)mainpyfile=filename=sys.argv[1]# Get script filenameifnotos.path.exists(filename):print'Error:',`filename`,'does not exist'sys.exit(1)mainmodule=os.path.basename(filename)delsys.argv[0]# Hide "pdb.py" from argument list# Insert script directory in front of module search pathsys.path.insert(0,os.path.dirname(filename))run('execfile('+`filename`+')')