[Python-checkins] r51242 - in sandbox/trunk/pdb: README.txt mconnection.py mpdb.py mproc.py mremote.py pdbserver.py test/support.py
matt.fleming
python-checkins at python.org
Sun Aug 13 18:44:36 CEST 2006
Author: matt.fleming
Date: Sun Aug 13 18:44:35 2006
New Revision: 51242
Added:
sandbox/trunk/pdb/mproc.py
sandbox/trunk/pdb/mremote.py
sandbox/trunk/pdb/pdbserver.py (contents, props changed)
Modified:
sandbox/trunk/pdb/README.txt
sandbox/trunk/pdb/mconnection.py
sandbox/trunk/pdb/mpdb.py
sandbox/trunk/pdb/test/support.py
Log:
Create some factory functions to decouple the instation of the conection
objects from mpdb.py. Unit tests need fixing.
Modified: sandbox/trunk/pdb/README.txt
==============================================================================
--- sandbox/trunk/pdb/README.txt (original)
+++ sandbox/trunk/pdb/README.txt Sun Aug 13 18:44:35 2006
@@ -51,6 +51,8 @@
input from the user and doesn't work remotely.
* pdbserver using a TCP connection uses setsockopt() REUSEADDR by default.
Need some way to make this configurable. `set reuseaddr' ?
+* The style in which mpdb.py is written is too 'amorphous'. Need to at least
+ separate process, thread and communication code out.
-=[FIXED]=-
* Can restart program remotely with new arguments.
Modified: sandbox/trunk/pdb/mconnection.py
==============================================================================
--- sandbox/trunk/pdb/mconnection.py (original)
+++ sandbox/trunk/pdb/mconnection.py Sun Aug 13 18:44:35 2006
@@ -36,6 +36,45 @@
"""
raise NotImplementedError, NotImplementedMessage
+def import_hook(target):
+ cls = target[target.rfind('.')+1:]
+ target = target[:target.rfind('.')]
+ pkg = __import__(target, globals(), locals(), [])
+ return getattr(pkg, cls)
+
+class MConnectionClientFactory:
+
+ """A factory class that provides a connection for use with a client
+ for example, with a target function.
+ """
+ @staticmethod
+ def create(target):
+ if 'tcp' in target:
+ return MConnectionClientTCP()
+ elif 'serial' in target:
+ return MConnectionSerial()
+ elif 'fifo' in target:
+ return MConnectionClientFIFO()
+ else:
+ return import_hook(target)
+
+
+class MConnectionServerFactory:
+
+ """A factory class that provides a connection to be used with
+ a pdbserver.
+ """
+ @staticmethod
+ def create(target):
+ if 'tcp' in target:
+ return MConnectionServerTCP()
+ elif 'serial' in target:
+ return MConnectionSerial()
+ elif 'fifo' in target:
+ return MConnectionServerFIFO()
+ else:
+ return import_hook(target)
+
### This might go in a different file
# Not, serial protocol does not require the distinction between server and
# client.
Modified: sandbox/trunk/pdb/mpdb.py
==============================================================================
--- sandbox/trunk/pdb/mpdb.py (original)
+++ sandbox/trunk/pdb/mpdb.py Sun Aug 13 18:44:35 2006
@@ -53,16 +53,6 @@
self.target = 'local' # local connections by default
self.lastcmd = ''
self.connection = None
- self.debugger_name = 'mpdb'
-
- self.setcmds.add('debug-signal', self.set_debug_signal)
- self.setcmds.add('target-address', self.set_target_address)
- self.showcmds.add('debug-signal', self.show_debug_signal)
- self.showcmds.add('target-address', self.show_target_address)
- self.infocmds.add('target', self.info_target)
-
- self.target_addr = "" # target address used by 'attach'
- self.debug_signal = None # The signal used by 'attach'
# We need to be in control of self.stdin
self.use_rawinput = False
@@ -76,349 +66,15 @@
if not hasattr(self.stdout, 'flush'):
self.stdout.flush = lambda: None
- def remote_onecmd(self, line):
- """ All commands in 'line' are sent across this object's connection
- instance variable.
- """
- if not line:
- # Execute the previous command
- line = self.lastcmd
- # This is the simplest way I could think of to do this without
- # breaking any of the inherited code from pydb/pdb. If we're a
- # remote client, always call 'rquit' (remote quit) when connected to
- # a pdbserver. This executes extra code to allow the client and server
- # to quit cleanly.
- if 'quit'.startswith(line):
- line = 'rquit'
- self.connection.write(line)
- # Reset the onecmd method
- self.onecmd = lambda x: pydb.Pdb.onecmd(self, x)
- self.do_rquit(None)
- return
- if 'detach'.startswith(line):
- self.connection.write('rdetach')
- self.do_detach(None)
- self.connection.write(line)
- ret = self.connection.readline()
- if ret == '':
- self.errmsg('Connection closed unexpectedly')
- self.onecmd = lambda x: pydb.Pdb.onecmd(self, x)
- self.do_rquit(None)
- # The output from the command that we've just sent to the server
- # is returned along with the prompt of that server. So we keep reading
- # until we find our prompt.
- i = 1
- while self.local_prompt not in ret:
- if i == 100:
- # We're probably _never_ going to get that data and that
- # connection is probably dead.
- self.errmsg('Connection died unexpectedly')
- self.onecmd = lambda x: pydb.Pdb.onecmd(self, x)
- self.do_rquit(None)
- else:
- ret += self.connection.readline()
- i += 1
-
- # Some 'special' actions must be taken depending on the data returned
- if 'restart_now' in ret:
- self.connection.write('ACK:restart_now')
- self.errmsg('Pdbserver restarting..')
- # We've acknowledged a restart, which means that a new pdbserver
- # process is started, so we have to connect all over again.
- self._disconnect()
- time.sleep(3.0)
- if not self.do_target(self.target_addr):
- # We cannot trust these variables below to be in a
- # stable state. i.e. if the pdbserver doesn't come back up.
- self.onecmd = lambda x: pydb.Pdb.onecmd(self, x)
- return
- self.msg_nocr(ret)
- self.lastcmd = line
- return
-
- def _disconnect(self):
- """ Disconnect a connection. """
- self.connection.disconnect()
- self.connection = None
- self.target = 'local'
- if hasattr(self, 'local_prompt'):
- self.prompt = self.local_prompt
- self.onecmd = lambda x: pydb.Pdb.onecmd(self, x)
-
def help_mpdb(self, *arg):
help()
- def set_debug_signal(self, args):
- """Set the signal sent to a process to trigger debugging."""
- try:
- exec 'from signal import %s' % args[1]
- except ImportError:
- self.errmsg('Invalid signal')
- return
- self.debug_signal = args[1]
- self.msg('debug-signal set to: %s' % self.debug_signal)
-
- def set_target_address(self, args):
- """Set the address of a target."""
- self.target_addr = "".join(["%s " % a for a in args[1:]])
- self.target_addr = self.target_addr.strip()
- self.msg('target address set to %s' % self.target_addr)
-
- def show_debug_signal(self, arg):
- """Show the signal currently used for triggering debugging
- of an already running process.
- """
- if not self.debug_signal:
- self.msg('debug-signal not set.')
- return
- self.msg('debug-signal is %s' % self.debug_signal)
-
- def show_target_address(self, arg):
- """Show the address of the current target."""
- self.msg('target-address is %s.' % self.target_addr.__repr__())
-
- def info_target(self, args):
- """Display information about the current target."""
- self.msg('target is %s' % self.target)
-
- # Debugger commands
- def do_attach(self, addr):
- """ Attach to a process or file outside of Pdb.
-This command attaches to another target, of the same type as your last
-"target" command. The command may take as argument a process id or a
-device file. For a process id, you must have permission to send the
-process a signal, and it must have the same effective uid as the debugger.
-When using "attach" with a process id, the debugger finds the
-program running in the process, looking first in the current working
-directory, or (if not found there) using the source file search path
-(see the "directory" command). You can also use the "file" command
-to specify the program, and to load its symbol table.
-"""
- if not self.target_addr:
- self.errmsg('No target address is set')
- return
- try:
- pid = int(addr)
- except ValueError:
- # no PID
- self.errmsg('You must specify a process ID to attach to.')
- return
- if not self.debug_signal:
- from signal import SIGUSR1
- self.debug_signal = SIGUSR1
- try:
- os.kill(pid, self.debug_signal)
- except OSError, err:
- self.errmsg(err)
- return
-
- # XXX this still needs removing
- time.sleep(1.0)
-
- self.do_target(self.target_addr)
-
- def do_target(self, args):
- """ Connect to a target machine or process.
-The first argument is the type or protocol of the target machine
-(which can be the name of a class that is available either in the current
-working directory or in Python's PYTHONPATH environment variable).
-Remaining arguments are interpreted by the target protocol. For more
-information on the arguments for a particular protocol, type
-`help target ' followed by the protocol name.
-
-List of target subcommands:
-
-target serial device-name -- Use a remote computer via a serial line
-target tcp hostname:port -- Use a remote computer via a socket connection
-"""
- if not args:
- args = self.target_addr
- try:
- target, addr = args.split(' ')
- except ValueError:
- self.errmsg('Invalid arguments')
- return False
- # If addr is ':PORT' fill in 'localhost' as the hostname
- if addr[0] == ':':
- addr = 'localhost'+addr[:]
- if 'remote' in self.target:
- self.errmsg('Already connected to a remote machine.')
- return False
- if target == 'tcp':
- # TODO: need to save state of current debug session
- if self.connection: self.connection.disconnect()
- try:
- from mconnection import (MConnectionClientTCP,
- ConnectionFailed)
- self.connection = MConnectionClientTCP()
-
- except ImportError:
- self.errmsg('Could not import MConnectionClientTCP')
- return False
- elif target == 'serial':
- if self.connection: self.connection.disconnect()
- try:
- from mconnection import (MConnectionSerial,
- ConnectionFailed)
- self.connection = MConnectionSerial()
- except ImportError:
- self.errmsg('Could not import MConnectionSerial')
- return False
- elif target == 'fifo':
- if self.connection: self.connection.disconnect()
- try:
- from mconnection import (MConnectionClientFIFO,
- ConnectionFailed)
- self.connection = MConnectionClientFIFO()
- except ImportError:
- self.errmsg('Could not import MConnectionClientFIFO')
- return False
- else:
- cls = target[target.rfind('.')+1:]
- path = target[:target.rfind('.')]
- exec 'from ' + path + ' import ' + cls + ', ConnectionFailed'
- self.connection = eval(cls+'()')
- try:
- self.connection.connect(addr)
- except ConnectionFailed, err:
- self.errmsg("Failed to connect to %s: (%s)" % (addr, err))
- return False
- # This interpreter no longer interprets commands but sends
- # them straight across this object's connection to a server.
- # XXX: In the remote_onecmd method we use the local_prompt string
- # to find where the end of the message from the server is. We
- # really need a way to get the prompt from the server for checking
- # in remote_onecmd, because it may be different to this client's.
- self.local_prompt = self.prompt
- self.prompt = ""
- self.target_addr = target + " " + addr
- line = self.connection.readline()
- if line == '':
- self.errmsg('Connection closed unexpectedly')
- self.do_quit(None)
- while '(MPdb)' not in line:
- line = self.connection.readline()
- self.msg_nocr(line)
- self.onecmd = self.remote_onecmd
- self.target = 'remote-client'
- return True
-
- def do_detach(self, args):
- """ Detach a process or file previously attached.
-If a process, it is no longer traced, and it continues its execution. If
-you were debugging a file, the file is closed and Pdb no longer accesses it.
-"""
- raise KeyboardInterrupt
-
- def do_pdbserver(self, args):
- """ Allow a debugger to connect to this session.
-The first argument is the type or protocol that is used for this connection
-(which can be the name of a class that is avaible either in the current
-working directory or in Python's PYTHONPATH environtment variable).
-The next argument is protocol specific arguments (e.g. hostname and
-port number for a TCP connection, or a serial device for a serial line
-connection). The next argument is the filename of the script that is
-being debugged. The rest of the arguments are passed as arguments to the
-script file and are optional. For more information on the arguments for a
-particular protocol, type `help pdbserver ' followed by the protocol name.
-The syntax for this command is,
-
-`pdbserver ConnectionClass comm scriptfile [args ...]'
-
-"""
- try:
- target, comm = args.split(' ')
- except ValueError:
- self.errmsg('Invalid arguments')
- return
- if 'remote' in self.target:
- self.errmsg('Already connected remotely')
- return
- if target == 'tcp':
- try:
- from mconnection import (MConnectionServerTCP,
- ConnectionFailed)
- self.connection = MConnectionServerTCP()
- except ImportError:
- self.errmsg('Could not load MConnectionServerTCP class')
- return
- elif target == 'serial':
- try:
- from mconnection import (MConnectionSerial,
- ConnectionFailed)
- self.connection = MConnectionSerial()
- except ImportError:
- self.errmsg('Could not load MConnectionSerial class')
- return
- else:
- if '.' in target:
- base = target[:target.rfind('.')]
- target = target[target.rfind('.')+1:]
- try:
- exec 'from ' + base + ' import (' + target + \
- ', ConnectionFailed)'
- except ImportError:
- self.errmsg('Unknown protocol')
- return
- else:
- try:
- __import__(target)
- except ImportError:
- self.errmsg('Unknown protocol')
- return
- self.connection = eval(target+'()')
- try:
- self.msg('Listening on: %s' % comm)
- self.connection.connect(comm)
- except ConnectionFailed, err:
- self.errmsg("Failed to connect to %s: (%s)" % (comm, err))
- return
- self.pdbserver_addr = comm
- self.target = 'remote-pdbserver'
- self._rebind_input(self.connection)
- self._rebind_output(self.connection)
-
- def do_rquit(self, arg):
- """ Quit a remote debugging session. The program being executed
-is aborted.
-"""
- if self.target == 'local':
- self.errmsg('Connected locally; cannot remotely quit')
- return
- self._rebind_output(self.orig_stdout)
- self._rebind_input(self.orig_stdin)
- self._disconnect()
- self.target = 'local'
- sys.settrace(None)
- self.do_quit(None)
-
- def do_restart(self, arg):
- """ Extend pydb.do_restart to signal to any clients connected on
- a debugger's connection that this debugger is going to be restarted.
- All state is lost, and a new copy of the debugger is used.
+ def _pdbserver_hook(self, addr):
+ """This method allows a pdbserver to be created from inside the
+ 'default' debugger. This may be needed for instance, if this process
+ receives a signal to start debugging from another process.
"""
- # We don't proceed with the restart until the action has been
- # ACK'd by any connected clients
- if self.connection != None:
- self.msg('restart_now\n(MPdb)')
- line = ""
- while not 'ACK:restart_now' in line:
- line = self.connection.readline()
- self.do_rquit(None)
- else:
- self.msg("Re exec'ing\n\t%s" % self._sys_argv)
- os.execvp(self._sys_argv[0], self._sys_argv)
-
- def do_rdetach(self, arg):
- """ The rdetach command is performed on the pdbserver, it cleans
- things up when the client has detached from this process.
- Control returns to the file being debugged and execution of that
- file continues.
- """
- self._rebind_input(self.orig_stdin)
- self._rebind_output(self.orig_stdout)
-
- self.cmdqueue.append('continue') # Continue execution
+ mpdb.pdbserver(addr, self)
def pdbserver(addr, m):
""" This method sets up a pdbserver debugger that allows debuggers
@@ -426,6 +82,8 @@
tcp = 'tcp mydomainname.com:9876'
serial = 'serial /dev/ttyC0'
"""
+ from mremote import RemoteWrapperServer
+ m = RemoteWrapperServer(m)
m.do_pdbserver(addr)
while True:
try:
@@ -447,10 +105,13 @@
'opts' an the OptionParser object. If opts.target is True, call do_target.
If opts.pid is true, call do_attach.
"""
- mpdb.reset()
if opts.target:
+ from mremote import RemoteWrapperClient
+ mpdb = RemoteWrapperClient(mpdb)
mpdb.do_target(addr)
elif opts.pid:
+ from mproc import ProcessWrapper
+ mpdb = ProcessWrapper(mpdb)
pid = addr[:addr.find(' ')]
addr = addr[addr.find(' ')+1:]
mpdb.do_set('target ' + addr)
@@ -534,6 +195,8 @@
# Clear up namespace
del frame.f_globals['mpdb']
+ from mremote import RemoteWrapperServer
+ m = RemoteWrapperServer(m)
m.do_pdbserver(pdbserver_addr)
m.set_trace(frame)
Added: sandbox/trunk/pdb/mproc.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/pdb/mproc.py Sun Aug 13 18:44:35 2006
@@ -0,0 +1,57 @@
+"""This file contains all code to allow debugging of another process."""
+from mremote import RemoteWrapperClient
+
+class ProcessWrapper(RemoteWrapperClient):
+
+ """This class wraps an MPdb instance to provide functionality for
+ debugging processes.
+ """
+ def __init__(self, mpdb_object):
+ RemoteWrapperClient.__init__(self, mpdb_object)
+ self.debug_signal = None
+
+ def do_detach(self, args):
+ """ Detach a process or file previously attached.
+ If a process, it is no longer traced, and it continues its
+ execution. If you were debugging a file, the file is closed and
+ Pdb no longer accesses it.
+ """
+ raise KeyboardInterrupt
+
+ def do_attach(self, addr):
+ """ Attach to a process or file outside of Pdb.
+This command attaches to another target, of the same type as your last
+"target" command. The command may take as argument a process id or a
+device file. For a process id, you must have permission to send the
+process a signal, and it must have the same effective uid as the debugger.
+When using "attach" with a process id, the debugger finds the
+program running in the process, looking first in the current working
+directory, or (if not found there) using the source file search path
+(see the "directory" command). You can also use the "file" command
+to specify the program, and to load its symbol table.
+"""
+ if not self.target_addr:
+ self.errmsg('No target address is set')
+ return
+ try:
+ pid = int(addr)
+ except ValueError:
+ # no PID
+ self.errmsg('You must specify a process ID to attach to.')
+ return
+ if not self.debug_signal:
+ from signal import SIGUSR1
+ self.debug_signal = SIGUSR1
+ try:
+ import os
+ os.kill(pid, self.debug_signal)
+ except OSError, err:
+ self.errmsg(err)
+ return
+
+ # XXX this still needs removing
+ import time
+ time.sleep(1.0)
+
+ self.do_target(self.target_addr)
+
Added: sandbox/trunk/pdb/mremote.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/pdb/mremote.py Sun Aug 13 18:44:35 2006
@@ -0,0 +1,258 @@
+"""Contains all code for remote/out-of-process connections."""
+
+from mpdb import MPdb
+
+class RemoteWrapper(MPdb):
+
+ """An abstract wrapper class that provides common functionality
+ for an object that wishes to use remote communication. Classes
+ should inherit from this class if they wish to build an object
+ capable of remote communication.
+ """
+ def __init__(self, mpdb_object):
+ MPdb.__init__(self)
+ self.mpdb = mpdb_object
+ self.connection = None
+
+ def _disconnect(self):
+ """ Disconnect a connection. """
+ self.connection.disconnect()
+ self.connection = None
+ self.target = 'local'
+ if hasattr(self, 'local_prompt'):
+ import pydb
+ self.prompt = self.local_prompt
+ self.onecmd = lambda x: pydb.Pdb.onecmd(self, x)
+
+ def do_restart(self, arg):
+ """ Extend pydb.do_restart to signal to any clients connected on
+ a debugger's connection that this debugger is going to be restarted.
+ All state is lost, and a new copy of the debugger is used.
+ """
+ # We don't proceed with the restart until the action has been
+ # ACK'd by any connected clients
+ if self.connection != None:
+ self.msg('restart_now\n(MPdb)')
+ line = ""
+ while not 'ACK:restart_now' in line:
+ line = self.connection.readline()
+ self.do_rquit(None)
+ else:
+ self.msg("Re exec'ing\n\t%s" % self._sys_argv)
+ os.execvp(self._sys_argv[0], self._sys_argv)
+
+ def do_rquit(self, arg):
+ """ Quit a remote debugging session. The program being executed
+ is aborted.
+ """
+ if self.target == 'local':
+ self.errmsg('Connected locally; cannot remotely quit')
+ return
+ self._rebind_output(self.orig_stdout)
+ self._rebind_input(self.orig_stdin)
+ self._disconnect()
+ self.target = 'local'
+ import sys
+ sys.settrace(None)
+ self.do_quit(None)
+
+class RemoteWrapperServer(RemoteWrapper):
+ def __init__(self, mpdb_object):
+ RemoteWrapper.__init__(self, mpdb_object)
+
+ def do_pdbserver(self, args):
+ """ Allow a debugger to connect to this session.
+The first argument is the type or protocol that is used for this connection
+(which can be the name of a class that is avaible either in the current
+working directory or in Python's PYTHONPATH environtment variable).
+The next argument is protocol specific arguments (e.g. hostname and
+port number for a TCP connection, or a serial device for a serial line
+connection). The next argument is the filename of the script that is
+being debugged. The rest of the arguments are passed as arguments to the
+script file and are optional. For more information on the arguments for a
+particular protocol, type `help pdbserver ' followed by the protocol name.
+The syntax for this command is,
+
+`pdbserver ConnectionClass comm scriptfile [args ...]'
+
+"""
+ try:
+ target, comm = args.split(' ')
+ except ValueError:
+ self.errmsg('Invalid arguments')
+ return
+ if 'remote' in self.target:
+ self.errmsg('Already connected remotely')
+ return
+ if self.connection: self.connection.disconnect()
+ from mconnection import MConnectionServerFactory, ConnectionFailed
+ self.connection = MConnectionServerFactory.create(target)
+ if self.connection is None:
+ self.msg('Unknown protocol')
+ return
+ try:
+ self.msg('Listening on: %s' % comm)
+ self.connection.connect(comm)
+ except ConnectionFailed, err:
+ self.errmsg("Failed to connect to %s: (%s)" % (comm, err))
+ return
+ self.pdbserver_addr = comm
+ self.target = 'remote-pdbserver'
+ self._rebind_input(self.connection)
+ self._rebind_output(self.connection)
+
+ def do_rdetach(self, arg):
+ """ The rdetach command is performed on the pdbserver, it cleans
+ things up when the client has detached from this process.
+ Control returns to the file being debugged and execution of that
+ file continues.
+ """
+ self._rebind_input(self.orig_stdin)
+ self._rebind_output(self.orig_stdout)
+
+ self.cmdqueue.append('continue') # Continue execution
+
+class RemoteWrapperClient(RemoteWrapper):
+
+ """This is a wrapper class that provides remote capability to an
+ instance of mpdb.MPdb.
+ """
+ def __init__(self, mpdb_object):
+ RemoteWrapper.__init__(self, mpdb_object)
+ self.setcmds.add('target-address', self.set_target_address)
+ self.showcmds.add('target-address', self.show_target_address)
+ self.infocmds.add('target', self.info_target)
+ self.target_addr = ''
+
+ def remote_onecmd(self, line):
+ """ All commands in 'line' are sent across this object's connection
+ instance variable.
+ """
+ if not line:
+ # Execute the previous command
+ line = self.lastcmd
+ # This is the simplest way I could think of to do this without
+ # breaking any of the inherited code from pydb/pdb. If we're a
+ # remote client, always call 'rquit' (remote quit) when connected to
+ # a pdbserver. This executes extra code to allow the client and server
+ # to quit cleanly.
+ if 'quit'.startswith(line):
+ line = 'rquit'
+ self.connection.write(line)
+ # Reset the onecmd method
+ self.onecmd = lambda x: pydb.Pdb.onecmd(self, x)
+ self.do_rquit(None)
+ return
+ if 'detach'.startswith(line):
+ self.connection.write('rdetach')
+ self.do_detach(None)
+ self.connection.write(line)
+ ret = self.connection.readline()
+ if ret == '':
+ self.errmsg('Connection closed unexpectedly')
+ self.onecmd = lambda x: pydb.Pdb.onecmd(self, x)
+ self.do_rquit(None)
+ # The output from the command that we've just sent to the server
+ # is returned along with the prompt of that server. So we keep reading
+ # until we find our prompt.
+ i = 1
+ while self.local_prompt not in ret:
+ if i == 100:
+ # We're probably _never_ going to get that data and that
+ # connection is probably dead.
+ self.errmsg('Connection died unexpectedly')
+ self.onecmd = lambda x: pydb.Pdb.onecmd(self, x)
+ self.do_rquit(None)
+ else:
+ ret += self.connection.readline()
+ i += 1
+
+ # Some 'special' actions must be taken depending on the data returned
+ if 'restart_now' in ret:
+ self.connection.write('ACK:restart_now')
+ self.errmsg('Pdbserver restarting..')
+ # We've acknowledged a restart, which means that a new pdbserver
+ # process is started, so we have to connect all over again.
+ self._disconnect()
+ time.sleep(3.0)
+ if not self.do_target(self.target_addr):
+ # We cannot trust these variables below to be in a
+ # stable state. i.e. if the pdbserver doesn't come back up.
+ self.onecmd = lambda x: pydb.Pdb.onecmd(self, x)
+ return
+ self.msg_nocr(ret)
+ self.lastcmd = line
+ return
+
+ def do_target(self, args):
+ """ Connect to a target machine or process.
+The first argument is the type or protocol of the target machine
+(which can be the name of a class that is available either in the current
+working directory or in Python's PYTHONPATH environment variable).
+Remaining arguments are interpreted by the target protocol. For more
+information on the arguments for a particular protocol, type
+`help target ' followed by the protocol name.
+
+List of target subcommands:
+
+target serial device-name -- Use a remote computer via a serial line
+target tcp hostname:port -- Use a remote computer via a socket connection
+"""
+ if not args:
+ args = self.target_addr
+ try:
+ target, addr = args.split(' ')
+ except ValueError:
+ self.errmsg('Invalid arguments')
+ return False
+ # If addr is ':PORT' fill in 'localhost' as the hostname
+ if addr[0] == ':':
+ addr = 'localhost'+addr[:]
+ if 'remote' in self.target:
+ self.errmsg('Already connected to a remote machine.')
+ return False
+ if self.connection: self.connection.disconnect()
+ from mconnection import MConnectionClientFactory, ConnectionFailed
+ self.connection = MConnectionClientFactory.create(target)
+ try:
+ self.connection.connect(addr)
+ except ConnectionFailed, err:
+ self.errmsg("Failed to connect to %s: (%s)" % (addr, err))
+ return False
+ # This interpreter no longer interprets commands but sends
+ # them straight across this object's connection to a server.
+ # XXX: In the remote_onecmd method we use the local_prompt string
+ # to find where the end of the message from the server is. We
+ # really need a way to get the prompt from the server for checking
+ # in remote_onecmd, because it may be different to this client's.
+ self.local_prompt = self.prompt
+ self.prompt = ""
+ self.target_addr = target + " " + addr
+ line = self.connection.readline()
+ if line == '':
+ self.errmsg('Connection closed unexpectedly')
+ self.do_quit(None)
+ while '(MPdb)' not in line:
+ line = self.connection.readline()
+ self.msg_nocr(line)
+ self.onecmd = self.remote_onecmd
+ self.target = 'remote-client'
+ return True
+
+ def set_target_address(self, args):
+ """Set the address of a target."""
+ self.target_addr = "".join(["%s " % a for a in args[1:]])
+ self.target_addr = self.target_addr.strip()
+ self.msg('target address set to %s' % self.target_addr)
+
+ def show_target_address(self, arg):
+ """Show the address of the current target."""
+ self.msg('target-address is %s.' % self.target_addr.__repr__())
+
+ def info_target(self, args):
+ """Display information about the current target."""
+ self.msg('target is %s' % self.target)
+
+
+
+
Added: sandbox/trunk/pdb/pdbserver.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/pdb/pdbserver.py Sun Aug 13 18:44:35 2006
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+"""This file is analogous to the GNU debugger's "gdbserver" command."""
+
+from mpdb import MPdb, Restart
+from mremote import RemoteWrapperServer
+import sys
+
+#1. Parse commands
+#2. Create MPdb object capable of pdbservering
+#3. Run script
+
+# Should this be merged into one 'mremote.py' file with target-stuff too?
+def do_pdbserver():
+ """ This method sets up a pdbserver debugger that allows debuggers
+ to connect to 'addr', which a protocol-specific address, i.e.
+ tcp = 'tcp mydomainname.com:9876'
+ serial = 'serial /dev/ttyC0'
+ """
+ args = sys.argv[1:] # skip script filename
+ if len(args) < 2:
+ # Need address and file to debug
+ return
+ addr = args[0]
+ filename = args[1]
+
+ # args to pass to the script
+ if args[2:]:
+ script_args = args[2:]
+
+ server = MPdb()
+ server = RemoteWrapperServer(server)
+ server.do_pdbserver(addr)
+ while True:
+ try:
+ server.runscript(filename)
+ if server._user_requested_quit: break
+ except Restart:
+ sys.argv = list(server._program_sys_argv)
+ server.msg('Restarting')
+
+if __name__ == '__main__':
+ do_pdbserver()
Modified: sandbox/trunk/pdb/test/support.py
==============================================================================
--- sandbox/trunk/pdb/test/support.py (original)
+++ sandbox/trunk/pdb/test/support.py Sun Aug 13 18:44:35 2006
@@ -17,19 +17,20 @@
def write(self, msg):
pass
-from mpdb import MPdb
+from mremote import RemoteWrapperClient, RemoteWrapperServer
-class MPdbTest(MPdb):
+class MPdbTest(RemoteWrapperClient):
""" This class provides a version of the MPdb class that is
suitable for use in unit testing. All output is captured and
stored in this object's 'lines' instance variable.
"""
- def __init__(self, cmds=[]):
+ def __init__(self, mpdb, cmds=[]):
""" The optional argument 'cmds' is a list specifying commands
that this instance should interpret in it's 'cmdloop' method.
"""
- MPdb.__init__(self)
+ RemoteWrapperClient.__init__(self, mpdb)
self.lines = []
+ self.curframe = None
self.cmdqueue = cmds
self.botframe = None
@@ -37,19 +38,24 @@
self.lines.append(msg)
-class Pdbserver(threading.Thread, MPdb):
+class Pdbserver(threading.Thread, RemoteWrapperServer):
""" This class provides a fully functional pdbserver that runs
in a separate thread. The optional argument 'addr' specifies a
protocol and an address to use for pdbserver.
"""
- def __init__(self, addr=None):
- MPdb.__init__(self)
+ def __init__(self, mpdb, addr=None):
+ RemoteWrapperServer.__init__(self, mpdb)
threading.Thread.__init__(self)
self._sys_argv = ['python', '-c', '"pass"']
self.botframe = None
+ self.curframe = None
+ self.lines = []
if not addr:
self.addr = 'tcp localhost:8000'
+
+ def msg_nocr(self, msg, out=None):
+ self.lines.append(msg)
def run(self):
self.do_pdbserver(self.addr)
@@ -65,7 +71,17 @@
def __init__(self, cmds=[]):
threading.Thread.__init__(self)
MPdbTest.__init__(self, cmds)
+
+from mproc import ProcessWrapper
+class MPdbTestProc(ProcessWrapper):
+ def __init__(self, mpdb):
+ ProcessWrapper.__init__(self, mpdb)
+ self.lines = []
+ self.curframe = None
+
+ def msg_nocr(self, msg, out=None):
+ self.lines.append(msg)
More information about the Python-checkins
mailing list