[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