Table of Contents

Eric3 Source Documentation: Debugger  
# -*- coding: utf-8 -*-

# Copyright (c) 2002, 2003 Detlev Offenbach <detlev@die-offenbachs.de>
# Copyright (c) 2000 Phil Thompson <phil@river-bank.demon.co.uk>
#

"""
Module implementing the multithreaded Qt version of the debug client.
"""

import sys
import socket
import select
import codeop
import traceback
import os
import string
import thread

from qt import PYSIGNAL

from DebugProtocol import *
from AsyncIO import *
from Config import ConfigVarTypeStrings
from DebugThread import DebugThread

import PyCoverage
import PyProfile

def _debugclient_start_new_thread(target, args, kwargs={}):
    """
    Module function used to allow for debugging of multiple threads.
    
    The way it works is that below, we reset thread._start_new_thread to 
    this function object. Thus, providing a hook for us to see when
    threads are started. From here we forward the request onto the 
    DebugClient which will create a DebugThread object to allow tracing
    of the thread then start up the thread.
    See DebugClient.attachThread and DebugThread.DebugThread in 
    DebugThread.py
    """
    if DebugClientInstance is not None:
        return DebugClientInstance.attachThread(target, args, kwargs)
    else:
        return apply(_original_start_thread, args, kwargs)
    
# make thread hooks available to system    
_original_start_thread = thread.start_new_thread
thread.start_new_thread = _debugclient_start_new_thread

DebugClientInstance = None

def printerr(s):
    """
    Module function used for debugging the debug client.
    
    Arguments
    
        s -- the data to be printed
    """
    sys.__stderr__.write('%s\n' % str(s))
    sys.__stderr__.flush()
    
def DebugClientQAppHook():
    """
    Module function called by PyQt when the QApplication instance has been created.
    """
    if DebugClientInstance is not None:
        AsyncIO.setNotifiers(DebugClientInstance)

# Make the hook available to PyQt.
try:
    __builtins__.__dict__['__pyQtQAppHook__'] = DebugClientQAppHook
except:
    import __main__
    __main__.__builtins__.__dict__['__pyQtQAppHook__'] = DebugClientQAppHook


def DebugClientRawInput(prompt):
    """
    Replacement for the standard raw_input builtin.
    
    This function works with the Qt event loop.
    """
    if DebugClientInstance is None:
        return DebugClientOrigRawInput(prompt)

    return DebugClientInstance.raw_input(prompt)

# Use our own raw_input().
try:
    DebugClientOrigRawInput = __builtins__.__dict__['raw_input']
    __builtins__.__dict__['raw_input'] = DebugClientRawInput
except:
    DebugClientOrigRawInput = __main__.__builtins__.__dict__['raw_input']
    __main__.__builtins__.__dict__['raw_input'] = DebugClientRawInput
       
        
class DebugClient(AsyncIO):
    """
    Class implementing the client side of the debugger.

    It provides access to the Python interpeter from a debugger running in another
    process whether or not the Qt event loop is running.

    The protocol between the debugger and the client assumes that there will be
    a single source of debugger commands and a single source of Python
    statements.  Commands and statement are always exactly one line and may be
    interspersed.

    The protocol is as follows.  First the client opens a connection to the
    debugger and then sends a series of one line commands.  A command is either
    >Load<, >Step<, >StepInto<, ... or a Python statement. See DebugProtocol.py
    for a listing of valid protocol tokens.

    A Python statement consists of the statement to execute, followed (in a
    separate line) by >OK?<.  If the statement was incomplete then the response
    is >Continue<.  If there was an exception then the response is >Exception<.
    Otherwise the response is >OK<.  The reason for the >OK?< part is to
    provide a sentinal (ie. the responding >OK<) after any possible output as a
    result of executing the command.

    The client may send any other lines at any other time which should be
    interpreted as program output.

    If the debugger closes the session there is no response from the client.
    The client may close the session at any time as a result of the script
    being debugged closing or crashing.
    """
    def __init__(self):
        """
        Constructor
        """
        AsyncIO.__init__(self)

        # multi-threading support
        # NOTE: import threading here AFTER above hook, as threading cache's 
        # thread._start_new_thread.
        from threading import RLock
        
        # protection lock for synchronization
        self.clientLock = RLock()
        
        # dictionary of all threads running
        self.threads = {}
        
        # the "current" thread, basically the thread we are at a breakpoint for.
        self.currentThread = None
        
        # special objects representing the main scripts thread and frame
        self.mainThread = None
        self.mainFrame = None
                
        # The context to run the debugged program in.
        self.context = {'__name__' : '__main__'}

        # The list of complete lines to execute.
        self.buffer = ''

        self.connect(self,PYSIGNAL('lineReady'),self.handleLine)
        self.connect(self,PYSIGNAL('gotEOF'),self.sessionClose)

        self.pendingResponse = ResponseOK
        self.fncache = {}
        self.dircache = []
        
        # Global breakpoints.
        # NOTE: this and above fncache is shared by all threads.
        # See DebugThread.DebugThread.__init__ to see dictionary assignment.
        self.breakpoints = {} 
        
        self.inRawMode = 0
        self.mainProcStr = None     # used for the passive mode
        self.passive = 0            # used to indicate the passive mode
        self.running = None
        self.qt_gui_threadid = None

        self.readstream = None
        self.writestream = None
        self.errorstream = None
        
        # So much for portability.
        if sys.platform == 'win32':
            self.skipdir = sys.prefix
        else:
            self.skipdir = os.path.join(sys.prefix,'lib/python' + sys.version[0:3])

    def attachThread(self, target=None, args=None, kwargs=None, mainThread=0):
        """
        Public method to setup a thread for DebugClient to debug.
        
        If mainThread is non-zero, then we are attaching to the already 
        started mainthread of the app and the rest of the args are ignored.
        
        Arguments
        
            target -- the start function of the target thread (i.e. the user code)
            
            args -- arguments to pass to target
            
            kwargs -- keyword arguments to pass to target
            
            mainThread -- non-zero, if we are attaching to the already 
              started mainthread of the app
              
        Returns
        
            The identifier of the created thread
        """
        try:
            self.lockClient()
            newThread = DebugThread(self, target, args, kwargs, mainThread)
            ident = -1
            if mainThread:
                ident = thread.get_ident()
                self.mainThread = newThread
            else:
                ident = _original_start_thread(newThread.bootstrap, ())
            newThread.set_ident(ident)
            self.threads[newThread.get_ident()] = newThread
        finally:
            self.unlockClient()
        return ident
        
    def threadTerminated(self, dbgThread):
        """
        Public method called when a DebugThread has exited.
        
        Arguments
        
            dbgThread -- the DebugThread that has exited
        """
        try:
            self.lockClient()
            try:
                del self.threads[dbgThread.get_ident()]
            except:
                pass
        finally:
            self.unlockClient()
            
    def raw_input(self,prompt):
        """
        Public method to implement raw_input() using the event loop.
        
        Arguments
        
            prompt -- the prompt to be shown (string)
            
        Returns
            the entered string
        """
        self.write("%s%s\n" % (ResponseRaw, prompt))
        self.inRawMode = 1
        self.eventLoop()
        return self.rawLine

    def lockClient(self):
        """
        Public method to acquire the lock for this client.
        """
        self.clientLock.acquire()
        
    def unlockClient(self):
        """
        Public method to release the lock for this client.
        """
        self.clientLock.release()
        
    def handleException(self):
        """
        Private method called in the case of an exception
        
        It ensures that the debug server is informed of the raised exception.
        """
        self.pendingResponse = ResponseException

    def sessionClose(self):
        """
        Private method to close the session with the debugger and terminate.
        """
        try:
            self.set_quit()
        except:
            pass

        # clean up asyncio.
        self.disconnect()
        
        # make sure we close down our end of the socket
        # might be overkill as normally stdin, stdout and stderr
        # SHOULD be closed on exit, but it does not hurt to do it here
        self.readstream.close()
        self.writestream.close()
        self.errorstream.close()

        # Ok, go away.
        sys.exit()

    def setCurrentThread(self, id):
        """
        Private method to set the current thread.

        Arguments
        
            id -- the id the current thread should be set to.
        """
        self.lockClient()
        try:
            if id is None:
                self.currentThread = None
            else:
                self.currentThread = self.threads[id]
        finally:
            self.unlockClient()
            
            
    def handleLine(self,line):
        """
        Private method to handle the receipt of a complete line.

        It first looks for a valid protocol token at the start of the line. Thereafter
        it trys to execute the lines accumulated so far.
        
        Arguments
        
            line -- the received line
        """
        # Remove any newline.
        if line[-1] == '\n':
            line = line[:-1]

##~         printerr(line)          ##debug

        eoc = line.find('<')

                
        if eoc >= 0 and line[0] == '>':
            # Get the command part and any argument.
            cmd = line[:eoc + 1]
            arg = line[eoc + 1:]
            
            if cmd == RequestVariables:
                frmnr, scope, filter = eval(arg)
                self.dumpVariables(int(frmnr), int(scope), filter)
                return
                
            if cmd == RequestStep:
                self.currentThread.step(1)
                self.eventExit = 1
                return

            if cmd == RequestStepOver:
                self.currentThread.step(0)
                self.eventExit = 1
                return
                
            if cmd == RequestStepOut:
                self.currentThread.stepOut()
                self.eventExit = 1
                return
                
            if cmd == RequestStepQuit:
                if self.passive:
                    self.progTerminated(42)
                else:
                    self.set_quit()
                    self.eventExit = 1
                return

            if cmd == RequestContinue:
                self.currentThread.go()
                self.eventExit = 1
                return

            if cmd == RequestOK:
                self.write(self.pendingResponse + '\n')
                self.pendingResponse = ResponseOK
                return

            if cmd == RequestLoad:
                self.fncache = {}
                self.dircache = []
                sys.argv = []
                wd, fn, args = arg.split('|')
                sys.argv.append(fn)
                sys.argv.extend(args.split())
                sys.path[0] = os.path.dirname(sys.argv[0])
                if wd == '':
                    os.chdir(sys.path[0])
                else:
                    os.chdir(wd)
                self.running = sys.argv[0]
                self.inRawMode = 0
                self.threads.clear()
                self.mainFrame = None
                self.attachThread(mainThread = 1)
                
                # set the system exception handling function to ensure, that
                # we report on all unhandled exceptions
                sys.excepthook = self.unhandled_exception
                
                # This will eventually enter a local event loop.
                # Note the use of backquotes to cause a repr of self.running. The
                # need for this is on Windows os where backslash is the path separator.
                # They will get inadvertantly stripped away during the eval causing IOErrors
                # if self.running is passed as a normal str.
                self.mainThread.run('execfile(' + `self.running` + ')',self.context)
                return

            if cmd == RequestRun:
                sys.argv = []
                wd, fn, args = arg.split('|')
                sys.argv.append(fn)
                sys.argv.extend(args.split())
                sys.path[0] = os.path.dirname(sys.argv[0])
                if wd == '':
                    os.chdir(sys.path[0])
                else:
                    os.chdir(wd)

                # set the system exception handling function to ensure, that
                # we report on all unhandled exceptions
                sys.excepthook = self.unhandled_exception
                
                execfile(sys.argv[0], self.context)
                return

            if cmd == RequestCoverage:
                sys.argv = []
                wd, fn, args, erase = arg.split('|')
                sys.argv.append(fn)
                sys.argv.extend(args.split())
                sys.path[0] = os.path.dirname(sys.argv[0])
                if wd == '':
                    os.chdir(sys.path[0])
                else:
                    os.chdir(wd)

                # set the system exception handling function to ensure, that
                # we report on all unhandled exceptions
                sys.excepthook = self.unhandled_exception
                
                # generate a coverage object
                self.cover = PyCoverage.coverage(sys.argv[0])
                
                # register an exit handler to save the collected data
                try:
                    import atexit
                    atexit.register(self.cover.save)
                except ImportError:
                    sys.exitfunc = self.cover.save

                if int(erase):
                    self.cover.erase()
                self.cover.start()
                execfile(sys.argv[0], self.context)
                return

            if cmd == RequestProfile:
                sys.argv = []
                wd, fn, args, erase = arg.split('|')
                sys.argv.append(fn)
                sys.argv.extend(args.split())
                sys.path[0] = os.path.dirname(sys.argv[0])
                if wd == '':
                    os.chdir(sys.path[0])
                else:
                    os.chdir(wd)

                # set the system exception handling function to ensure, that
                # we report on all unhandled exceptions
                sys.excepthook = self.unhandled_exception
                
                # generate a profile object
                self.prof = PyProfile.PyProfile(sys.argv[0])
                
                if int(erase):
                    self.prof.erase()
                self.prof.run('execfile(' + `sys.argv[0]` + ',' + `self.context` + ')')
                return

            if cmd == RequestShutdown:
                self.sessionClose()
                return
                
            if cmd == RequestBreak:
                fn, line, set, cond = arg.split(',')
                line = int(line)
                set = int(set)

                if set:
                    if cond == 'None':
                        cond = None
                    self.mainThread.set_break(fn,line, 0, cond)
                else:
                    self.mainThread.clear_break(fn,line)

                return
                
            if cmd == RequestEval:
                try:
                    currentFrame = self.currentThread.getCurrentFrame()
                    value = eval(arg, currentFrame.f_globals,
                                      currentFrame.f_locals)
                except:
                    # Report the exception and the traceback
                    try:
                        type, value, tb = sys.exc_info()
                        sys.last_type = type
                        sys.last_value = value
                        sys.last_traceback = tb
                        tblist = traceback.extract_tb(tb)
                        del tblist[:1]
                        list = traceback.format_list(tblist)
                        if list:
                            list.insert(0, "Traceback (innermost last):\n")
                            list[len(list):] = traceback.format_exception_only(type, value)
                    finally:
                        tblist = tb = None

                    map(self.write,list)

                    self.write(ResponseException + '\n')
                    
                else:
                    self.write(str(value) + '\n')
                    self.write(ResponseOK + '\n')
                    
                return
            
            if cmd == RequestExec:
                globals = self.currentThread().getCurrentFrame().f_globals
                locals = self.currentThread().getCurrentFrame().f_locals
                try:
                    code = compile(arg + '\n', '<stdin>', 'single')
                    exec code in globals, locals
                except:
                    # Report the exception and the traceback
                    try:
                        type, value, tb = sys.exc_info()
                        sys.last_type = type
                        sys.last_value = value
                        sys.last_traceback = tb
                        tblist = traceback.extract_tb(tb)
                        del tblist[:1]
                        list = traceback.format_list(tblist)
                        if list:
                            list.insert(0, "Traceback (innermost last):\n")
                            list[len(list):] = traceback.format_exception_only(type, value)
                    finally:
                        tblist = tb = None

                    map(self.write,list)

                    self.write(ResponseException + '\n')
                    
                return
                
            if cmd == RequestBanner:
                self.write('%s%s\n' % (ResponseBanner, 
                    str((sys.version, sys.platform, 'Qt-Version, threaded'))))
                return
            
        # If we are handling raw mode input then reset the mode and break out
        # of the current event loop.
        if self.inRawMode:
            self.inRawMode = 0
            self.rawLine = line
            self.eventExit = 1
            return

        if self.buffer:
            self.buffer = self.buffer + '\n' + line
        else:
            self.buffer = line

        try:
            code = codeop.compile_command(self.buffer,self.readstream.name)
        except (OverflowError, SyntaxError, ValueError):
            # Report the exception
            sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info()
            map(self.write,traceback.format_exception_only(sys.last_type,sys.last_value))
            self.buffer = ''

            self.handleException()
        else:
            if code is None:
                self.pendingResponse = ResponseContinue
            else: 
                self.buffer = ''

                try:
                    if self.running is None:
                        exec code in self.context
                    else:
                        globals = self.currentFrame.f_globals
                        locals = self.currentFrame.f_locals
                        exec code in globals, locals
                except SystemExit:
                    self.sessionClose()
                except:
                    # Report the exception and the traceback
                    try:
                        type, value, tb = sys.exc_info()
                        sys.last_type = type
                        sys.last_value = value
                        sys.last_traceback = tb
                        tblist = traceback.extract_tb(tb)
                        del tblist[:1]
                        list = traceback.format_list(tblist)
                        if list:
                            list.insert(0, "Traceback (innermost last):\n")
                            list[len(list):] = traceback.format_exception_only(type, value)
                    finally:
                        tblist = tb = None

                    map(self.write,list)

                    self.handleException()


    def write(self,s):
        """
        Private method to write data to the output stream.
        
        Arguments
        
            s -- data to be written (string)
        """
        self.writestream.write(s)
        self.writestream.flush()

    def interact(self):
        """
        Private method to Interact with  the debugger.
        
        Returns
        
            Never
        """
        # Set the descriptors now and the notifiers when the QApplication
        # instance is created.
        global DebugClientInstance

        self.setDescriptors(self.readstream,self.writestream)
        DebugClientInstance = self

        if not self.passive:
            # At this point the Qt event loop isn't running, so simulate it.
            self.eventLoop()

    def eventLoop(self):
        """
        Private method implementing our event loop.
        """
        self.eventExit = None
        
        # make sure we set the current thread appropriately
        threadid = thread.get_ident()
        self.setCurrentThread(threadid)
        
        while self.eventExit is None:
            wrdy = []
    
            if AsyncPendingWrite(self.writestream):
                wrdy.append(self.writestream)

            if AsyncPendingWrite(self.errorstream):
                wrdy.append(self.errorstream)

            rrdy, wrdy, xrdy = select.select([self.readstream],wrdy,[])

            if self.readstream in rrdy:
                self.readReady(self.readstream.fileno())

            if self.writestream in wrdy:
                self.writeReady(self.writestream.fileno())

            if self.errorstream in wrdy:
                self.writeReady(self.errorstream.fileno())

        self.setCurrentThread(None)
        self.eventExit = None

    def connectDebugger(self,port,remoteAddress=None):
        """
        Public method to establish a session with the debugger. 
        
        It opens a network connection to the debugger, connects it to stdin, 
        stdout and stderr and saves these file objects in case the application
        being debugged redirects them itself.
        
        Arguments
        
            port -- the port number to connect to (int)
            
            remoteAddress -- the network address of the debug server host (string)
        """
        sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        if remoteAddress is None:
            sock.connect((DebugAddress,port))
        else:
            sock.connect((remoteAddress,port))
        sock.setblocking(0)

        sys.stdin = AsyncFile(sock,sys.stdin.mode,sys.stdin.name)
        sys.stdout = AsyncFile(sock,sys.stdout.mode,sys.stdout.name)
        sys.stderr = AsyncFile(sock,sys.stderr.mode,sys.stderr.name)
        
        # save the stream in case a program redirects them
        self.readstream = sys.stdin
        self.writestream = sys.stdout
        self.errorstream = sys.stderr
        
        # attach to the main thread here
        self.attachThread(mainThread=1)
        
    def absPath(self,fn):
        """
        Private method to convert a filename to an absolute name.

        sys.path is used as a set of possible prefixes. The name stays 
        relative if a file could not be found.
        
        Arguments
        
            fn -- filename (string)
            
        Returns
        
            the converted filename (string)
        """
        if os.path.isabs(fn):
            return fn

        # Check the cache.
        if self.fncache.has_key(fn):
            return self.fncache[fn]

        # Search sys.path.
        for p in sys.path:
            afn = os.path.abspath(os.path.join(p,fn))

            if os.path.exists(afn):
                self.fncache[fn] = afn
                d = os.path.dirname(afn)
                if (d not in sys.path) and (d not in self.dircache):
                    self.dircache.append(d)
                return afn

        # Search the additional directory cache
        for p in self.dircache:
            afn = os.path.abspath(os.path.join(p,fn))
            
            if os.path.exists(afn):
                self.fncache[fn] = afn
                return afn
                
        # Nothing found.
        return fn
        
    def shouldSkip(self, fn):
        """
        Public method to check if a file should be skipped.
        
        Arguments
        
            fn -- filename to be checked
            
        Returns
            non-zero if fn represents a file we are 'skipping', zero otherwise.
        """
        # Eliminate anything that is part of the Python installation.
        #XXX - this should be a user option, or an extension of the meaning of
        # 'step into', or a separate type of step altogether, or have a
        #configurable set of ignored directories.
        afn = self.absPath(fn)
        if afn.find(self.skipdir) == 0:
            return 1
            
        return 0
        
    def getRunning(self):
        """
        Public method to return the main script we are currently running.
        """
        return self.running

    def set_quit(self):
        """
        Private method to do a 'set' quit on all threads.
        """
        try:
            self.lockClient()
            try:
                for key in self.threads.keys():
                    self.threads[key].set_quit()
            except:
                pass
        finally:
            self.unlockClient()
        
    def unhandled_exception(self, exctype, excval, exctb):
        """
        Private method called to report an uncaught exception.
        
        Arguments
        
            exctype -- the type of the exception
            
            excval -- data about the exception
            
            exctb -- traceback for the exception
        """
        self.mainThread.user_exception(None, (exctype,excval,exctb), 1)
        
    def progTerminated(self,status):
        """
        Private method to tell the debugger that the program has terminated.
        
        Arguments
        
            status -- the return status
        """
        if status is None:
            status = 0
        else:
            try:
                int(status)
            except:
                status = 1

        self.set_quit()
        self.running = None
        self.write('%s%d\n' % (ResponseExit,status))
    
    def formatVariablesList(self, keylist, dict, filter = [], classdict = 0, prefix = ''):
        """
        Private method to produce a formated variables list.
        
        The dictionary passed in to it is scanned. If classdict is false,
        it builds a list of all class instances in dict. If it is
        true, we are formatting a class dictionary. In this case
        we prepend prefix to the variable names. Variables are
        only added to the list, if their type is not contained 
        in the filter list. The formated variables list (a list of 
        tuples of 3 values) and the list of class instances is returned.
        
        Arguments
        
            keylist -- keys of the dictionary
            
            dict -- the dictionary to be scanned
            
            filter -- the indices of variable types to be filtered. Variables are
              only added to the list, if their type is not contained 
              in the filter list.
            
            classdict -- boolean indicating the formating of a class or
              module dictionary. If classdict is false,
              it builds a list of all class instances in dict. If it is
              true, we are formatting a class dictionary. In this case
              we prepend prefix to the variable names.
              
            prefix -- prefix to prepend to the variable names (string)
            
        Returns
        
            A tuple consisting of a list of formatted variables and a list of
            class instances. Each variable entry is a tuple of three elements,
            the variable name, its type and value.
        """
        varlist = []
        classes = []
        
        for key in keylist:
            # filter hidden attributes (filter #0)
            if 0 in filter and str(key)[:2] == '__':
                continue
            
            # special handling for '__builtins__' (it's way too big)
            if key == '__builtins__':
                rvalue = '<module __builtin__ (built-in)>'
                valtype = 'module'
            else:
                value = dict[key]
                valtypestr = str(type(value))[1:-1]
                if string.split(valtypestr,' ',1)[0] == 'class':
                    # handle new class type of python 2.2+
                    if ConfigVarTypeStrings.index('instance') in filter:
                        continue
                    valtype = valtypestr[7:-1]
                    classes.append(key)
                else:
                    valtype = valtypestr[6:-1]
                    try:
                        if ConfigVarTypeStrings.index(valtype) in filter:
                            continue
                        if valtype in ['instance', 'module']:
                            classes.append(key)
                    except ValueError:
                        if ConfigVarTypeStrings.index('other') in filter:
                            continue
                    
                try:
                    rvalue = repr(value)
                except:
                    rvalue = ''
                    
                if classdict:
                    key = prefix + '.' + key
                    
            varlist.append((key, valtype, rvalue))
        
        return (varlist, classes)
        
    def dumpVariables(self, frmnr, scope, filter):
        """
        Private method to return the variables of a frame to the debug server.
        
        Arguments
        
            frmnr -- distance of frame reported on. 0 is the current frame (int)
            
            scope -- 1 to report global variables, 0 for local variables (int)
            
            filter -- the indices of variable types to be filtered (list of int)
        """
        f = self.currentThread.getCurrentFrame()
        
        while f is not None and frmnr > 0:
            f = f.f_back
            frmnr -= 1
        
        if f is None:
            return
        
        if scope:
            dict = f.f_globals
        else:
            dict = f.f_locals
            
            if f.f_globals is f.f_locals:
                scope = -1
                
        varlist = [scope]
        
        if scope != -1:
            keylist = dict.keys()
            
            (vlist, klist) = self.formatVariablesList(keylist, dict, filter)
            varlist = varlist + vlist
            
            if len(klist):
                for key in klist:
                    cdict = None
                    try:
                        cdict = dict[key].__dict__
                    except:
                        try:
                            slv = dict[key].__slots__
                            cdict = {}
                            for v in slv:
                                exec 'cdict[v] = dict[key].%s' % v
                        except:
                            pass
                    if cdict is not None:
                        keylist = cdict.keys()
                        (vlist, clist) = \
                            self.formatVariablesList(keylist, cdict, filter, 1, key)
                        varlist = varlist + vlist
                
        self.write('%s%s\n' % (ResponseVariables, str(varlist)))

    def startDebugger(self, filename=None, host=None, port=None):
        """
        Method used to start the remote debugger.
        
        Arguments
        
            filename -- the program to be debugged (string)
            
            host -- hostname of the debug server (string)
            
            port -- portnumber of the debug server (int)
        """
        global debugClient
        if host is None:
            host = os.getenv('ERICHOST', 'localhost')
        if port is None:
            port = os.getenv('ERICPORT', 42424)
            
        self.connectDebugger(port, socket.gethostbyname(host))
        if filename is not None:
            self.running = os.path.abspath(filename)
        else:
            try:
                self.running = os.path.abspath(sys.argv[0])
            except:
                pass
        self.passive = 1
        self.write(PassiveStartup + '\n')
        self.interact()
        
        # setup the debugger variables
        self.fncache = {}
        self.dircache = []
        self.inRawMode = 0
        self.mainFrame = None
        
        self.attachThread(mainThread=1)
        
        # set the system exception handling function to ensure, that
        # we report on all unhandled exceptions
        sys.excepthook = self.unhandled_exception
        
        # now start debugging
        self.mainThread.set_trace()
        
    
# We are normally called by the debugger to execute directly.

if __name__ == '__main__':
    try:
        port = int(sys.argv[1])
    except:
        port = -1
    try:
        remoteAddress = sys.argv[2]
    except:
        remoteAddress = None
    sys.argv = ['']
    sys.path[0] = ''
    debugClient = DebugClient()
    if port >= 0:
        debugClient.connectDebugger(port, remoteAddress)
        debugClient.interact()

Table of Contents

This document was automatically generated by HappyDoc version 2.1