Debugger/DebugServer.py

changeset 0
de9c2efb9d02
child 7
c679fb30c8f3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Debugger/DebugServer.py	Mon Dec 28 16:03:33 2009 +0000
@@ -0,0 +1,1256 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2007 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the debug server.
+"""
+
+import sys
+import os
+import time
+import signal
+
+from PyQt4.QtCore import *
+from PyQt4.QtGui import QMessageBox
+from PyQt4.QtNetwork import QTcpServer, QHostAddress, QHostInfo
+
+from E4Gui.E4Application import e4App
+
+from BreakPointModel import BreakPointModel
+from WatchPointModel import WatchPointModel
+import DebugClientCapabilities
+
+import Preferences
+import Utilities
+
+
+DebuggerInterfaces = [
+    "DebuggerInterfacePython",
+    "DebuggerInterfacePython3",
+    "DebuggerInterfaceRuby",
+    "DebuggerInterfaceNone",
+]
+
+class DebugServer(QTcpServer):
+    """
+    Class implementing the debug server embedded within the IDE.
+    
+    @signal clientProcessStdout emitted after the client has sent some output
+            via stdout
+    @signal clientProcessStderr emitted after the client has sent some output
+            via stderr
+    @signal clientOutput emitted after the client has sent some output
+    @signal clientRawInputSent emitted after the data was sent to the debug client
+    @signal clientLine(filename, lineno, forStack) emitted after the debug client 
+            has executed a line of code
+    @signal clientStack(stack) emitted after the debug client has executed a
+            line of code
+    @signal clientThreadList(currentId, threadList) emitted after a thread list
+            has been received
+    @signal clientThreadSet emitted after the client has acknowledged the change
+            of the current thread
+    @signal clientVariables(scope, variables) emitted after a variables dump has 
+            been received
+    @signal clientVariable(scope, variables) emitted after a dump for one class 
+            variable has been received
+    @signal clientStatement(boolean) emitted after an interactive command has
+            been executed. The parameter is 0 to indicate that the command is
+            complete and 1 if it needs more input.
+    @signal clientException(exception) emitted after an exception occured on the 
+            client side
+    @signal clientSyntaxError(exception) emitted after a syntax error has been detected
+            on the client side
+    @signal clientExit(int) emitted with the exit status after the client has exited
+    @signal clientClearBreak(filename, lineno) emitted after the debug client
+            has decided to clear a temporary breakpoint
+    @signal clientBreakConditionError(fn, lineno) emitted after the client has signaled
+            a syntax error in a breakpoint condition
+    @signal clientClearWatch(condition) emitted after the debug client
+            has decided to clear a temporary watch expression
+    @signal clientWatchConditionError(condition) emitted after the client has signaled
+            a syntax error in a watch expression
+    @signal clientRawInput(prompt, echo) emitted after a raw input request was received
+    @signal clientBanner(banner) emitted after the client banner was received
+    @signal clientCapabilities(int capabilities, QString cltype) emitted after the clients
+            capabilities were received
+    @signal clientCompletionList(completionList, text) emitted after the client
+            the commandline completion list and the reworked searchstring was
+            received from the client
+    @signal passiveDebugStarted emitted after the debug client has connected in
+            passive debug mode
+    @signal clientGone emitted if the client went away (planned or unplanned)
+    @signal utPrepared(nrTests, exc_type, exc_value) emitted after the client has
+            loaded a unittest suite
+    @signal utFinished emitted after the client signalled the end of the unittest
+    @signal utStartTest(testname, testdocu) emitted after the client has started 
+            a test
+    @signal utStopTest emitted after the client has finished a test
+    @signal utTestFailed(testname, exc_info) emitted after the client reported 
+            a failed test
+    @signal utTestErrored(testname, exc_info) emitted after the client reported 
+            an errored test
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        QTcpServer.__init__(self)
+        
+        # create our models
+        self.breakpointModel = BreakPointModel(self)
+        self.watchpointModel = WatchPointModel(self)
+        self.watchSpecialCreated = \
+            self.trUtf8("created", "must be same as in EditWatchpointDialog") 
+        self.watchSpecialChanged = \
+            self.trUtf8("changed", "must be same as in EditWatchpointDialog")
+        
+        self.networkInterface = Preferences.getDebugger("NetworkInterface")
+        if self.networkInterface == "all":
+            hostAddress = QHostAddress("0.0.0.0")#QHostAddress.Any)
+        elif self.networkInterface == "allv6":
+            hostAddress = QHostAddress("::")#QHostAddress.AnyIPv6)
+        else:
+            hostAddress = QHostAddress(Preferences.getDebugger("NetworkInterface"))
+        if Preferences.getDebugger("PassiveDbgEnabled"):
+            socket = Preferences.getDebugger("PassiveDbgPort") # default: 42424
+            self.listen(hostAddress, socket)
+            self.passive = True
+            self.passiveClientExited = False
+        else:
+            self.listen(hostAddress)
+            self.passive = False
+        
+        self.debuggerInterface = None
+        self.debugging = False
+        self.clientProcess = None
+        self.clientType = \
+            Preferences.Prefs.settings.value('DebugClient/Type', 
+                QVariant('Python')).toString()
+        self.lastClientType = ''
+        self.__autoClearShell = False
+        
+        self.connect(self, SIGNAL("clientClearBreak"), self.__clientClearBreakPoint)
+        self.connect(self, SIGNAL("clientClearWatch"), self.__clientClearWatchPoint)
+        self.connect(self, SIGNAL("newConnection()"), self.__newConnection)
+        
+        self.connect(self.breakpointModel, 
+            SIGNAL("rowsAboutToBeRemoved(const QModelIndex &, int, int)"), 
+            self.__deleteBreakPoints)
+        self.connect(self.breakpointModel,
+            SIGNAL("dataAboutToBeChanged(const QModelIndex &, const QModelIndex &)"),
+            self.__breakPointDataAboutToBeChanged)
+        self.connect(self.breakpointModel,
+            SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"),
+            self.__changeBreakPoints)
+        self.connect(self.breakpointModel,
+            SIGNAL("rowsInserted(const QModelIndex &, int, int)"),
+            self.__addBreakPoints)
+        
+        self.connect(self.watchpointModel, 
+            SIGNAL("rowsAboutToBeRemoved(const QModelIndex &, int, int)"), 
+            self.__deleteWatchPoints)
+        self.connect(self.watchpointModel,
+            SIGNAL("dataAboutToBeChanged(const QModelIndex &, const QModelIndex &)"),
+            self.__watchPointDataAboutToBeChanged)
+        self.connect(self.watchpointModel, 
+            SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"),
+            self.__changeWatchPoints)
+        self.connect(self.watchpointModel,
+            SIGNAL("rowsInserted(const QModelIndex &, int, int)"),
+            self.__addWatchPoints)
+        
+        self.__registerDebuggerInterfaces()
+        
+    def getHostAddress(self, localhost):
+        """
+        Public method to get the IP address or hostname the debug server is listening.
+        
+        @param localhost flag indicating to return the address for localhost (boolean)
+        @return IP address or hostname (string)
+        """
+        if self.networkInterface == "all":
+            if localhost:
+                return "127.0.0.1"
+            else:
+                return "%s@@v4" % QHostInfo.localHostName()
+        elif self.networkInterface == "allv6":
+            if localhost:
+                return "::1"
+            else:
+                return "%s@@v6" % QHostInfo.localHostName()
+        else:
+            return self.networkInterface
+        
+    def preferencesChanged(self):
+        """
+        Public slot to handle the preferencesChanged signal.
+        """
+        self.__registerDebuggerInterfaces()
+        
+    def __registerDebuggerInterfaces(self):
+        """
+        Private method to register the available debugger interface modules.
+        """
+        self.__clientCapabilities = {}
+        self.__clientAssociations = {}
+        
+        for interface in DebuggerInterfaces:
+            modName = "Debugger.%s" % interface
+            mod = __import__(modName)
+            components = modName.split('.')
+            for comp in components[1:]:
+                mod = getattr(mod, comp)
+            
+            clientLanguage, clientCapabilities, clientExtensions = \
+                mod.getRegistryData()
+            if clientLanguage:
+                self.__clientCapabilities[clientLanguage] = clientCapabilities
+                for extension in clientExtensions:
+                    if extension not in self.__clientAssociations:
+                        self.__clientAssociations[extension] = clientLanguage
+        
+    def getSupportedLanguages(self, shellOnly = False):
+        """
+        Public slot to return the supported programming languages.
+        
+        @param shellOnly flag indicating only languages supporting an
+            interactive shell should be returned
+        @return list of supported languages (list of strings)
+        """
+        languages = self.__clientCapabilities.keys()
+        try:
+            del languages[languages.index("None")]
+        except ValueError:
+            pass    # it is not in the list
+        
+        if shellOnly:
+            languages = \
+                [lang for lang in languages \
+                 if self.__clientCapabilities[lang] & DebugClientCapabilities.HasShell]
+        
+        return languages[:]
+        
+    def getExtensions(self, language):
+        """
+        Public slot to get the extensions associated with the given language.
+        
+        @param language language to get extensions for (string)
+        @return tuple of extensions associated with the language (tuple of strings)
+        """
+        extensions = []
+        for ext, lang in self.__clientAssociations.items():
+            if lang == language:
+                extensions.append(ext)
+        
+        return tuple(extensions)
+        
+    def __createDebuggerInterface(self, clientType = None):
+        """
+        Private slot to create the debugger interface object.
+        
+        @param clientType type of the client interface to be created (string)
+        """
+        if self.lastClientType != self.clientType or clientType is not None:
+            if clientType is None:
+                clientType = self.clientType
+            if clientType == "Python":
+                from DebuggerInterfacePython import DebuggerInterfacePython
+                self.debuggerInterface = DebuggerInterfacePython(self, self.passive)
+            elif clientType == "Python3":
+                from DebuggerInterfacePython3 import DebuggerInterfacePython3
+                self.debuggerInterface = DebuggerInterfacePython3(self, self.passive)
+            elif clientType == "Ruby":
+                from DebuggerInterfaceRuby import DebuggerInterfaceRuby
+                self.debuggerInterface = DebuggerInterfaceRuby(self, self.passive)
+            elif clientType == "None":
+                from DebuggerInterfaceNone import DebuggerInterfaceNone
+                self.debuggerInterface = DebuggerInterfaceNone(self, self.passive)
+            else:
+                from DebuggerInterfaceNone import DebuggerInterfaceNone
+                self.debuggerInterface = DebuggerInterfaceNone(self, self.passive)
+                self.clientType = "None"
+        
+    def __setClientType(self, clType):
+        """
+        Private method to set the client type.
+        
+        @param clType type of client to be started (string)
+        """
+        if clType is not None and clType in self.getSupportedLanguages():
+            self.clientType = clType
+            ok = Preferences.Prefs.settings.setValue('DebugClient/Type', 
+                QVariant(self.clientType))
+        
+    def startClient(self, unplanned = True, clType = None, forProject = False, 
+                    runInConsole = False):
+        """
+        Public method to start a debug client.
+        
+        @keyparam unplanned flag indicating that the client has died (boolean)
+        @keyparam clType type of client to be started (string)
+        @keyparam forProject flag indicating a project related action (boolean)
+        @keyparam runInConsole flag indicating to start the debugger in a 
+            console window (boolean)
+        """
+        if not self.passive or not self.passiveClientExited: 
+            if self.debuggerInterface and self.debuggerInterface.isConnected():
+                self.shutdownServer()
+                self.emit(SIGNAL('clientGone'), unplanned & self.debugging)
+        
+        if clType:
+            self.__setClientType(clType)
+        
+        # only start the client, if we are not in passive mode
+        if not self.passive:
+            if self.clientProcess:
+                self.disconnect(self.clientProcess, SIGNAL("readyReadStandardError()"), 
+                                self.__clientProcessError)
+                self.disconnect(self.clientProcess, SIGNAL("readyReadStandardOutput()"), 
+                                self.__clientProcessOutput)
+                self.clientProcess.close()
+                self.clientProcess.kill()
+                self.clientProcess.waitForFinished(10000)
+                self.clientProcess = None
+            
+            self.__createDebuggerInterface()
+            if forProject:
+                project = e4App().getObject("Project")
+                if not project.isDebugPropertiesLoaded():
+                    self.clientProcess, isNetworked = \
+                        self.debuggerInterface.startRemote(self.serverPort(), 
+                                                           runInConsole)
+                else:
+                    self.clientProcess, isNetworked = \
+                        self.debuggerInterface.startRemoteForProject(self.serverPort(), 
+                                                                     runInConsole)
+            else:
+                self.clientProcess, isNetworked = \
+                    self.debuggerInterface.startRemote(self.serverPort(), runInConsole)
+            
+            if self.clientProcess:
+                self.connect(self.clientProcess, SIGNAL("readyReadStandardError()"), 
+                             self.__clientProcessError)
+                self.connect(self.clientProcess, SIGNAL("readyReadStandardOutput()"), 
+                             self.__clientProcessOutput)
+                
+                if not isNetworked:
+                    # the client is connected through stdin and stdout
+                    # Perform actions necessary, if client type has changed
+                    if self.lastClientType != self.clientType:
+                        self.lastClientType = self.clientType
+                        self.remoteBanner()
+                    elif self.__autoClearShell:
+                        self.__autoClearShell = False
+                        self.remoteBanner()
+                    
+                    self.debuggerInterface.flush()
+        else:
+            self.__createDebuggerInterface("None")
+
+    def __clientProcessOutput(self):
+        """
+        Private slot to process client output received via stdout.
+        """
+        output = unicode(self.clientProcess.readAllStandardOutput())
+        self.emit(SIGNAL("clientProcessStdout"), output)
+        
+    def __clientProcessError(self):
+        """
+        Private slot to process client output received via stderr.
+        """
+        error = unicode(self.clientProcess.readAllStandardError())
+        self.emit(SIGNAL("clientProcessStderr"), error)
+        
+    def __clientClearBreakPoint(self, fn, lineno):
+        """
+        Private slot to handle the clientClearBreak signal.
+        
+        @param fn filename of breakpoint to clear (string)
+        @param lineno line number of breakpoint to clear (integer)
+        """
+        if self.debugging:
+            index = self.breakpointModel.getBreakPointIndex(fn, lineno)
+            self.breakpointModel.deleteBreakPointByIndex(index)
+
+    def __deleteBreakPoints(self, parentIndex, start, end):
+        """
+        Private slot to delete breakpoints.
+        
+        @param parentIndex index of parent item (QModelIndex)
+        @param start start row (integer)
+        @param end end row (integer)
+        """
+        if self.debugging:
+            for row in range(start, end+1):
+                index = self.breakpointModel.index(row, 0, parentIndex)
+                fn, lineno = self.breakpointModel.getBreakPointByIndex(index)[0:2]
+                self.remoteBreakpoint(fn, lineno, False)
+
+    def __changeBreakPoints(self, startIndex, endIndex):
+        """
+        Private slot to set changed breakpoints.
+        
+        @param indexes indexes of changed breakpoints.
+        """
+        if self.debugging:
+            self.__addBreakPoints(QModelIndex(), startIndex.row(), endIndex.row())
+
+    def __breakPointDataAboutToBeChanged(self, startIndex, endIndex):
+        """
+        Private slot to handle the dataAboutToBeChanged signal of the breakpoint model.
+        
+        @param startIndex start index of the rows to be changed (QModelIndex)
+        @param endIndex end index of the rows to be changed (QModelIndex)
+        """
+        if self.debugging:
+            self.__deleteBreakPoints(QModelIndex(), startIndex.row(), endIndex.row())
+        
+    def __addBreakPoints(self, parentIndex, start, end):
+        """
+        Private slot to add breakpoints.
+        
+        @param parentIndex index of parent item (QModelIndex)
+        @param start start row (integer)
+        @param end end row (integer)
+        """
+        if self.debugging:
+            for row in range(start, end+1):
+                index = self.breakpointModel.index(row, 0, parentIndex)
+                fn, line, cond, temp, enabled, ignorecount = \
+                    self.breakpointModel.getBreakPointByIndex(index)[:6]
+                self.remoteBreakpoint(fn, line, True, cond, temp)
+                if not enabled:
+                    self.__remoteBreakpointEnable(fn, line, False)
+                if ignorecount:
+                    self.__remoteBreakpointIgnore(fn, line, ignorecount)
+
+    def __makeWatchCondition(self, cond, special):
+        """
+        Private method to construct the condition string.
+        
+        @param cond condition (string)
+        @param special special condition (string)
+        @return condition string (string)
+        """
+        if special == "":
+            _cond = cond
+        else:
+            if special == self.watchSpecialCreated:
+                _cond = "%s ??created??" % cond
+            elif special == self.watchSpecialChanged:
+                _cond = "%s ??changed??" % cond
+        return _cond
+        
+    def __splitWatchCondition(self, cond):
+        """
+        Private method to split a remote watch expression.
+        
+        @param cond remote expression (string)
+        @return tuple of local expression (string) and special condition (string)
+        """
+        if cond.endswith(" ??created??"):
+            cond, special = cond.split()
+            special = self.watchSpecialCreated
+        elif cond.endswith(" ??changed??"):
+            cond, special = cond.split()
+            special = self.watchSpecialChanged
+        else:
+            return cond, ""
+        
+    def __clientClearWatchPoint(self, condition):
+        """
+        Private slot to handle the clientClearWatch signal.
+        
+        @param condition expression of watch expression to clear (string)
+        """
+        if self.debugging:
+            cond, special = self.__splitWatchCondition(condition)
+            index = self.watchpointModel.getWatchPointIndex(cond, special)
+            self.watchpointModel.deleteWatchPointByIndex(index)
+        
+    def __deleteWatchPoints(self, parentIndex, start, end):
+        """
+        Private slot to delete watch expressions.
+        
+        @param parentIndex index of parent item (QModelIndex)
+        @param start start row (integer)
+        @param end end row (integer)
+        """
+        if self.debugging:
+            for row in range(start, end+1):
+                index = self.watchpointModel.index(row, 0, parentIndex)
+                cond, special = self.watchpointModel.getWatchPointByIndex(index)[0:2]
+                cond = self.__makeWatchCondition(cond, special)
+                self.__remoteWatchpoint(cond, False)
+        
+    def __watchPointDataAboutToBeChanged(self, startIndex, endIndex):
+        """
+        Private slot to handle the dataAboutToBeChanged signal of the 
+        watch expression model.
+        
+        @param startIndex start index of the rows to be changed (QModelIndex)
+        @param endIndex end index of the rows to be changed (QModelIndex)
+        """
+        if self.debugging:
+            self.__deleteWatchPoints(QModelIndex(), startIndex.row(), endIndex.row())
+        
+    def __addWatchPoints(self, parentIndex, start, end):
+        """
+        Private slot to set a watch expression.
+        
+        @param parentIndex index of parent item (QModelIndex)
+        @param start start row (integer)
+        @param end end row (integer)
+        """
+        if self.debugging:
+            for row in range(start, end + 1):
+                index = self.watchpointModel.index(row, 0, parentIndex)
+                cond, special, temp, enabled, ignorecount = \
+                    self.watchpointModel.getWatchPointByIndex(index)[:5]
+                cond = self.__makeWatchCondition(cond, special)
+                self.__remoteWatchpoint(cond, True, temp)
+                if not enabled:
+                    self.__remoteWatchpointEnable(cond, False)
+                if ignorecount:
+                    self.__remoteWatchpointIgnore(cond, ignorecount)
+        
+    def __changeWatchPoints(self, startIndex, endIndex):
+        """
+        Private slot to set changed watch expressions.
+        
+        @param startIndex start index of the rows to be changed (QModelIndex)
+        @param endIndex end index of the rows to be changed (QModelIndex)
+        """
+        if self.debugging:
+            self.__addWatchPoints(QModelIndex(), startIndex.row(), endIndex.row())
+        
+    def getClientCapabilities(self, type):
+        """
+        Public method to retrieve the debug clients capabilities.
+        
+        @param type debug client type (string)
+        @return debug client capabilities (integer)
+        """
+        try:
+            return self.__clientCapabilities[type]
+        except KeyError:
+            return 0    # no capabilities
+        
+    def __newConnection(self):
+        """
+        Private slot to handle a new connection.
+        """
+        sock = self.nextPendingConnection()
+        peerAddress = sock.peerAddress().toString()
+        if peerAddress not in Preferences.getDebugger("AllowedHosts"):
+            # the peer is not allowed to connect
+            res = QMessageBox.warning(None,
+                self.trUtf8("Connection from illegal host"),
+                self.trUtf8("""<p>A connection was attempted by the"""
+                    """ illegal host <b>{0}</b>. Accept this connection?</p>""")\
+                    .format(peerAddress),
+                QMessageBox.StandardButtons(\
+                    QMessageBox.No | \
+                    QMessageBox.Yes),
+                QMessageBox.No)
+            if res == QMessageBox.No:
+                sock.abort()
+                return
+            else:
+                allowedHosts = Preferences.getDebugger("AllowedHosts")
+                allowedHosts.append(peerAddress)
+                Preferences.setDebugger("AllowedHosts", allowedHosts)
+        
+        if self.passive:
+            self.__createDebuggerInterface(Preferences.getDebugger("PassiveDbgType"))
+        
+        accepted = self.debuggerInterface.newConnection(sock)
+        if accepted:
+            # Perform actions necessary, if client type has changed
+            if self.lastClientType != self.clientType:
+                self.lastClientType = self.clientType
+                self.remoteBanner()
+            elif self.__autoClearShell:
+                self.__autoClearShell = False
+                self.remoteBanner()
+            elif self.passive:
+                self.remoteBanner()
+            
+            self.debuggerInterface.flush()
+
+    def shutdownServer(self):
+        """
+        Public method to cleanly shut down.
+        
+        It closes our socket and shuts down
+        the debug client. (Needed on Win OS)
+        """
+        if self.debuggerInterface is not None:
+            self.debuggerInterface.shutdown()
+
+    def remoteEnvironment(self, env):
+        """
+        Public method to set the environment for a program to debug, run, ...
+        
+        @param env environment settings (string)
+        """
+        envlist = Utilities.parseEnvironmentString(env)
+        envdict = {}
+        for el in envlist:
+            try:
+                key, value = el.split('=', 1)
+                if value.startswith('"') or value.startswith("'"):
+                    value = value[1:-1]
+                envdict[key] = value
+            except UnpackError:
+                pass
+        self.debuggerInterface.remoteEnvironment(envdict)
+        
+    def remoteLoad(self, fn, argv, wd, env, autoClearShell = True,
+                   tracePython = False, autoContinue = True, forProject = False, 
+                   runInConsole = False, autoFork = False, forkChild = False):
+        """
+        Public method to load a new program to debug.
+        
+        @param fn the filename to debug (string)
+        @param argv the commandline arguments to pass to the program (string)
+        @param wd the working directory for the program (string)
+        @param env environment settings (string)
+        @keyparam autoClearShell flag indicating, that the interpreter window should
+            be cleared (boolean)
+        @keyparam tracePython flag indicating if the Python library should be traced
+            as well (boolean)
+        @keyparam autoContinue flag indicating, that the debugger should not stop
+            at the first executable line (boolean)
+        @keyparam forProject flag indicating a project related action (boolean)
+        @keyparam runInConsole flag indicating to start the debugger in a 
+            console window (boolean)
+        @keyparam autoFork flag indicating the automatic fork mode (boolean)
+        @keyparam forkChild flag indicating to debug the child after forking (boolean)
+        """
+        self.__autoClearShell = autoClearShell
+        self.__autoContinue = autoContinue
+        
+        # Restart the client
+        try:
+            self.__setClientType(self.__clientAssociations[os.path.splitext(fn)[1]])
+        except KeyError:
+            self.__setClientType('Python')    # assume it is a Python file
+        self.startClient(False, forProject = forProject, runInConsole = runInConsole)
+        
+        self.remoteEnvironment(env)
+        
+        self.debuggerInterface.remoteLoad(fn, argv, wd, tracePython, autoContinue, 
+                                          autoFork, forkChild)
+        self.debugging = True
+        self.__restoreBreakpoints()
+        self.__restoreWatchpoints()
+
+    def remoteRun(self, fn, argv, wd, env, autoClearShell = True,
+                  forProject = False, runInConsole = False):
+        """
+        Public method to load a new program to run.
+        
+        @param fn the filename to run (string)
+        @param argv the commandline arguments to pass to the program (string)
+        @param wd the working directory for the program (string)
+        @param env environment settings (string)
+        @keyparam autoClearShell flag indicating, that the interpreter window should
+            be cleared (boolean)
+        @keyparam forProject flag indicating a project related action (boolean)
+        @keyparam runInConsole flag indicating to start the debugger in a 
+            console window (boolean)
+        """
+        self.__autoClearShell = autoClearShell
+        
+        # Restart the client
+        try:
+            self.__setClientType(self.__clientAssociations[os.path.splitext(fn)[1]])
+        except KeyError:
+            self.__setClientType('Python')    # assume it is a Python file
+        self.startClient(False, forProject = forProject, runInConsole = runInConsole)
+        
+        self.remoteEnvironment(env)
+        
+        self.debuggerInterface.remoteRun(fn, argv, wd)
+        self.debugging = False
+
+    def remoteCoverage(self, fn, argv, wd, env, autoClearShell = True,
+                       erase = False, forProject = False, runInConsole = False):
+        """
+        Public method to load a new program to collect coverage data.
+        
+        @param fn the filename to run (string)
+        @param argv the commandline arguments to pass to the program (string)
+        @param wd the working directory for the program (string)
+        @param env environment settings (string)
+        @keyparam autoClearShell flag indicating, that the interpreter window should
+            be cleared (boolean)
+        @keyparam erase flag indicating that coverage info should be 
+            cleared first (boolean)
+        @keyparam forProject flag indicating a project related action (boolean)
+        @keyparam runInConsole flag indicating to start the debugger in a 
+            console window (boolean)
+        """
+        self.__autoClearShell = autoClearShell
+        
+        # Restart the client
+        try:
+            self.__setClientType(self.__clientAssociations[os.path.splitext(fn)[1]])
+        except KeyError:
+            self.__setClientType('Python')    # assume it is a Python file
+        self.startClient(False, forProject = forProject, runInConsole = runInConsole)
+        
+        self.remoteEnvironment(env)
+        
+        self.debuggerInterface.remoteCoverage(fn, argv, wd, erase)
+        self.debugging = False
+
+    def remoteProfile(self, fn, argv, wd, env, autoClearShell = True,
+                      erase = False, forProject = False, 
+                      runInConsole = False):
+        """
+        Public method to load a new program to collect profiling data.
+        
+        @param fn the filename to run (string)
+        @param argv the commandline arguments to pass to the program (string)
+        @param wd the working directory for the program (string)
+        @param env environment settings (string)
+        @keyparam autoClearShell flag indicating, that the interpreter window should
+            be cleared (boolean)
+        @keyparam erase flag indicating that timing info should be cleared first (boolean)
+        @keyparam forProject flag indicating a project related action (boolean)
+        @keyparam runInConsole flag indicating to start the debugger in a 
+            console window (boolean)
+        """
+        self.__autoClearShell = autoClearShell
+        
+        # Restart the client
+        try:
+            self.__setClientType(self.__clientAssociations[os.path.splitext(fn)[1]])
+        except KeyError:
+            self.__setClientType('Python')    # assume it is a Python file
+        self.startClient(False, forProject = forProject, runInConsole = runInConsole)
+        
+        self.remoteEnvironment(env)
+        
+        self.debuggerInterface.remoteProfile(fn, argv, wd, erase)
+        self.debugging = False
+
+    def remoteStatement(self, stmt):
+        """
+        Public method to execute a Python statement.  
+        
+        @param stmt the Python statement to execute (string). It
+              should not have a trailing newline.
+        """
+        self.debuggerInterface.remoteStatement(stmt)
+
+    def remoteStep(self):
+        """
+        Public method to single step the debugged program.
+        """
+        self.debuggerInterface.remoteStep()
+
+    def remoteStepOver(self):
+        """
+        Public method to step over the debugged program.
+        """
+        self.debuggerInterface.remoteStepOver()
+
+    def remoteStepOut(self):
+        """
+        Public method to step out the debugged program.
+        """
+        self.debuggerInterface.remoteStepOut()
+
+    def remoteStepQuit(self):
+        """
+        Public method to stop the debugged program.
+        """
+        self.debuggerInterface.remoteStepQuit()
+
+    def remoteContinue(self, special = False):
+        """
+        Public method to continue the debugged program.
+        
+        @param special flag indicating a special continue operation
+        """
+        self.debuggerInterface.remoteContinue(special)
+
+    def remoteBreakpoint(self, fn, line, set, cond=None, temp=False):
+        """
+        Public method to set or clear a breakpoint.
+        
+        @param fn filename the breakpoint belongs to (string)
+        @param line linenumber of the breakpoint (int)
+        @param set flag indicating setting or resetting a breakpoint (boolean)
+        @param cond condition of the breakpoint (string)
+        @param temp flag indicating a temporary breakpoint (boolean)
+        """
+        self.debuggerInterface.remoteBreakpoint(fn, line, set, cond, temp)
+        
+    def __remoteBreakpointEnable(self, fn, line, enable):
+        """
+        Private method to enable or disable a breakpoint.
+        
+        @param fn filename the breakpoint belongs to (string)
+        @param line linenumber of the breakpoint (int)
+        @param enable flag indicating enabling or disabling a breakpoint (boolean)
+        """
+        self.debuggerInterface.remoteBreakpointEnable(fn, line, enable)
+        
+    def __remoteBreakpointIgnore(self, fn, line, count):
+        """
+        Private method to ignore a breakpoint the next couple of occurrences.
+        
+        @param fn filename the breakpoint belongs to (string)
+        @param line linenumber of the breakpoint (int)
+        @param count number of occurrences to ignore (int)
+        """
+        self.debuggerInterface.remoteBreakpointIgnore(fn, line, count)
+        
+    def __remoteWatchpoint(self, cond, set, temp = False):
+        """
+        Private method to set or clear a watch expression.
+        
+        @param cond expression of the watch expression (string)
+        @param set flag indicating setting or resetting a watch expression (boolean)
+        @param temp flag indicating a temporary watch expression (boolean)
+        """
+        # cond is combination of cond and special (s. watch expression viewer)
+        self.debuggerInterface.remoteWatchpoint(cond, set, temp)
+    
+    def __remoteWatchpointEnable(self, cond, enable):
+        """
+        Private method to enable or disable a watch expression.
+        
+        @param cond expression of the watch expression (string)
+        @param enable flag indicating enabling or disabling a watch expression (boolean)
+        """
+        # cond is combination of cond and special (s. watch expression viewer)
+        self.debuggerInterface.remoteWatchpointEnable(cond, enable)
+    
+    def __remoteWatchpointIgnore(self, cond, count):
+        """
+        Private method to ignore a watch expression the next couple of occurrences.
+        
+        @param cond expression of the watch expression (string)
+        @param count number of occurrences to ignore (int)
+        """
+        # cond is combination of cond and special (s. watch expression viewer)
+        self.debuggerInterface.remoteWatchpointIgnore(cond, count)
+    
+    def remoteRawInput(self,s):
+        """
+        Public method to send the raw input to the debugged program.
+        
+        @param s the raw input (string)
+        """
+        self.debuggerInterface.remoteRawInput(s)
+        self.emit(SIGNAL('clientRawInputSent'))
+        
+    def remoteThreadList(self):
+        """
+        Public method to request the list of threads from the client.
+        """
+        self.debuggerInterface.remoteThreadList()
+        
+    def remoteSetThread(self, tid):
+        """
+        Public method to request to set the given thread as current thread.
+        
+        @param tid id of the thread (integer)
+        """
+        self.debuggerInterface.remoteSetThread(tid)
+        
+    def remoteClientVariables(self, scope, filter, framenr = 0):
+        """
+        Public method to request the variables of the debugged program.
+        
+        @param scope the scope of the variables (0 = local, 1 = global)
+        @param filter list of variable types to filter out (list of int)
+        @param framenr framenumber of the variables to retrieve (int)
+        """
+        self.debuggerInterface.remoteClientVariables(scope, filter, framenr)
+        
+    def remoteClientVariable(self, scope, filter, var, framenr = 0):
+        """
+        Public method to request the variables of the debugged program.
+        
+        @param scope the scope of the variables (0 = local, 1 = global)
+        @param filter list of variable types to filter out (list of int)
+        @param var list encoded name of variable to retrieve (string)
+        @param framenr framenumber of the variables to retrieve (int)
+        """
+        self.debuggerInterface.remoteClientVariable(scope, filter, var, framenr)
+        
+    def remoteClientSetFilter(self, scope, filter):
+        """
+        Public method to set a variables filter list.
+        
+        @param scope the scope of the variables (0 = local, 1 = global)
+        @param filter regexp string for variable names to filter out (string)
+        """
+        self.debuggerInterface.remoteClientSetFilter(scope, filter)
+        
+    def remoteEval(self, arg):
+        """
+        Public method to evaluate arg in the current context of the debugged program.
+        
+        @param arg the arguments to evaluate (string)
+        """
+        self.debuggerInterface.remoteEval(arg)
+        
+    def remoteExec(self, stmt):
+        """
+        Public method to execute stmt in the current context of the debugged program.
+        
+        @param stmt statement to execute (string)
+        """
+        self.debuggerInterface.remoteExec(stmt)
+        
+    def remoteBanner(self):
+        """
+        Public slot to get the banner info of the remote client.
+        """
+        self.debuggerInterface.remoteBanner()
+        
+    def remoteCapabilities(self):
+        """
+        Public slot to get the debug clients capabilities.
+        """
+        self.debuggerInterface.remoteCapabilities()
+        
+    def remoteCompletion(self, text):
+        """
+        Public slot to get the a list of possible commandline completions
+        from the remote client.
+        
+        @param text the text to be completed (string)
+        """
+        self.debuggerInterface.remoteCompletion(text)
+
+    def remoteUTPrepare(self, fn, tn, tfn, cov, covname, coverase):
+        """
+        Public method to prepare a new unittest run.
+        
+        @param fn the filename to load (string)
+        @param tn the testname to load (string)
+        @param tfn the test function name to load tests from (string)
+        @param cov flag indicating collection of coverage data is requested
+        @param covname filename to be used to assemble the coverage caches
+                filename (string)
+        @param coverase flag indicating erasure of coverage data is requested (boolean)
+        """
+        # Restart the client if there is already a program loaded.
+        try:
+            self.__setClientType(self.__clientAssociations[os.path.splitext(fn)[1]])
+        except KeyError:
+            self.__setClientType('Python')    # assume it is a Python file
+        self.startClient(False)
+        
+        self.debuggerInterface.remoteUTPrepare(fn, tn, tfn, cov, covname, coverase)
+        self.debugging = False
+        
+    def remoteUTRun(self):
+        """
+        Public method to start a unittest run.
+        """
+        self.debuggerInterface.remoteUTRun()
+        
+    def remoteUTStop(self):
+        """
+        public method to stop a unittest run.
+        """
+        self.debuggerInterface.remoteUTStop()
+        
+    def clientOutput(self, line):
+        """
+        Public method to process a line of client output.
+        
+        @param line client output (string)
+        """
+        self.emit(SIGNAL('clientOutput'), line)
+        
+    def clientLine(self, filename, lineno, forStack = False):
+        """
+        Public method to process client position feedback.
+        
+        @param filename name of the file currently being executed (string)
+        @param lineno line of code currently being executed (integer)
+        @param forStack flag indicating this is for a stack dump (boolean)
+        """
+        self.emit(SIGNAL('clientLine'), filename, lineno, forStack)
+        
+    def clientStack(self, stack):
+        """
+        Public method to process a client's stack information.
+        
+        @param stack list of stack entries. Each entry is a tuple of three
+            values giving the filename, linenumber and method
+            (list of lists of (string, integer, string))
+        """
+        self.emit(SIGNAL('clientStack'), stack)
+        
+    def clientThreadList(self, currentId, threadList):
+        """
+        Public method to process the client thread list info.
+        
+        @param currentID id of the current thread (integer)
+        @param threadList list of dictionaries containing the thread data
+        """
+        self.emit(SIGNAL('clientThreadList'), currentId, threadList)
+        
+    def clientThreadSet(self):
+        """
+        Public method to handle the change of the client thread.
+        """
+        self.emit(SIGNAL('clientThreadSet'))
+        
+    def clientVariables(self, scope, variables):
+        """
+        Public method to process the client variables info.
+        
+        @param scope scope of the variables (-1 = empty global, 1 = global, 0 = local)
+        @param variables the list of variables from the client
+        """
+        self.emit(SIGNAL('clientVariables'), scope, variables)
+        
+    def clientVariable(self, scope, variables):
+        """
+        Public method to process the client variable info.
+        
+        @param scope scope of the variables (-1 = empty global, 1 = global, 0 = local)
+        @param variables the list of members of a classvariable from the client
+        """
+        self.emit(SIGNAL('clientVariable'), scope, variables)
+        
+    def clientStatement(self, more):
+        """
+        Public method to process the input response from the client.
+        
+        @param more flag indicating that more user input is required
+        """
+        self.emit(SIGNAL('clientStatement'), more)
+        
+    def clientException(self, exceptionType, exceptionMessage, stackTrace):
+        """
+        Public method to process the exception info from the client.
+        
+        @param exceptionType type of exception raised (string)
+        @param exceptionMessage message given by the exception (string)
+        @param stackTrace list of stack entries with the exception position
+            first. Each stack entry is a list giving the filename and the linenumber.
+        """
+        self.emit(SIGNAL('clientException'), exceptionType, exceptionMessage, stackTrace)
+        
+    def clientSyntaxError(self, message, filename, lineNo, characterNo):
+        """
+        Public method to process the syntax error info from the client.
+        
+        @param message message of the syntax error (string)
+        @param filename translated filename of the syntax error position (string)
+        @param lineNo line number of the syntax error position (integer)
+        @param characterNo character number of the syntax error position (integer)
+        """
+        self.emit(SIGNAL('clientSyntaxError'), message, filename, lineNo, characterNo)
+        
+    def clientExit(self, status):
+        """
+        Public method to process the client exit status.
+        
+        @param status exit code as a string (string)
+        """
+        if self.passive:
+            self.__passiveShutDown()
+        self.emit(SIGNAL('clientExit(int)'), int(status))
+        if Preferences.getDebugger("AutomaticReset"):
+            self.startClient(False)
+        if self.passive:
+            self.__createDebuggerInterface("None")
+            self.clientOutput(self.trUtf8('\nNot connected\n'))
+            self.clientStatement(False)
+        
+    def clientClearBreak(self, filename, lineno):
+        """
+        Public method to process the client clear breakpoint command.
+        
+        @param filename filename of the breakpoint (string)
+        @param lineno line umber of the breakpoint (integer)
+        """
+        self.emit(SIGNAL('clientClearBreak'), filename, lineno)
+        
+    def clientBreakConditionError(self, filename, lineno):
+        """
+        Public method to process the client breakpoint condition error info.
+        
+        @param filename filename of the breakpoint (string)
+        @param lineno line umber of the breakpoint (integer)
+        """
+        self.emit(SIGNAL('clientBreakConditionError'), filename, lineno)
+        
+    def clientClearWatch(self, condition):
+        """
+        Public slot to handle the clientClearWatch signal.
+        
+        @param condition expression of watch expression to clear (string)
+        """
+        self.emit(SIGNAL('clientClearWatch'), condition)
+        
+    def clientWatchConditionError(self, condition):
+        """
+        Public method to process the client watch expression error info.
+        
+        @param condition expression of watch expression to clear (string)
+        """
+        self.emit(SIGNAL('clientWatchConditionError'), condition)
+        
+    def clientRawInput(self, prompt, echo):
+        """
+        Public method to process the client raw input command.
+        
+        @param prompt the input prompt (string)
+        @param echo flag indicating an echoing of the input (boolean)
+        """
+        self.emit(SIGNAL('clientRawInput'), prompt, echo)
+        
+    def clientBanner(self, version, platform, debugClient):
+        """
+        Public method to process the client banner info.
+        
+        @param version interpreter version info (string)
+        @param platform hostname of the client (string)
+        @param debugClient additional debugger type info (string)
+        """
+        self.emit(SIGNAL('clientBanner'), version, platform, debugClient)
+        
+    def clientCapabilities(self, capabilities, clientType):
+        """
+        Public method to process the client capabilities info.
+        
+        @param capabilities bitmaks with the client capabilities (integer)
+        @param clientType type of the debug client (string)
+        """
+        self.__clientCapabilities[clientType] = capabilities
+        self.emit(SIGNAL('clientCapabilities'), capabilities, clientType)
+        
+    def clientCompletionList(self, completionList, text):
+        """
+        Public method to process the client auto completion info.
+        
+        @param completionList list of possible completions (list of strings)
+        @param text the text to be completed (string)
+        """
+        self.emit(SIGNAL('clientCompletionList'), completionList, text)
+        
+    def clientUtPrepared(self, result, exceptionType, exceptionValue):
+        """
+        Public method to process the client unittest prepared info.
+        
+        @param result number of test cases (0 = error) (integer)
+        @param exceptionType exception type (string)
+        @param exceptionValue exception message (string)
+        """
+        self.emit(SIGNAL('utPrepared'), result, exceptionType, exceptionValue)
+        
+    def clientUtStartTest(self, testname, doc):
+        """
+        Public method to process the client start test info.
+        
+        @param testname name of the test (string)
+        @param doc short description of the test (string)
+        """
+        self.emit(SIGNAL('utStartTest'), testname, doc)
+        
+    def clientUtStopTest(self):
+        """
+        Public method to process the client stop test info.
+        """
+        self.emit(SIGNAL('utStopTest'))
+        
+    def clientUtTestFailed(self, testname, traceback):
+        """
+        Public method to process the client test failed info.
+        
+        @param testname name of the test (string)
+        @param traceback lines of traceback info (string)
+        """
+        self.emit(SIGNAL('utTestFailed'), testname, traceback)
+        
+    def clientUtTestErrored(self, testname, traceback):
+        """
+        Public method to process the client test errored info.
+        
+        @param testname name of the test (string)
+        @param traceback lines of traceback info (string)
+        """
+        self.emit(SIGNAL('utTestErrored'), testname, traceback)
+        
+    def clientUtFinished(self):
+        """
+        Public method to process the client unit test finished info.
+        """
+        self.emit(SIGNAL('utFinished'))
+        
+    def passiveStartUp(self, fn, exc):
+        """
+        Public method to handle a passive debug connection.
+        
+        @param fn filename of the debugged script (string)
+        @param exc flag to enable exception reporting of the IDE (boolean)
+        """
+        print self.trUtf8("Passive debug connection received")
+        self.passiveClientExited = False
+        self.debugging = True
+        self.__restoreBreakpoints()
+        self.__restoreWatchpoints()
+        self.emit(SIGNAL('passiveDebugStarted'), fn, exc)
+        
+    def __passiveShutDown(self):
+        """
+        Private method to shut down a passive debug connection.
+        """
+        self.passiveClientExited = True
+        self.shutdownServer()
+        print self.trUtf8("Passive debug connection closed")
+        
+    def __restoreBreakpoints(self):
+        """
+        Private method to restore the breakpoints after a restart.
+        """
+        if self.debugging:
+            self.__addBreakPoints(QModelIndex(), 0, self.breakpointModel.rowCount()-1)
+    
+    def __restoreWatchpoints(self):
+        """
+        Private method to restore the watch expressions after a restart.
+        """
+        if self.debugging:
+            self.__addWatchPoints(QModelIndex(), 0, self.watchpointModel.rowCount()-1)
+    
+    def getBreakPointModel(self):
+        """
+        Public slot to get a reference to the breakpoint model object.
+        
+        @return reference to the breakpoint model object (BreakPointModel)
+        """
+        return self.breakpointModel
+
+    def getWatchPointModel(self):
+        """
+        Public slot to get a reference to the watch expression model object.
+        
+        @return reference to the watch expression model object (WatchPointModel)
+        """
+        return self.watchpointModel
+    
+    def isConnected(self):
+        """
+        Public method to test, if the debug server is connected to a backend.
+        
+        @return flag indicating a connection (boolean)
+        """
+        return self.debuggerInterface and self.debuggerInterface.isConnected()

eric ide

mercurial