[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