[Python-checkins] r47082 - in sandbox/trunk/pdb: README.txt doc mconnection.py mpdb.py mthread.py test/test_mconnection.py test/test_mpdb.py

matt.fleming python-checkins at python.org
Fri Jun 23 12:59:58 CEST 2006


Author: matt.fleming
Date: Fri Jun 23 12:59:57 2006
New Revision: 47082
Added:
 sandbox/trunk/pdb/doc/
Modified:
 sandbox/trunk/pdb/README.txt
 sandbox/trunk/pdb/mconnection.py
 sandbox/trunk/pdb/mpdb.py
 sandbox/trunk/pdb/mthread.py
 sandbox/trunk/pdb/test/test_mconnection.py
 sandbox/trunk/pdb/test/test_mpdb.py
Log:
Introduce protocol-ignorant exceptions for mconnection which makes it eaiser
for mpdb to look for the same exceptions regardless of which protocol is being
used for remote debugging. Add a doc directory for documentation which I'll 
start to fill in soon. Update the README.txt to reflect dicussion via e-mail
my mentor, regarding what tasks are to be completed. Fix some test cases
and documentat _why_ we need to have each connection class implement a flush
method.
Modified: sandbox/trunk/pdb/README.txt
==============================================================================
--- sandbox/trunk/pdb/README.txt	(original)
+++ sandbox/trunk/pdb/README.txt	Fri Jun 23 12:59:57 2006
@@ -12,5 +12,14 @@
 * Write more unit tests, test the pdbserver and target commands.
 * Write a signal handler that scripts can import from mpdb that, when
 the signal is received, start remote debugging.
-* info target,threads command needs to be written.
+* info [target/threads]
+ set debug threads [on|off]
+ show debug threads, command needs to be written.
+* Implement thread debugging, commands to switch between threads.
+* Lightweight and heavyweight mechanism for setting up threads.
+* Write a proper entry point to mpdb (a rewrite of the main method in mpdb.py).
+* Extend mconnection FIFO's
+* mconnection should use the exceptions that have been introduced and mpdb
+ should check for these exceptions being raised.
+* Write documentation
 
Modified: sandbox/trunk/pdb/mconnection.py
==============================================================================
--- sandbox/trunk/pdb/mconnection.py	(original)
+++ sandbox/trunk/pdb/mconnection.py	Fri Jun 23 12:59:57 2006
@@ -5,11 +5,11 @@
 NotImplementedMessage = "This method must be overriden in a subclass"
 
 ### Exceptions
-class ConnectionRefused(Exception): pass
+class ConnectionFailed(Exception): pass
 class DroppedConnection(Exception): pass
 class ReadOnClose(Exception): pass
 
-class MServerConnectionInterface(object):
+class MConnectionServerInterface(object):
 """ This is an abstract class that specifies the interface a server
 connection class must implement. If a target is given, we'll
 set up a connection on that target
@@ -35,7 +35,7 @@
 """
 raise NotImplementedError, NotImplementedMessage
 
-class MClientConnectionInterface(object):
+class MConnectionClientInterface(object):
 """ This is the interface that a client connection should implement.
 """
 def connect(self, target):
@@ -59,14 +59,14 @@
 
 
 ### This might go in a different file
-class MServerConnectionSerial(MServerConnectionInterface):
+class MConnectionServerSerial(MConnectionServerInterface):
 
 """ This server connection class that allows a connection to a
 target via a serial line. 
 """
 
 def __init__(self):
- MServerConnectionInterface.__init__(self)
+ MConnectionServerInterface.__init__(self)
 self.input = None
 self.output = None
 
@@ -100,19 +100,19 @@
 self.output.write(msg)
 self.output.flush()
 
-MClientConnectionSerial = MServerConnectionSerial
+MConnectionClientSerial = MConnectionServerSerial
 
 ### This might go in a different file
 import socket
 
-class MServerConnectionTCP(MServerConnectionInterface):
+class MConnectionServerTCP(MConnectionServerInterface):
 """This is an implementation of a server class that uses the TCP
 protocol as its means of communication.
 """
 def __init__(self):
 self.listening = False
 self._sock = self.output = self.input = None
-	MServerConnectionInterface.__init__(self)
+	MConnectionServerInterface.__init__(self)
 
 def connect(self, addr):
 """Set to allow a connection from a client. 'addr' specifies
@@ -126,8 +126,9 @@
 self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 try:
 self._sock.bind((self.host, self.port))
- except socket.error:
- raise ConnectionRefused
+ except socket.error, e:
+ # Use e[1] as a more detailed error message
+ raise ConnectionFailed, e[1]
 self._sock.listen(1)
 self.listening = True
 self.output, addr = self._sock.accept()
@@ -147,19 +148,16 @@
 if line[-1].isalpha(): line += '\n'
 return line
 
- def flush(self):
- pass
- 
 def write(self, msg):
 self.output.sendall(msg)
 
-class MClientConnectionTCP(MClientConnectionInterface):
+class MConnectionClientTCP(MConnectionClientInterface):
 """ A class that allows a connection to be made from a debugger
 to a server via TCP.
 """
 def __init__(self):
 """ Specify the address to connection to. """
- MClientConnectionInterface.__init__(self)
+ MConnectionClientInterface.__init__(self)
 self._sock = self.output = self.input = None
 
 def connect(self, addr):
@@ -180,9 +178,6 @@
 line = self._sock.recv(bufsize)
 return line
 
- def flush(self):
- pass
- 
 def disconnect(self):
 """ Close the socket to the server. """
 # We shouldn't bail if we haven't been connected yet
Modified: sandbox/trunk/pdb/mpdb.py
==============================================================================
--- sandbox/trunk/pdb/mpdb.py	(original)
+++ sandbox/trunk/pdb/mpdb.py	Fri Jun 23 12:59:57 2006
@@ -19,6 +19,7 @@
 import pydb
 import socket
 import sys
+import time
 import traceback
 
 line_prefix = '\n-> '
@@ -45,6 +46,7 @@
 self.prompt = '(MPdb)'
 self.target = 'local' # local connections by default
 self.connection = None
+ self.debug_thread = False
 
 def _rebind_input(self, new_input):
 """ This method rebinds the debugger's input to the object specified
@@ -59,6 +61,13 @@
 """
 self.stdout.flush()
 self.stdout = new_output
+ if not hasattr(self.stdout, 'flush'):
+ # Add a dummy flush method because cmdloop() in cmd.py
+ # uses this code:
+ # self.stdout.write(self.prompt)
+ # self.stdout.flush()
+ # line = self.readline()
+ self.stdout.flush = lambda: None
 
 def remote_onecmd(self, line):
 """ All commands in 'line' are sent across this object's connection
@@ -100,6 +109,36 @@
 else:
 pydb.Pdb.do_info(self, arg)
 
+ def do_thread(self, arg):
+ """ Enable thread debugging. """
+ # XXX Rocky, how are we subclassing info/set commands? This will do
+ # for now.
+ if arg == 'info':
+ if not self.debug_thread:
+ self.msg('Thread debugging is not on.')
+ return
+ try:
+ import threading
+ except ImportError:
+ self.msg('Thread debugging not available.')
+ self.msg(threading.enumerate())
+ return
+ if arg == 'debug':
+ try:
+ import threading
+ import mthread
+ except ImportError:
+ self.msg('Thread debugging not available.')
+ # We do not continue with the main thread for as long as there
+ # is another thread running. (Will change later so you can choose
+ # between threads).
+ ev = threading.Event()
+ mthread.set_event(ev)
+ threading.settrace(mthread.trace_dispatch)
+ self.msg('Thread debugging on.')
+ self.debug_thread = True
+ return
+
 # Debugger commands
 def do_attach(self, addr):
 """ Attach to a process or file outside of Pdb.
@@ -139,21 +178,23 @@
 # TODO: need to save state of current debug session
 if self.connection: self.connection.disconnect()
 try:
- from mconnection import MClientConnectionTCP
+ from mconnection import (MConnectionClientTCP,
+ ConnectionFailed)
 # Matt - Where are the connection parameters? 
- self.connection = MClientConnectionTCP()
+ self.connection = MConnectionClientTCP()
 
 except ImportError:
- self.msg('Could not import MClientConnectionTCP')
+ self.msg('Could not import MConnectionClientTCP')
 return
 elif target == 'serial':
 # Matt - Where are the connection parameters? 
 if self.connection: self.connection.disconnect()
 try:
- from mconnection import MClientConnectionSerial
- self.connection = MClientConnectionSerial()
+ from mconnection import (MConnectionClientSerial,
+ ConnectionFailed)
+ self.connection = MConnectionClientSerial()
 except ImportError:
- self.msg('Could not import MClientConnectionSerial')
+ self.msg('Could not import MConnectionClientSerial')
 return
 else:
 if '.' in target:
@@ -161,7 +202,7 @@
 # We dynamically load the class for the connection
 base = target[:target.rfind('.')]
 cls = target[target.rfind('.')+1:]
- exec 'from ' + base + ' import ' + cls
+ exec 'from ' + base + ' import (' + cls + ', ConnectionFailed)'
 else:
 try:
 __import__(target)
@@ -171,17 +212,15 @@
 self.connection = eval(target+'()')
 try: 
 self.connection.connect(addr)
- except socket.error:
- # Matt: I couldn't figure out what the right
- # exception name was to use that getts the error message.
- self.msg("Failed to connect to %s" % addr)
+ except ConnectionFailed, err:
+ self.msg("Failed to connect to %s: (%s)" % (addr, err))
 return
 # This interpreter no longer interprets commands but sends
 # them straight across this object's connection to a server.
 self.prompt = "" # Get our prompt from the server now
 line = self.connection.readline()
 self.msg_nocr(line)
- self.msg = self.connection.write
+ self._rebind_output(self.connection)
 self.onecmd = self.remote_onecmd
 self.target = 'remote'
 
@@ -218,23 +257,29 @@
 return
 if target == 'tcp':
 try:
- from mconnection import MServerConnectionTCP
- self.connection = MServerConnectionTCP()
+ from mconnection import (MConnectionServerTCP,
+ ConnectionFailed)
+ self.connection = MConnectionServerTCP()
 except ImportError:
- self.msg('Could not load MServerConnectionTCP class')
+ self.msg('Could not load MConnectionServerTCP class')
 return
 else:
 if '.' in target:
 base = target[:target.rfind('.')]
 target = target[target.rfind('.')+1:]
- exec 'from ' + base + ' import ' + target
+ exec 'from ' + base + ' import (' + target + \
+ ', ConnectionFailed)'
 else:
 __import__(target)
 self.connection = eval(target+'()')
- self.connection.connect(comm)
+ try:
+ self.connection.connect(comm)
+ except ConnectionFailed, err:
+ self.msg("Failed to connect to %s: (%s)" % (addr, err))
+ return
 self.target = 'remote'
- self._rebind_output(self.connection)
 self._rebind_input(self.connection)
+ self._rebind_output(self.connection)
 
 # This is a mess. It's only here so that I can test other features of the
 # debugger whilst I'm writing them. It will be removed at some point.
@@ -252,7 +297,7 @@
 mpdb._runscript(mainpyfile)
 if mpdb._user_requested_quit:
 break
- self.msg("The program finished and will be restarted")
+ mpdb.msg("The program finished and will be restarted")
 except SystemExit:
 # In most cases SystemExit does not warrant a post-mortem session.
 mpdb.msg("The program exited via sys.exit(). " + \
Modified: sandbox/trunk/pdb/mthread.py
==============================================================================
--- sandbox/trunk/pdb/mthread.py	(original)
+++ sandbox/trunk/pdb/mthread.py	Fri Jun 23 12:59:57 2006
@@ -1,97 +1,61 @@
 """ This file contains all code for allowing the debugging of threads. """
 
 import sys
-import thread
 import threading
 import time
-from mpdb import MPdb
 
-# Globals
-STOP = False
-t_dict = {}
-t_current = None
-
-class MyThread(threading.Thread):
- def run(self):
- while True:
- if STOP: break
- x = 2
+# Globals that are private to this file
+_threads = []
+_stop = False
+g_event = None
 
-class MTracer(MPdb):
+class MTracer(object):
 """ A class to trace a thread. """
- def __init__(self):
- MPdb.__init__(self)
- self.quitting = False
- self.botframe = None
- self.stopframe = None
- self.returnframe = None
- self.thread = threading.currentThread().getName()
+ def __init__(self, event):
+ self.thread = threading.currentThread()
 
 def trace_dispatch(self, frame, event, arg):
- try:
- if not t_dict[self.thread]:
- return
- except KeyError:
- return
- if self.quitting:
- return # None
 if event == 'line':
- return self.dispatch_line(frame)
+ print '*** line'
+ return self.trace_dispatch
 if event == 'call':
- return self.dispatch_call(frame, arg)
+ print '*** call'
+ return self.trace_dispatch
 if event == 'return':
- return self.dispatch_return(frame, arg)
+ print '*** return'
+ return self.trace_dispatch
 if event == 'exception':
- return self.dispatch_exception(frame, arg)
+ print '*** exception'
+ return self.trace_dispatch
 if event == 'c_call':
+ print '*** c_call'
 return self.trace_dispatch
 if event == 'c_exception':
+ print '*** c_exception'
 return self.trace_dispatch
 if event == 'c_return':
+ print '*** c_return'
 return self.trace_dispatch
 print 'bdb.Bdb.dispatch: unknown debugging event:', repr(event)
 return self.trace_dispatch
 
+def trace_dispatch(frame, event, arg):
+ """ Create a new MTracer object for a thread and set that thread's
+ tracing function to the MTracer objects trace method.
+ """
+ m = MTracer(g_event)
+ global _threads
+ _threads.append(threading.currentThread())
 
- # Override Pdb methods
+ sys.settrace(m.trace_dispatch)
 
- def interaction(self, frame, traceback):
- print "Thread[%s]: %s" % (self.thread, frame.f_code)
+def set_event(e):
+ global g_event
+ g_event = e
 
-def trace_dispatch(self, frame, somethinelse):
- """ Create a new MTracer object for a thread and set that threads
- tracing function to the MTracer objects trace method. Put this thread
- into the global dict of threads that we can trace.
- """
- global t_current
- m = MTracer()
 
- # This is supposed to simulate the idea of the first thread becomes
- # the current thread we're debugging. In mpdb the user will have to
- # manually change the currently debugged thread with a command.
- # This thread is not necessarily the first 'created' in a script, but
- # the first to be 'run'.
- if t_current is None:
- global t_dict
- t_current = m.thread
- t_dict[m.thread] = True
- sys.settrace(m.trace_dispatch)
 
-def threads():
- # Set the globla tracing function
- threading.settrace(trace_dispatch)
-
- global STOP, t_dict
-
- t_list = []
- # Create 3 threads
- for i in range(3):
- t = MyThread()
- t.start()
- t_list.append(t)
+ 
 
- STOP = True
+ 
 
-if __name__ == '__main__':
- threads()
- 
Modified: sandbox/trunk/pdb/test/test_mconnection.py
==============================================================================
--- sandbox/trunk/pdb/test/test_mconnection.py	(original)
+++ sandbox/trunk/pdb/test/test_mconnection.py	Fri Jun 23 12:59:57 2006
@@ -18,9 +18,9 @@
 TESTFN = 'device'
 
 sys.path.append("..")
-from mconnection import (MServerConnectionTCP, MClientConnectionTCP,
- MServerConnectionSerial, MClientConnectionSerial,
- ConnectionRefused)
+from mconnection import (MConnectionServerTCP, MConnectionClientTCP,
+ MConnectionServerSerial, MConnectionClientSerial,
+ ConnectionFailed)
 
 # Try to connect the client to addr either until we've tried MAXTRIES
 # times or until it succeeds.
@@ -35,8 +35,8 @@
 
 class TestTCPConnections(unittest.TestCase):
 def setUp(self):
- self.server = MServerConnectionTCP()
- self.client = MClientConnectionTCP()
+ self.server = MConnectionServerTCP()
+ self.client = MConnectionClientTCP()
 
 def testClientConnectToServer(self):
 """(tcp) Connect client to server. """
@@ -57,7 +57,7 @@
 
 def testDisconnectDisconnected(self):
 """(tcp) Disconnect a disconnected session. """
- s = MServerConnectionTCP()
+ s = MConnectionServerTCP()
 s.disconnect()
 s.disconnect()
 
@@ -70,14 +70,14 @@
 line = self.server.readline()
 self.assertEquals('good\n', line, 'Could not read first line.')
 
- def testConnectionRefused(self):
- """(tcp) Test refused connection. """
+ def testErrorAddressAlreadyInUse(self):
+ """(tcp) Test address already in use error. """
 thread.start_new_thread(repeatedConnect, (self.client, __addr__))
 self.server.connect(__addr__)
 
 # Set up second server on same port
- s = MServerConnectionTCP()
- self.assertRaises(ConnectionRefused, s.connect, __addr__)
+ s = MConnectionServerTCP()
+ self.assertRaises(ConnectionFailed, s.connect, __addr__)
 
 def tearDown(self):
 self.server.disconnect()
@@ -88,8 +88,8 @@
 on *nix systems is just files anyway.
 """
 def setUp(self):
- self.server = MServerConnectionSerial()
- self.client = MClientConnectionSerial()
+ self.server = MConnectionServerSerial()
+ self.client = MConnectionClientSerial()
 fd = open(TESTFN, "wr+")
 fd.close()
 self.server.connect(TESTFN)
Modified: sandbox/trunk/pdb/test/test_mpdb.py
==============================================================================
--- sandbox/trunk/pdb/test/test_mpdb.py	(original)
+++ sandbox/trunk/pdb/test/test_mpdb.py	Fri Jun 23 12:59:57 2006
@@ -18,7 +18,6 @@
 
 sys.path.append("..")
 from mpdb import MPdb
-from mconnection import MServerConnectionTCP, MClientConnectionTCP
 
 def doTargetConnect(cmds=None):
 global g_client


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /