[Python-checkins] r46494 - sandbox/trunk/pdb/README.txt sandbox/trunk/pdb/mpdb.py
matt.fleming
python-checkins at python.org
Sun May 28 02:36:45 CEST 2006
Author: matt.fleming
Date: Sun May 28 02:36:42 2006
New Revision: 46494
Added:
sandbox/trunk/pdb/README.txt
Modified:
sandbox/trunk/pdb/mpdb.py
Log:
Pdb uses a simpe socket-based implementation to attach a debugger console to a debugee.
Added: sandbox/trunk/pdb/README.txt
==============================================================================
--- (empty file)
+++ sandbox/trunk/pdb/README.txt Sun May 28 02:36:42 2006
@@ -0,0 +1,21 @@
+-=[Pdb Improvements]=-
+
+-=[Author: Matt Fleming <mattjfleming at googlemail.com>]=-
+-=[Mentor: Robert L. Bernstein]=-
+
+-=[Abstract]=-
+This project is part of a Google Summer of Code 2006 project. Many people
+have stated that they would like to see improvements in pdb. This projects
+aims to correct this wish.
+
+-=[TODO]=-
+* make help command work
+* sort out the namespace corruption -
+ """
+ c:\soc\pdb\test.py(3)x()
+ -> print c[i+1], i
+ (MPdb)p i
+ *** NameError: <exceptions.NameError instance at 0x00BF6990>
+ """
+
+
Modified: sandbox/trunk/pdb/mpdb.py
==============================================================================
--- sandbox/trunk/pdb/mpdb.py (original)
+++ sandbox/trunk/pdb/mpdb.py Sun May 28 02:36:42 2006
@@ -1,36 +1,248 @@
-# Matt's Pdb Improvements
+# Pdb Improvements
+#
+# This is a Google Summer of Code project
+# Student: Matthew J. Fleming
+# Mentor: Robert L. Bernstein
+
+import cmd
import os
+from optparse import OptionParser
import pdb
+import socket
+import sys
+import traceback
+import thread
+
+# Need custom safe Repr just like pdb
+_saferepr = pdb._repr.repr
+line_prefix = '\n-> '
class MPdb(pdb.Pdb):
def __init__(self):
pdb.Pdb.__init__(self)
- self._pid = os.getpid() # This debugger's pid
- self.current_pid = self._pid # The pid of the process we're debugging
+ self.fd = sys.stdout # The file descriptor we're writing output to
+ self.prompt = '(MPdb)'
+
+ def read(self):
+ """ Read a line from our input stream/socket. """
+ try:
+ line = raw_input(self.prompt)
+ except EOFError:
+ line = 'EOF'
+ return line
+
+class DebuggerConsoleConnected(Exception): pass
+
+# Below are the classes that allow MPdb instances to be debugged locally and
+# remotely by a debugger running in a different process
+class MPdbServer(MPdb):
+ def __init__(self):
+ MPdb.__init__(self)
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.debugger_list = []
+ self.attached = False
+
+ def listen(self, port):
+ self.port = port
+ self._sock.bind((self.host, self.port))
+ self._sock.listen(1) # Only one debugger is allowed control
+ self.thread_handle = thread.start_new_thread(self.accept, ())
+
+ def accept(self):
+ c, a = self._sock.accept()
+ print "\n\nDebugger attached..\n"
+ self.debugger_list.append((c,a))
+ self.fd = self.debugger_list[-1][0].makefile("w")
+ self.attached = True
+ # We've got a debugger console attached so we hijack stdout
+ self.old_stdout = os.dup(1)
+ os.close(1)
+ sys.stdout = self.fd
+ # XXX Need a way to enter the current 'waiting' on stdin for input
+ # and start reading from the debugger console
- def attach(self, pid):
- """ Attach the debugger to a running process. """
- self.attach_pid = pid
- print >> self.stdout, "Attaching to pid: %d" % self.attach_pid
-
- def do_attach(self, pid):
- print >> self.stdout, "Attaching"
- self.attach(pid)
-
- def do_detach(self):
- print >> self.stdout, "Detaching from running process"
- self.current_pid = self._pid
-
- def do_info(self, args):
- if not args:
- print >>self.stdout, "Info [args]"
+ def cmdloop(self):
+ self.preloop()
+ stop = None
+ while not stop:
+ if self.cmdqueue:
+ line = self.cmdqueue.pop(0)
+ else:
+ line = self.read()
+ if not len(line):
+ line = 'EOF'
+ line = self.precmd(line)
+ stop = self.onecmd(line)
+ stop = self.postcmd(stop, line)
+ # Force stdout flush its contents
+ sys.stdout.flush()
+ self.postloop()
+
+ def read(self):
+ # We need to check whether we've actually been attached to yet
+ if self.attached:
+ line = self.debugger_list[-1][0].recv(1024)
+ return line
else:
- print >> self.stdout, "Current PID = %d" % self._pid
+ line = raw_input(self.prompt)
+ return line
+
+class LMPdbServer(MPdbServer):
+ """ A local debugee which can only communicate with a debugger running
+ on the localhost.
+ """
+ def __init__(self):
+ MPdbServer.__init__(self)
+ self.host = 'localhost'
- do_i = do_info
+class MPdbConsole(cmd.Cmd):
+ def __init__(self):
+ cmd.Cmd.__init__(self, stdin=None, stdout=None)
+ self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.attached = False
+ self.prompt = '(MPdb)'
+ self.lastcmd = ""
+
+ def connect(self, host, port):
+ self._sock.connect((host, port))
+ self.th_r = thread.start_new_thread(self.recv, ())
+
+ def do_attach(self, args):
+ if self.attached:
+ print "Already attached to a debugger.."
+ return None
+ h = args.split(" ")
+ self.connect(h[0], int(h[1]))
+ print "Attaching..\n"
+ self.attached = True
+
+ def help_attach(self):
+ print """attach [host] [port]
+ Attach to a debugging session at [host] on [port]."""
+
+ def cmdloop(self):
+ """ Repeatedly issue a prompt and accept input from a debugger
+ console. All commands (apart from the 'attach' and 'help' commands)
+ are sent to an attached debugee.
+ """
+ print self.prompt,
+ stop = None
+ while not stop:
+ line = self.input()
+ if not len(line):
+ line = 'EOF'
+ if self.attached:
+ self.send(line)
+ else:
+ if 'attach' in line:
+ line = line.split(' ')
+ line = line[1:]
+ line = line[0] + " " + line[1]
+ self.do_attach(line)
+ elif 'help' in line:
+ self.help_attach()
+ else:
+ print "Not currently attached to a debugger..\n"
+ continue
+ self.postloop()
+
+ def send(self, line):
+ """ Send the line to the debugee server for parsing and execution. """
+ self._sock.send(line)
+
+ def input(self):
+ """ This method will be changed later. """
+ line = raw_input()
+ # Hitting enter repeatedly should execute the last command
+ if line is not '':
+ self.lastcmd = line
+ elif line is '':
+ if self.lastcmd is '':
+ print "No previous command.. \n"
+ line = 'EOF'
+ else:
+ line = self.lastcmd
+ return line
+
+ def recv(self):
+ """ Receive all data being sent from the debugee. """
+ while 1:
+ data = self._sock.recv(1024)
+ if data:
+ print data,
+ print self.prompt,
+
+ def close(self):
+ self._sock.close()
+
+def main(options):
+ opts = options[0]
+ args = options[1]
+ if not args:
+ print 'usage: mpdb.py scriptfile [arg] ... '
+ sys.exit(1)
+ mainpyfile = args[0]
+ if not os.path.exists(mainpyfile):
+ print 'Error:', mainpyfile, 'does not exist'
+ sys.exit(1)
+ if opts.debugger:
+ # We may be local or remote but we're still a debugger console
+ # This console acts differently to everything else, for now, it's just
+ # a glorified telnet client that works _properly_ with our server
+ mpdb = MPdbConsole()
+ # All we can actually do is issue commands and receieve the result
+ mpdb.cmdloop()
+ else:
+ if opts.local_debugee:
+ mpdb = LMPdbServer()
+ mpdb.listen(7000)
+ elif opts.remote_debugee:
+ pass
+ else:
+ # Regular interactive debugger session
+ mpdb = MPdb()
+ while 1:
+ try:
+ mpdb._runscript(mainpyfile)
+ if mpdb._user_requested_quit:
+ break
+ print "The program finished and will be restarted"
+ except SystemExit:
+ # In most cases SystemExit does not warrant a post-mortem session.
+ print "The program exited via sys.exit(). " + \
+ "Exit status:",sys.exc_info()[1]
+ except:
+ print traceback.format_exc()
+ print "Uncaught exception. Entering post mortem debugging"
+ print "Running 'cont' or 'step' will restart the program"
+ t = sys.exc_info()[2]
+ while t.tb_next is not None:
+ t = t.tb_next
+ mpdb.interaction(t.tb_frame,t)
+ print "Post mortem debugger finished. The " + \
+ mainpyfile + " will be restarted"
+
+# A utility function to parse the options from the command line
+def parse_opts():
+ parser = OptionParser()
+ parser.add_option("-s", "--script", dest="scriptname",
+ help="The script to debug")
+ parser.add_option("-l", "--local-debugee", dest="local_debugee",
+ action="store_true",
+ help="This script is to be debugged locally, from " + \
+ "another process")
+ parser.add_option("-r", "--remote-debugee", dest="remote_debugee",
+ action="store_true",
+ help="This script is to be debugged by a remote " + \
+ "debugger")
+ parser.add_option("-d", "--debugger", dest="debugger",
+ action="store_true",
+ help="Invoke the debugger.")
+ (options, args) = parser.parse_args()
+ # We don't currently support any arguments
+ return (options,args)
+
+if __name__ == '__main__':
+ main(parse_opts())
- def help_info(self):
- print >> self.stdout, """i(nfo)
-Displays information about [args]"""
- help_i = help_info
More information about the Python-checkins
mailing list