summaryrefslogtreecommitdiffstats
path: root/Lib/pdb.py
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1992-01-10 14:54:42 (GMT)
committerGuido van Rossum <guido@python.org>1992-01-10 14:54:42 (GMT)
commit921c82401b6053ae7dacad5ef9a4bd02bdf8dbf1 (patch)
treeecb7de5787db359eee57b4e997ae3baaae3a8eca /Lib/pdb.py
parent626dae7a4269a9d2f3708e57080995cbf60289eb (diff)
downloadcpython-921c82401b6053ae7dacad5ef9a4bd02bdf8dbf1.zip
cpython-921c82401b6053ae7dacad5ef9a4bd02bdf8dbf1.tar.gz
cpython-921c82401b6053ae7dacad5ef9a4bd02bdf8dbf1.tar.bz2
Initial revision
Diffstat (limited to 'Lib/pdb.py')
-rwxr-xr-xLib/pdb.py396
1 files changed, 396 insertions, 0 deletions
diff --git a/Lib/pdb.py b/Lib/pdb.py
new file mode 100755
index 0000000..259c770
--- /dev/null
+++ b/Lib/pdb.py
@@ -0,0 +1,396 @@
+# pdb.py -- finally, a Python debugger!
+
+# To do:
+# - Keep a list of exceptions trapped (default only KeyboardInterrupt?)
+# - A blank line should repeat the previous command, not execute 'next'
+# - Don't line-trace functions (or at least files) without breakpoints
+# - Better handling of call/return events
+# - It should be possible to intercept KeyboardInterrupt completely
+# - Where and when to stop exactly when 'next' encounters a return?
+# (should be level-based -- don't trace anything deeper than current)
+# - Show stack traces upside-down (like dbx/gdb)
+
+import string
+import sys
+import linecache
+
+
+# A generic class to build command interpreters
+
+PROMPT = '(Cmd) '
+IDENTCHARS = string.letters + string.digits + '_'
+
+class Cmd:
+ def init(self):
+ self.prompt = PROMPT
+ self.identchars = IDENTCHARS
+ return self
+ def commandloop(self):
+ try:
+ self.innerloop()
+ except EOFError:
+ pass
+ def innerloop(self):
+ while 1:
+ line = string.strip(self.getline())
+ self.execline(line)
+ def execline(self, line):
+ i, n = 0, len(line)
+ while i < n and line[i] in self.identchars: i = i+1
+ cmd, arg = line[:i], string.strip(line[i:])
+ if cmd == '':
+ self.default(line)
+ else:
+ try:
+ func = eval('self.do_' + cmd)
+ except AttributeError:
+ self.default(line)
+ return
+ func(arg)
+ def getline(self):
+ return raw_input(self.prompt)
+ def default(self, line):
+ print '*** Unknown syntax:', line
+ def do_help(self, arg):
+ if arg:
+ # XXX check arg syntax
+ try:
+ func = eval('self.help_' + arg)
+ except:
+ print '*** No help on', `arg`
+ return
+ func()
+ else:
+ import getattr
+ names = getattr.dir(self)
+ cmds = []
+ for name in names:
+ if name[:3] == 'do_':
+ cmds.append(name[3:])
+ print cmds
+
+
+# A specialization of Cmd for use by the debugger
+
+PdbDone = 'PdbDone' # Exception used internally
+PdbQuit = 'PdbQuit' # Exception to just give up
+
+class Pdb(Cmd):
+
+ def init(self):
+ self = Cmd.init(self)
+ self.prompt = '(Pdb) '
+ self.reset()
+ self.breaks = {}
+ return self
+
+ def reset(self):
+ self.whatnext = ''
+ self.botframe = None
+ self.forget()
+
+ def forget(self):
+ self.curframe = self.topframe = None
+ self.stack = []
+ self.lineno = None
+
+ def setup(self, frame):
+ self.curframe = self.topframe = frame
+ self.stack = []
+ self.lineno = None
+ self.stopframe = None
+ self.whatnext = ''
+
+ def run(self, cmd):
+ if cmd[-1:] != '\n': cmd = cmd + '\n'
+ self.reset()
+ sys.trace = self.dispatch
+ try:
+ exec(cmd)
+ except PdbQuit:
+ pass
+ finally:
+ sys.trace = None
+ del sys.trace
+ self.reset()
+
+ def dispatch(self, frame, where, arg):
+ if self.whatnext == 'quit':
+ return
+ if self.botframe is None:
+ self.botframe = frame
+ if where == 'exception':
+ stop = 1
+ elif self.whatnext == 'continue':
+ stop = 0
+ elif self.whatnext == 'next':
+ stop = (frame == self.stopframe)
+ else:
+ stop = 1
+ if not stop:
+ # Check breakpoints only
+ filename = frame.f_code.co_filename
+ if not self.breaks.has_key(filename):
+ return self.dispatch
+ lineno = frame.f_lineno
+ if lineno not in self.breaks[filename]:
+ return self.dispatch
+ if where == 'call':
+ print 'call arguments:', arg
+ elif where == 'return':
+ print 'return value:', arg
+ elif where == 'exception':
+ print 'exception:', arg[:2]
+ elif where != 'line':
+ print 'unknown trace type:', `where`
+ self.setup(frame)
+ try:
+ self.whatnext = self.commandloop()
+ finally:
+ self.forget()
+ if self.whatnext == 'quit':
+ raise PdbQuit
+ if self.whatnext == 'next':
+ if where != 'return':
+ self.stopframe = frame
+ else:
+ self.whatnext = 'step'
+ return self.dispatch
+
+ def commandloop(self):
+ self.printwhere(self.curframe)
+ try:
+ self.innerloop()
+ except EOFError:
+ self.do_next('')
+ except PdbDone, msg:
+ return msg
+
+ def default(self, line):
+ if not line:
+ self.do_next('')
+ else:
+ if line[0] == '!': line = line[1:]
+ try:
+ exec(line + '\n', \
+ self.curframe.f_globals, \
+ self.curframe.f_locals)
+ except:
+ print '***', sys.exc_type + ':',
+ print `sys.exc_value`
+
+ do_h = Cmd.do_help
+
+ def do_break(self, arg):
+ if not arg:
+ print self.breaks # XXX
+ return
+ try:
+ lineno = int(eval(arg))
+ except:
+ print '*** Error in argument:', `arg`
+ return
+ filename = self.curframe.f_code.co_filename
+ line = linecache.getline(filename, lineno)
+ if not line:
+ print '*** That line does not exist!'
+ return
+ if not self.breaks.has_key(filename):
+ self.breaks[filename] = []
+ list = self.breaks[filename]
+ if lineno in list:
+ print '*** There is already a break there!'
+ return
+ list.append(lineno)
+ do_b = do_break
+
+ def do_clear(self, arg):
+ if not arg:
+ self.breaks = {}
+ print 'All breaks cleared!'
+ return
+ try:
+ lineno = int(eval(arg))
+ except:
+ print '*** Error in argument:', `arg`
+ return
+ filename = self.curframe.f_code.co_filename
+ try:
+ self.breaks[filename].remove(lineno)
+ if self.breaks[filename] == []:
+ del self.breaks[filename]
+ except (ValueError, KeyError):
+ print '*** There is no break there!'
+ return
+
+ def do_where(self, arg):
+ self.printtb()
+ do_w = do_where
+
+ def do_up(self, arg):
+ if not self.stack: print '*** Top'
+ else:
+ self.curframe = self.stack[-1]
+ self.lineno = None
+ del self.stack[-1]
+ self.printwhere(self.curframe)
+ do_u = do_up
+
+ def do_down(self, arg):
+ if self.curframe == self.botframe or \
+ not self.curframe.f_back: print '*** Bottom'
+ else:
+ self.stack.append(self.curframe)
+ self.curframe = self.curframe.f_back
+ self.lineno = None
+ self.printwhere(self.curframe)
+ do_d = do_down
+
+ def do_step(self, arg):
+ raise PdbDone, 'step'
+ do_s = do_step
+
+ def do_next(self, arg):
+ raise PdbDone, 'next'
+ do_n = do_next
+
+ def do_continue(self, arg):
+ raise PdbDone, 'continue'
+ do_c = do_cont = do_continue
+
+ def do_quit(self, arg):
+ raise PdbDone, 'quit'
+ do_q = do_quit
+
+ def do_list(self, arg):
+ 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:
+ first = int(x)
+ 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
+ if last is None:
+ last = first + 10
+ filename = self.curframe.f_code.co_filename
+ if self.breaks.has_key(filename):
+ breaklist = self.breaks[filename]
+ else:
+ breaklist = []
+ 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
+
+ # Print a traceback starting at a given stack frame
+ # Note that it is printed upside-down with respect
+ # to the orientation suggested by the up/down commands.
+ # This is consistent with gdb.
+ def printtb(self):
+ list = []
+ frame = self.topframe
+ while frame:
+ list.append(frame)
+ if frame is self.botframe: break
+ frame = frame.f_back
+ list.reverse()
+ for frame in list:
+ self.printwhere(frame)
+
+ def printwhere(self, frame):
+ if frame is self.curframe: print '->',
+ code = frame.f_code
+ filename = code.co_filename
+ lineno = frame.f_lineno
+ print filename + '(' + `lineno` + ')',
+ line = linecache.getline(filename, lineno)
+ if line: print string.strip(line),
+ print
+
+
+# --------------------- testing ---------------------
+
+# The Ackermann function -- a highly recursive beast
+cheat = 0
+cache = {}
+def ack(x, y):
+ key = `(long(x), long(y))`
+ if cache.has_key(key):
+ res = cache[key]
+ else:
+ if x == 0:
+ res = 1L
+ elif y == 0:
+ if x == 1:
+ res = 2L
+ else:
+ res = 2L + x
+ elif y == 1 and cheat >= 1:
+ res = 2L * x
+ elif y == 2 and cheat >= 2:
+ res = pow(2L, x)
+ else:
+ res = ack(ack(x-1, y), y-1)
+ cache[key] = res
+ return res
+
+def foo(n):
+ print 'foo', n
+ x = bar(n*2)
+ print 'bar returned', x
+ return
+
+def bar(a):
+ print 'bar', a
+ return a*10
+
+def test():
+ linecache.checkcache()
+ Pdb().init().run('foo(12)')
+
+
+# --------------------- main ---------------------
+
+import os
+
+def main():
+ if sys.argv[1:]:
+ file = sys.argv[1]
+ head, tail = os.path.split(file)
+ if tail[-3:] != '.py':
+ print 'Sorry, file arg must be a python module'
+ print '(i.e., must end in \'.py\')'
+ # XXX Or we could copy it to a temp file
+ sys.exit(2)
+ del sys.argv[0]
+ sys.path.insert(0, head)
+ Pdb().init().run('import ' + tail[:-3])
+ else:
+ Pdb().init().run('')