eric6/Debugger/DebugServer.py

branch
maintenance
changeset 8043
0acf98cd089a
parent 7924
8a96736d465e
parent 7986
2971d5d19951
child 8142
43248bafe9b2
diff -r 866adc8c315b -r 0acf98cd089a eric6/Debugger/DebugServer.py
--- a/eric6/Debugger/DebugServer.py	Sun Jan 17 13:53:08 2021 +0100
+++ b/eric6/Debugger/DebugServer.py	Mon Feb 01 10:38:16 2021 +0100
@@ -9,7 +9,7 @@
 
 import os
 
-from PyQt5.QtCore import pyqtSignal, QModelIndex
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QModelIndex
 from PyQt5.QtNetwork import (
     QTcpServer, QHostAddress, QHostInfo, QNetworkInterface
 )
@@ -40,49 +40,57 @@
     @signal clientProcessStderr(str) emitted after the client has sent some
         output via stderr
     @signal clientOutput(str) 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
+    @signal clientRawInputSent(debuggerId) emitted after the data was sent
+        to the indicated debug client
+    @signal clientLine(filename, lineno, debuggerId, threadName, forStack)
+        emitted after the debug client has executed a line of code
+    @signal clientStack(stack, debuggerId, threadName) 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(bool) 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 clientDisassembly(disassembly) emitted after the client has sent
-        a disassembly of the code raising an exception
-    @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 clientSignal(signal) emitted after a signal has been generated on
-        the client side
-    @signal clientExit(int, str, bool) emitted after the client has exited
-        giving the exit status, an exit message and an indication to be quiet
-    @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(version, platform, dbgclient, venvname) emitted after
+    @signal clientThreadList(currentId, threadList, debuggerId) emitted after
+        a thread list has been received
+    @signal clientThreadSet(debuggerId) emitted after the client has
+        acknowledged the change of the current thread
+    @signal clientVariables(scope, variables, debuggerId) emitted after a
+        variables dump has been received
+    @signal clientVariable(scope, variables, debuggerId) emitted after a dump
+        for one class variable has been received
+    @signal clientStatement(continue, debuggerId) emitted after an interactive
+        command has been executed. The parameter is False to indicate that the
+        command is complete and True if it needs more input.
+    @signal clientDisassembly(disassembly, debuggerId) emitted after the client
+        has sent a disassembly of the code raising an exception
+    @signal clientException(exceptionType, exceptionMessage, stackTrace,
+        debuggerId, threadName) emitted after an exception occured on the
+        client side
+    @signal clientSyntaxError(message, filename, linenumber, characternumber,
+        debuggerId, threadName) emitted after a syntax error has been detected
+        on the client side
+    @signal clientSignal(message, filename, linenumber, function name,
+        function arguments, debuggerId) emitted after a signal has been
+        generated on the client side
+    @signal clientDisconnected(str) emitted after a debug client has
+        disconnected (i.e. closed the network socket)
+    @signal clientExit(str, int, str, bool, str) emitted after the client has
+        exited giving the program name, the exit status, an exit message, an
+        indication to be quiet and the ID of the exited client
+    @signal lastClientExited() emitted to indicate that the last connected
+        debug client has terminated
+    @signal clientClearBreak(filename, lineno, debuggerId) emitted after the
+        debug client has decided to clear a temporary breakpoint
+    @signal clientBreakConditionError(fn, lineno, debuggerId) emitted after the
+        client has signaled a syntax error in a breakpoint condition
+    @signal clientClearWatch(condition, debuggerId) emitted after the debug
+        client has decided to clear a temporary watch expression
+    @signal clientWatchConditionError(condition, debuggerId) emitted after the
+        client has signaled a syntax error in a watch expression
+    @signal clientRawInput(prompt, echo, debuggerId) emitted after a raw input
+        request was received
+    @signal clientBanner(version, platform, venvname) emitted after
         the client banner data was received
     @signal clientCapabilities(capabilities, cltype, venvname) emitted after
         the clients capabilities were received
-    @signal clientCompletionList(completionList, text) emitted after the client
-        the commandline completion list and the reworked searchstring was
+    @signal clientCompletionList(completionList, text) emitted after the
+        commandline completion list and the reworked search string was
         received from the client
     @signal passiveDebugStarted(str, bool) emitted after the debug client has
         connected in passive debug mode
@@ -111,36 +119,41 @@
         reported an unexpected test success
     @signal callTraceInfo emitted after the client reported the call trace
         data (isCall, fromFile, fromLine, fromFunction, toFile, toLine,
-        toFunction)
+        toFunction, debuggerId)
     @signal appendStdout(msg) emitted when a passive debug connection is
         established or lost
+    @signal clientDebuggerId(debuggerId) emitted to indicate a newly connected
+        debugger backend
     """
-    clientClearBreak = pyqtSignal(str, int)
-    clientClearWatch = pyqtSignal(str)
+    clientClearBreak = pyqtSignal(str, int, str)
+    clientClearWatch = pyqtSignal(str, str)
     clientGone = pyqtSignal(bool)
     clientProcessStdout = pyqtSignal(str)
     clientProcessStderr = pyqtSignal(str)
-    clientRawInputSent = pyqtSignal()
+    clientRawInputSent = pyqtSignal(str)
     clientOutput = pyqtSignal(str)
-    clientLine = pyqtSignal(str, int, bool)
-    clientStack = pyqtSignal(list)
-    clientThreadList = pyqtSignal('PyQt_PyObject', list)
-    clientThreadSet = pyqtSignal()
-    clientVariables = pyqtSignal(int, list)
-    clientVariable = pyqtSignal(int, list)
-    clientStatement = pyqtSignal(bool)
-    clientDisassembly = pyqtSignal(dict)
-    clientException = pyqtSignal(str, str, list)
-    clientSyntaxError = pyqtSignal(str, str, int, int)
-    clientSignal = pyqtSignal(str, str, int, str, str)
-    clientExit = pyqtSignal(int, str, bool)
-    clientBreakConditionError = pyqtSignal(str, int)
-    clientWatchConditionError = pyqtSignal(str)
-    clientRawInput = pyqtSignal(str, bool)
-    clientBanner = pyqtSignal(str, str, str, str)
+    clientLine = pyqtSignal(str, int, str, str, bool)
+    clientStack = pyqtSignal(list, str, str)
+    clientThreadList = pyqtSignal('PyQt_PyObject', list, str)
+    clientThreadSet = pyqtSignal(str)
+    clientVariables = pyqtSignal(int, list, str)
+    clientVariable = pyqtSignal(int, list, str)
+    clientStatement = pyqtSignal(bool, str)
+    clientDisassembly = pyqtSignal(dict, str)
+    clientException = pyqtSignal(str, str, list, str, str)
+    clientSyntaxError = pyqtSignal(str, str, int, int, str, str)
+    clientSignal = pyqtSignal(str, str, int, str, str, str)
+    clientDisconnected = pyqtSignal(str)
+    clientExit = pyqtSignal(str, int, str, bool, str)
+    lastClientExited = pyqtSignal()
+    clientBreakConditionError = pyqtSignal(str, int, str)
+    clientWatchConditionError = pyqtSignal(str, str)
+    clientRawInput = pyqtSignal(str, bool, str)
+    clientBanner = pyqtSignal(str, str, str)
     clientCapabilities = pyqtSignal(int, str, str)
     clientCompletionList = pyqtSignal(list, str)
     clientInterpreterChanged = pyqtSignal(str)
+    clientDebuggerId = pyqtSignal(str)
     utDiscovered = pyqtSignal(list, str, str)
     utPrepared = pyqtSignal(int, str, str)
     utStartTest = pyqtSignal(str, str)
@@ -152,7 +165,7 @@
     utTestSucceededUnexpected = pyqtSignal(str, str)
     utFinished = pyqtSignal()
     passiveDebugStarted = pyqtSignal(str, bool)
-    callTraceInfo = pyqtSignal(bool, str, str, str, str, str, str)
+    callTraceInfo = pyqtSignal(bool, str, str, str, str, str, str, str)
     appendStdout = pyqtSignal(str)
     
     def __init__(self, originalPathString, preventPassiveDebugging=False):
@@ -187,6 +200,10 @@
         self.watchSpecialChanged = self.tr(
             "changed", "must be same as in EditWatchpointDialog")
         
+        # arrays to track already reported issues
+        self.__reportedBreakpointIssues = []
+        self.__reportedWatchpointIssues = []
+        
         self.networkInterface = Preferences.getDebugger("NetworkInterface")
         if self.networkInterface == "all":
             hostAddress = QHostAddress("0.0.0.0")  # QHostAddress.Any)  # secok
@@ -242,16 +259,19 @@
         
         self.__maxVariableSize = Preferences.getDebugger("MaxVariableSize")
         
+        self.__multiprocessNoDebugList = []
+        
         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)
+        @type bool
+        @return IP address or hostname
+        @rtype str
         """
         if self.networkInterface == "all":
             if localhost:
@@ -266,14 +286,16 @@
         else:
             return "{0}@@i{1}".format(self.networkInterface,
                                       self.networkInterfaceIndex)
-        
+    
     def __getNetworkInterfaceAndIndex(self, address):
         """
         Private method to determine the network interface and the interface
         index.
         
-        @param address address to determine the info for (string)
-        @return tuple of network interface name (string) and index (integer)
+        @param address address to determine the info for
+        @type str
+        @return tuple of network interface name and index
+        @rtype tuple of (str, int)
         """
         if address not in ["all", "allv6"]:
             for networkInterface in QNetworkInterface.allInterfaces():
@@ -286,7 +308,7 @@
                                     networkInterface.index())
         
         return "", 0
-        
+    
     def preferencesChanged(self):
         """
         Public slot to handle the preferencesChanged signal.
@@ -302,7 +324,7 @@
                                            reregister=True)
         
         self.__maxVariableSize = Preferences.getDebugger("MaxVariableSize")
-        
+    
     def registerDebuggerInterface(self, interfaceName, getRegistryData,
                                   reregister=False):
         """
@@ -336,7 +358,7 @@
                 self.__debuggerInterfaceRegistry[clientLanguage] = [
                     clientCapabilities, clientExtensions, interfaceCreator,
                     interfaceName]
-        
+    
     def unregisterDebuggerInterface(self, interfaceName):
         """
         Public method to unregister a debugger interface.
@@ -353,7 +375,7 @@
             for clientLanguage in clientLanguages:
                 del self.__debuggerInterfaceRegistry[clientLanguage]
             del self.__debuggerInterfaces[interfaceName]
-        
+    
     def __findLanguageForExtension(self, ext):
         """
         Private method to get the language associated with a file extension.
@@ -368,7 +390,7 @@
                 return language
         
         return ""
-        
+    
     def __registerDebuggerInterfaces(self):
         """
         Private method to register the available internal debugger interfaces.
@@ -381,14 +403,16 @@
                 mod = getattr(mod, comp)
             
             self.registerDebuggerInterface(name, mod.getRegistryData)
-        
+    
     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)
+        @type bool
+        @return list of supported languages
+        @rtype list of str
         """
         languages = list(self.__debuggerInterfaceRegistry.keys())
         try:
@@ -402,25 +426,27 @@
                          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)
+        @param language language to get extensions for
+        @type str
         @return tuple of extensions associated with the language
-            (tuple of strings)
+        @rtype tuple of str
         """
         if language in self.__debuggerInterfaceRegistry:
             return tuple(self.__debuggerInterfaceRegistry[language][1])
         else:
             return ()
-        
+    
     def __createDebuggerInterface(self, clientType=None):
         """
         Private slot to create the debugger interface object.
         
-        @param clientType type of the client interface to be created (string)
+        @param clientType type of the client interface to be created
+        @type str
         """
         if self.lastClientType != self.clientType or clientType is not None:
             if clientType is None:
@@ -434,35 +460,36 @@
                     self.__debuggerInterfaceRegistry["None"][2](
                         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)
+        @param clType type of client to be started
+        @type str
         """
         if clType is not None and clType in self.getSupportedLanguages():
             self.clientType = clType
             Preferences.Prefs.settings.setValue(
                 'DebugClient/Type', self.clientType)
-        
+    
     def startClient(self, unplanned=True, clType=None, forProject=False,
                     runInConsole=False, venvName="", workingDir=None):
         """
         Public method to start a debug client.
         
-        @keyparam unplanned flag indicating that the client has died
+        @param unplanned flag indicating that the client has died
         @type bool
-        @keyparam clType type of client to be started
+        @param clType type of client to be started
         @type str
-        @keyparam forProject flag indicating a project related action
+        @param forProject flag indicating a project related action
         @type bool
-        @keyparam runInConsole flag indicating to start the debugger in a
+        @param runInConsole flag indicating to start the debugger in a
             console window
         @type bool
-        @keyparam venvName name of the virtual environment to be used
+        @param venvName name of the virtual environment to be used
         @type str
-        @keyparam workingDir directory to start the debugger client in
+        @param workingDir directory to start the debugger client in
         @type str
         """
         self.running = False
@@ -521,8 +548,6 @@
                 elif self.__autoClearShell:
                     self.__autoClearShell = False
                     self.remoteBanner()
-##                self.remoteClientVariables(0, [], 0)
-##                self.remoteClientVariables(1, [], 0)
             else:
                 if clType and self.lastClientType:
                     self.__setClientType(self.lastClientType)
@@ -533,7 +558,7 @@
         if clientInterpreter != self.clientInterpreter:
             self.clientInterpreter = clientInterpreter
             self.clientInterpreterChanged.emit(clientInterpreter)
-
+    
     def __clientProcessOutput(self):
         """
         Private slot to process client output received via stdout.
@@ -542,7 +567,7 @@
                      Preferences.getSystem("IOEncoding"),
                      'replace')
         self.clientProcessStdout.emit(output)
-        
+    
     def __clientProcessError(self):
         """
         Private slot to process client output received via stderr.
@@ -551,82 +576,112 @@
                     Preferences.getSystem("IOEncoding"),
                     'replace')
         self.clientProcessStderr.emit(error)
-        
+    
+    @pyqtSlot(str, int)
     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)
+        @param fn filename of breakpoint to clear
+        @type str
+        @param lineno line number of breakpoint to clear
+        @type int
         """
         if self.debugging:
             index = self.breakpointModel.getBreakPointIndex(fn, lineno)
             self.breakpointModel.deleteBreakPointByIndex(index)
-
+            if (fn, lineno) in self.__reportedBreakpointIssues:
+                self.__reportedBreakpointIssues.remove((fn, lineno))
+    
     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)
+        @param parentIndex index of parent item
+        @type QModelIndex
+        @param start start row
+        @type int
+        @param end end row
+        @type int
         """
         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)
-
+                # delete the breakpoints of all connected backends
+                self.remoteBreakpoint("", fn, lineno, False)
+                if (fn, lineno) in self.__reportedBreakpointIssues:
+                    self.__reportedBreakpointIssues.remove((fn, lineno))
+    
     def __changeBreakPoints(self, startIndex, endIndex):
         """
         Private slot to set changed breakpoints.
         
-        @param startIndex starting index of the change breakpoins (QModelIndex)
-        @param endIndex ending index of the change breakpoins (QModelIndex)
+        @param startIndex starting index of the change breakpoins
+        @type QModelIndex
+        @param endIndex ending index of the change breakpoins
+        @type QModelIndex
         """
         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)
+        @param startIndex start index of the rows to be changed
+        @type QModelIndex
+        @param endIndex end index of the rows to be changed
+        @type QModelIndex
         """
         if self.debugging:
             self.__deleteBreakPoints(
                 QModelIndex(), startIndex.row(), endIndex.row())
-        
-    def __addBreakPoints(self, parentIndex, start, end):
+    
+    def __addBreakPoints(self, parentIndex, start, end, debuggerId=""):
         """
         Private slot to add breakpoints.
         
-        @param parentIndex index of parent item (QModelIndex)
-        @param start start row (integer)
-        @param end end row (integer)
+        @param parentIndex index of parent item
+        @type QModelIndex
+        @param start start row
+        @type int
+        @param end end row
+        @type int
+        @param debuggerId ID of the debugger backend to send to. If this is
+            empty, they will be broadcast to all connected backends.
+        @type str
         """
         if self.debugging:
             for row in range(start, end + 1):
                 index = self.breakpointModel.index(row, 0, parentIndex)
-                fn, line, cond, temp, enabled, ignorecount = (
+                fn, lineno, cond, temp, enabled, ignorecount = (
                     self.breakpointModel.getBreakPointByIndex(index)[:6])
-                self.remoteBreakpoint(fn, line, True, cond, temp)
+                
+                if (fn, lineno) in self.__reportedBreakpointIssues:
+                    self.__reportedBreakpointIssues.remove((fn, lineno))
+                
+                self.remoteBreakpoint(debuggerId, fn, lineno, True, cond, temp)
                 if not enabled:
-                    self.__remoteBreakpointEnable(fn, line, False)
+                    self.__remoteBreakpointEnable(
+                        debuggerId, fn, lineno, False)
                 if ignorecount:
-                    self.__remoteBreakpointIgnore(fn, line, ignorecount)
-
+                    self.__remoteBreakpointIgnore(
+                        debuggerId, fn, lineno, 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)
+        @param cond condition
+        @type str
+        @param special special condition
+        @type str
+        @return condition string
+        @rtype str
         """
         if special == "":
             _cond = cond
@@ -636,14 +691,15 @@
             elif special == self.watchSpecialChanged:
                 _cond = "{0} ??changed??".format(cond)
         return _cond
-        
+    
     def __splitWatchCondition(self, cond):
         """
         Private method to split a remote watch expression.
         
-        @param cond remote expression (string)
+        @param cond remote expression
+        @type str
         @return tuple of local expression (string) and special condition
-            (string)
+        @rtype str
         """
         if cond.endswith(" ??created??"):
             cond, special = cond.split()
@@ -656,25 +712,32 @@
             special = ""
         
         return cond, special
-        
+    
+    @pyqtSlot(str)
     def __clientClearWatchPoint(self, condition):
         """
         Private slot to handle the clientClearWatch signal.
         
-        @param condition expression of watch expression to clear (string)
+        @param condition expression of watch expression to clear
+        @type str
         """
         if self.debugging:
             cond, special = self.__splitWatchCondition(condition)
             index = self.watchpointModel.getWatchPointIndex(cond, special)
             self.watchpointModel.deleteWatchPointByIndex(index)
-        
+            if condition in self.__reportedWatchpointIssues:
+                self.__reportedWatchpointIssues.remove(condition)
+    
     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)
+        @param parentIndex index of parent item
+        @type QModelIndex
+        @param start start row
+        @type int
+        @param end end row
+        @type int
         """
         if self.debugging:
             for row in range(start, end + 1):
@@ -682,27 +745,37 @@
                 cond, special = (
                     self.watchpointModel.getWatchPointByIndex(index)[0:2])
                 cond = self.__makeWatchCondition(cond, special)
-                self.__remoteWatchpoint(cond, False)
-        
+                self.__remoteWatchpoint("", cond, False)
+                if cond in self.__reportedWatchpointIssues:
+                    self.__reportedWatchpointIssues.remove(cond)
+    
     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)
+        @param startIndex start index of the rows to be changed
+        @type QModelIndex
+        @param endIndex end index of the rows to be changed
+        @type QModelIndex
         """
         if self.debugging:
             self.__deleteWatchPoints(
                 QModelIndex(), startIndex.row(), endIndex.row())
-        
-    def __addWatchPoints(self, parentIndex, start, end):
+    
+    def __addWatchPoints(self, parentIndex, start, end, debuggerId=""):
         """
         Private slot to set a watch expression.
         
-        @param parentIndex index of parent item (QModelIndex)
-        @param start start row (integer)
-        @param end end row (integer)
+        @param parentIndex index of parent item
+        @type QModelIndex
+        @param start start row
+        @type int
+        @param end end row
+        @type int
+        @param debuggerId ID of the debugger backend to send to. If this is
+            empty, they will be broadcast to all connected backends.
+        @type str
         """
         if self.debugging:
             for row in range(start, end + 1):
@@ -710,40 +783,50 @@
                 cond, special, temp, enabled, ignorecount = (
                     self.watchpointModel.getWatchPointByIndex(index)[:5])
                 cond = self.__makeWatchCondition(cond, special)
-                self.__remoteWatchpoint(cond, True, temp)
+                
+                if cond in self.__reportedWatchpointIssues:
+                    self.__reportedWatchpointIssues.remove(cond)
+                
+                self.__remoteWatchpoint(debuggerId, cond, True, temp)
                 if not enabled:
-                    self.__remoteWatchpointEnable(cond, False)
+                    self.__remoteWatchpointEnable(debuggerId, cond, False)
                 if ignorecount:
-                    self.__remoteWatchpointIgnore(cond, ignorecount)
-        
+                    self.__remoteWatchpointIgnore(debuggerId, 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)
+        @param startIndex start index of the rows to be changed
+        @type QModelIndex
+        @param endIndex end index of the rows to be changed
+        @type QModelIndex
         """
         if self.debugging:
             self.__addWatchPoints(
                 QModelIndex(), startIndex.row(), endIndex.row())
-        
+    
     def getClientCapabilities(self, clientType):
         """
         Public method to retrieve the debug clients capabilities.
         
-        @param clientType debug client type (string)
-        @return debug client capabilities (integer)
+        @param clientType debug client type
+        @type str
+        @return debug client capabilities
+        @rtype int
         """
         try:
             return self.__debuggerInterfaceRegistry[clientType][0]
         except KeyError:
             return 0    # no capabilities
-        
+    
     def getClientInterpreter(self):
         """
         Public method to get the interpreter of the debug client.
         
-        @return interpreter of the debug client (string)
+        @return interpreter of the debug client
+        @rtype str
         """
         return self.clientInterpreter
     
@@ -793,20 +876,23 @@
             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()
-
+        self.debuggerInterface.newConnection(sock)
+    
+    def masterClientConnected(self):
+        """
+        Public method to perform actions after the master client has finally
+        established the connection.
+        """
+        # 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()
+    
     def shutdownServer(self):
         """
         Public method to cleanly shut down.
@@ -816,12 +902,13 @@
         """
         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)
+        @param env environment settings
+        @type str
         """
         envlist = Utilities.parseEnvironmentString(env)
         envdict = {}
@@ -834,11 +921,11 @@
             except ValueError:
                 pass
         self.debuggerInterface.remoteEnvironment(envdict)
-        
+    
     def remoteLoad(self, venvName, fn, argv, wd, env, autoClearShell=True,
                    tracePython=False, autoContinue=True, forProject=False,
-                   runInConsole=False, autoFork=False, forkChild=False,
-                   clientType="", enableCallTrace=False):
+                   runInConsole=False, clientType="", enableCallTrace=False,
+                   enableMultiprocess=False, multiprocessNoDebug=""):
         """
         Public method to load a new program to debug.
         
@@ -852,32 +939,36 @@
         @type str
         @param env environment parameter settings
         @type str
-        @keyparam autoClearShell flag indicating, that the interpreter window
+        @param autoClearShell flag indicating, that the interpreter window
             should be cleared
         @type bool
-        @keyparam tracePython flag indicating if the Python library should be
+        @param tracePython flag indicating if the Python library should be
             traced as well
         @type bool
-        @keyparam autoContinue flag indicating, that the debugger should not
+        @param autoContinue flag indicating, that the debugger should not
             stop at the first executable line
         @type bool
-        @keyparam forProject flag indicating a project related action
+        @param forProject flag indicating a project related action
         @type bool
-        @keyparam runInConsole flag indicating to start the debugger in a
+        @param runInConsole flag indicating to start the debugger in a
             console window
         @type bool
-        @keyparam autoFork flag indicating the automatic fork mode
-        @type bool
-        @keyparam forkChild flag indicating to debug the child after forking
-        @type bool
-        @keyparam clientType client type to be used
+        @param clientType client type to be used
         @type str
-        @keyparam enableCallTrace flag indicating to enable the call trace
+        @param enableCallTrace flag indicating to enable the call trace
             function
         @type bool
+        @param enableMultiprocess flag indicating to perform multiprocess
+            debugging
+        @type bool
+        @param multiprocessNoDebug space separated list of programs not to be
+            debugged
+        @type str
         """
         self.__autoClearShell = autoClearShell
-        self.__autoContinue = autoContinue
+        self.__multiprocessNoDebugList = [
+            s.strip() for s in multiprocessNoDebug.split(os.pathsep)
+        ]
         
         if clientType not in self.getSupportedLanguages():
             # a not supported client language was requested
@@ -902,19 +993,21 @@
         self.startClient(False, forProject=forProject,
                          runInConsole=runInConsole, venvName=venvName)
         
-        self.setCallTraceEnabled(enableCallTrace)
+        self.setCallTraceEnabled("", enableCallTrace)
         self.remoteEnvironment(env)
         
-        self.debuggerInterface.remoteLoad(fn, argv, wd, tracePython,
-                                          autoContinue, autoFork, forkChild)
+        self.debuggerInterface.remoteLoad(
+            fn, argv, wd, tracePython, autoContinue,
+            enableMultiprocess=enableMultiprocess
+        )
         self.debugging = True
         self.running = True
         self.__restoreBreakpoints()
         self.__restoreWatchpoints()
-
+        self.__restoreNoDebugList()
+    
     def remoteRun(self, venvName, fn, argv, wd, env, autoClearShell=True,
-                  forProject=False, runInConsole=False, autoFork=False,
-                  forkChild=False, clientType=""):
+                  forProject=False, runInConsole=False, clientType=""):
         """
         Public method to load a new program to run.
         
@@ -928,19 +1021,15 @@
         @type str
         @param env environment parameter settings
         @type str
-        @keyparam autoClearShell flag indicating, that the interpreter window
+        @param autoClearShell flag indicating, that the interpreter window
             should be cleared
         @type bool
-        @keyparam forProject flag indicating a project related action
+        @param forProject flag indicating a project related action
         @type bool
-        @keyparam runInConsole flag indicating to start the debugger in a
+        @param runInConsole flag indicating to start the debugger in a
             console window
         @type bool
-        @keyparam autoFork flag indicating the automatic fork mode
-        @type bool
-        @keyparam forkChild flag indicating to debug the child after forking
-        @type bool
-        @keyparam clientType client type to be used
+        @param clientType client type to be used
         @type str
         """
         self.__autoClearShell = autoClearShell
@@ -970,10 +1059,10 @@
         
         self.remoteEnvironment(env)
         
-        self.debuggerInterface.remoteRun(fn, argv, wd, autoFork, forkChild)
+        self.debuggerInterface.remoteRun(fn, argv, wd)
         self.debugging = False
         self.running = True
-
+    
     def remoteCoverage(self, venvName, fn, argv, wd, env,
                        autoClearShell=True, erase=False, forProject=False,
                        runInConsole=False, clientType=""):
@@ -990,18 +1079,18 @@
         @type str
         @param env environment parameter settings
         @type str
-        @keyparam autoClearShell flag indicating, that the interpreter window
+        @param autoClearShell flag indicating, that the interpreter window
             should be cleared
         @type bool
-        @keyparam erase flag indicating that coverage info should be
+        @param erase flag indicating that coverage info should be
             cleared first
         @type bool
-        @keyparam forProject flag indicating a project related action
+        @param forProject flag indicating a project related action
         @type bool
-        @keyparam runInConsole flag indicating to start the debugger in a
+        @param runInConsole flag indicating to start the debugger in a
             console window
         @type bool
-        @keyparam clientType client type to be used
+        @param clientType client type to be used
         @type str
         """
         self.__autoClearShell = autoClearShell
@@ -1034,7 +1123,7 @@
         self.debuggerInterface.remoteCoverage(fn, argv, wd, erase)
         self.debugging = False
         self.running = True
-
+    
     def remoteProfile(self, venvName, fn, argv, wd, env,
                       autoClearShell=True, erase=False, forProject=False,
                       runInConsole=False, clientType=""):
@@ -1051,18 +1140,18 @@
         @type str
         @param env environment parameter settings
         @type str
-        @keyparam autoClearShell flag indicating, that the interpreter window
+        @param autoClearShell flag indicating, that the interpreter window
             should be cleared
         @type bool
-        @keyparam erase flag indicating that coverage info should be
+        @param erase flag indicating that coverage info should be
             cleared first
         @type bool
-        @keyparam forProject flag indicating a project related action
+        @param forProject flag indicating a project related action
         @type bool
-        @keyparam runInConsole flag indicating to start the debugger in a
+        @param runInConsole flag indicating to start the debugger in a
             console window
         @type bool
-        @keyparam clientType client type to be used
+        @param clientType client type to be used
         @type str
         """
         self.__autoClearShell = autoClearShell
@@ -1095,219 +1184,328 @@
         self.debuggerInterface.remoteProfile(fn, argv, wd, erase)
         self.debugging = False
         self.running = True
-
-    def remoteStatement(self, stmt):
+    
+    def remoteStatement(self, debuggerId, stmt):
         """
         Public method to execute a Python statement.
         
-        @param stmt the Python statement to execute (string). It
-              should not have a trailing newline.
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param stmt the Python statement to execute.
+        @type str
         """
-        self.debuggerInterface.remoteStatement(stmt)
-
-    def remoteStep(self):
+        self.debuggerInterface.remoteStatement(debuggerId, stmt.rstrip())
+    
+    def remoteStep(self, debuggerId):
         """
         Public method to single step the debugged program.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.debuggerInterface.remoteStep()
-
-    def remoteStepOver(self):
+        self.debuggerInterface.remoteStep(debuggerId)
+    
+    def remoteStepOver(self, debuggerId):
         """
         Public method to step over the debugged program.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.debuggerInterface.remoteStepOver()
-
-    def remoteStepOut(self):
+        self.debuggerInterface.remoteStepOver(debuggerId)
+    
+    def remoteStepOut(self, debuggerId):
         """
         Public method to step out the debugged program.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.debuggerInterface.remoteStepOut()
-
-    def remoteStepQuit(self):
+        self.debuggerInterface.remoteStepOut(debuggerId)
+    
+    def remoteStepQuit(self, debuggerId):
         """
         Public method to stop the debugged program.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.debuggerInterface.remoteStepQuit()
-
-    def remoteContinue(self, special=False):
+        self.debuggerInterface.remoteStepQuit(debuggerId)
+    
+    def remoteContinue(self, debuggerId, special=False):
         """
         Public method to continue the debugged program.
         
+        @param debuggerId ID of the debugger backend
+        @type str
         @param special flag indicating a special continue operation
         """
-        self.debuggerInterface.remoteContinue(special)
-
-    def remoteMoveIP(self, line):
+        self.debuggerInterface.remoteContinue(debuggerId, special)
+    
+    def remoteContinueUntil(self, debuggerId, line):
+        """
+        Public method to continue the debugged program to the given line
+        or until returning from the current frame.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param line the new line, where execution should be continued to
+        @type int
+        """
+        self.debuggerInterface.remoteContinueUntil(debuggerId, line)
+    
+    def remoteMoveIP(self, debuggerId, line):
         """
         Public method to move the instruction pointer to a different line.
         
+        @param debuggerId ID of the debugger backend
+        @type str
         @param line the new line, where execution should be continued
+        @type int
         """
-        self.debuggerInterface.remoteMoveIP(line)
-
-    def remoteBreakpoint(self, fn, line, setBreakpoint, cond=None, temp=False):
+        self.debuggerInterface.remoteMoveIP(debuggerId, line)
+    
+    def remoteBreakpoint(self, debuggerId, fn, line, setBreakpoint, 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 debuggerId ID of the debugger backend
+        @type str
+        @param fn filename the breakpoint belongs to
+        @type str
+        @param line linenumber of the breakpoint
+        @type int
         @param setBreakpoint flag indicating setting or resetting a breakpoint
-            (boolean)
-        @param cond condition of the breakpoint (string)
-        @param temp flag indicating a temporary breakpoint (boolean)
+        @type bool
+        @param cond condition of the breakpoint
+        @type str
+        @param temp flag indicating a temporary breakpoint
+        @type bool
         """
-        self.debuggerInterface.remoteBreakpoint(fn, line, setBreakpoint, cond,
-                                                temp)
-        
-    def __remoteBreakpointEnable(self, fn, line, enable):
+        self.debuggerInterface.remoteBreakpoint(
+            debuggerId, fn, line, setBreakpoint, cond, temp)
+    
+    def __remoteBreakpointEnable(self, debuggerId, 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 debuggerId ID of the debugger backend
+        @type str
+        @param fn filename the breakpoint belongs to
+        @type str
+        @param line linenumber of the breakpoint
+        @type int
         @param enable flag indicating enabling or disabling a breakpoint
-            (boolean)
+        @type bool
         """
-        self.debuggerInterface.remoteBreakpointEnable(fn, line, enable)
-        
-    def __remoteBreakpointIgnore(self, fn, line, count):
+        self.debuggerInterface.remoteBreakpointEnable(
+            debuggerId, fn, line, enable)
+    
+    def __remoteBreakpointIgnore(self, debuggerId, 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)
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param fn filename the breakpoint belongs to
+        @type str
+        @param line linenumber of the breakpoint
+        @type int
+        @param count number of occurrences to ignore
+        @type int
         """
-        self.debuggerInterface.remoteBreakpointIgnore(fn, line, count)
-        
-    def __remoteWatchpoint(self, cond, setWatch, temp=False):
+        self.debuggerInterface.remoteBreakpointIgnore(
+            debuggerId, fn, line, count)
+    
+    def __remoteWatchpoint(self, debuggerId, cond, setWatch, temp=False):
         """
         Private method to set or clear a watch expression.
         
-        @param cond expression of the watch expression (string)
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param cond expression of the watch expression
+        @type str
         @param setWatch flag indicating setting or resetting a watch expression
-            (boolean)
-        @param temp flag indicating a temporary watch expression (boolean)
+        @type bool
+        @param temp flag indicating a temporary watch expression
+        @type bool
         """
         # cond is combination of cond and special (s. watch expression viewer)
-        self.debuggerInterface.remoteWatchpoint(cond, setWatch, temp)
+        self.debuggerInterface.remoteWatchpoint(debuggerId, cond, setWatch,
+                                                temp)
     
-    def __remoteWatchpointEnable(self, cond, enable):
+    def __remoteWatchpointEnable(self, debuggerId, cond, enable):
         """
         Private method to enable or disable a watch expression.
         
-        @param cond expression of the watch expression (string)
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param cond expression of the watch expression
+        @type str
         @param enable flag indicating enabling or disabling a watch expression
-            (boolean)
+        @type bool
         """
         # cond is combination of cond and special (s. watch expression viewer)
-        self.debuggerInterface.remoteWatchpointEnable(cond, enable)
+        self.debuggerInterface.remoteWatchpointEnable(debuggerId, cond, enable)
     
-    def __remoteWatchpointIgnore(self, cond, count):
+    def __remoteWatchpointIgnore(self, debuggerId, 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)
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param cond expression of the watch expression
+        @type str
+        @param count number of occurrences to ignore
+        @type int
         """
         # cond is combination of cond and special (s. watch expression viewer)
-        self.debuggerInterface.remoteWatchpointIgnore(cond, count)
+        self.debuggerInterface.remoteWatchpointIgnore(debuggerId, cond, count)
     
-    def remoteRawInput(self, s):
+    def remoteRawInput(self, debuggerId, inputString):
         """
         Public method to send the raw input to the debugged program.
         
-        @param s the raw input (string)
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param inputString the raw input
+        @type str
         """
-        self.debuggerInterface.remoteRawInput(s)
-        self.clientRawInputSent.emit()
-        
-    def remoteThreadList(self):
+        self.debuggerInterface.remoteRawInput(debuggerId, inputString)
+        self.clientRawInputSent.emit(debuggerId)
+    
+    def remoteThreadList(self, debuggerId):
         """
         Public method to request the list of threads from the client.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.debuggerInterface.remoteThreadList()
-        
-    def remoteSetThread(self, tid):
+        self.debuggerInterface.remoteThreadList(debuggerId)
+    
+    def remoteSetThread(self, debuggerId, tid):
         """
         Public method to request to set the given thread as current thread.
         
-        @param tid id of the thread (integer)
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param tid id of the thread
+        @type int
+        """
+        self.debuggerInterface.remoteSetThread(debuggerId, tid)
+    
+    def remoteClientStack(self, debuggerId):
         """
-        self.debuggerInterface.remoteSetThread(tid)
+        Public method to request the stack of the main thread.
         
-    def remoteClientVariables(self, scope, filterList, framenr=0):
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.debuggerInterface.remoteClientStack(debuggerId)
+    
+    def remoteClientVariables(self, debuggerId, scope, filterList, framenr=0):
         """
         Public method to request the variables of the debugged program.
         
+        @param debuggerId ID of the debugger backend
+        @type str
         @param scope the scope of the variables (0 = local, 1 = global)
-        @param filterList list of variable types to filter out (list of int)
-        @param framenr framenumber of the variables to retrieve (int)
+        @type int
+        @param filterList list of variable types to filter out
+        @type list of str
+        @param framenr framenumber of the variables to retrieve
+        @type int
         """
         self.debuggerInterface.remoteClientVariables(
-            scope, filterList, framenr, self.__maxVariableSize)
-        
-    def remoteClientVariable(self, scope, filterList, var, framenr=0):
+            debuggerId, scope, filterList, framenr, self.__maxVariableSize)
+    
+    def remoteClientVariable(self, debuggerId, scope, filterList, var,
+                             framenr=0, maxSize=0):
         """
         Public method to request the variables of the debugged program.
         
+        @param debuggerId ID of the debugger backend
+        @type str
         @param scope the scope of the variables (0 = local, 1 = global)
-        @param filterList 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)
+        @type int
+        @param filterList list of variable types to filter out
+        @type list of str
+        @param var list encoded name of variable to retrieve
+        @type list of str
+        @param framenr framenumber of the variables to retrieve
+        @type int
+        @param maxSize maximum size the formatted value of a variable will
+            be shown. If it is bigger than that, a 'too big' indication will
+            be given (@@TOO_BIG_TO_SHOW@@).
+        @type int
         """
         self.debuggerInterface.remoteClientVariable(
-            scope, filterList, var, framenr, self.__maxVariableSize)
+            debuggerId, scope, filterList, var, framenr,
+            self.__maxVariableSize)
     
-    def remoteClientDisassembly(self):
+    def remoteClientDisassembly(self, debuggerId):
         """
         Public method to ask the client for the latest traceback disassembly.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
         """
         try:
-            self.debuggerInterface.remoteClientDisassembly()
+            self.debuggerInterface.remoteClientDisassembly(debuggerId)
         except AttributeError:
             # remote client doesn't support that
             pass
     
-    def remoteClientSetFilter(self, scope, filterStr):
+    def remoteClientSetFilter(self, debuggerId, scope, filterStr):
         """
         Public method to set a variables filter list.
         
+        @param debuggerId ID of the debugger backend
+        @type str
         @param scope the scope of the variables (0 = local, 1 = global)
+        @type int
         @param filterStr regexp string for variable names to filter out
-            (string)
+        @type str
         """
-        self.debuggerInterface.remoteClientSetFilter(scope, filterStr)
-        
-    def setCallTraceEnabled(self, on):
+        self.debuggerInterface.remoteClientSetFilter(
+            debuggerId, scope, filterStr)
+    
+    def setCallTraceEnabled(self, debuggerId, on):
         """
         Public method to set the call trace state.
         
-        @param on flag indicating to enable the call trace function (boolean)
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param on flag indicating to enable the call trace function
+        @type bool
         """
-        self.debuggerInterface.setCallTraceEnabled(on)
-        
+        self.debuggerInterface.setCallTraceEnabled(debuggerId, on)
+    
     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):
+    
+    def remoteCompletion(self, debuggerId, text):
         """
         Public slot to get the a list of possible commandline completions
         from the remote client.
         
-        @param text the text to be completed (string)
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param text the text to be completed
+        @type str
         """
-        self.debuggerInterface.remoteCompletion(text)
+        self.debuggerInterface.remoteCompletion(debuggerId, text)
     
     def remoteUTDiscover(self, clientType, forProject, venvName, syspath,
                          workdir, discoveryStart):
@@ -1422,7 +1620,7 @@
         if debug:
             self.__restoreBreakpoints()
             self.__restoreWatchpoints()
-        
+    
     def remoteUTRun(self, debug=False, failfast=False):
         """
         Public method to start a unittest run.
@@ -1433,125 +1631,179 @@
         @type bool
         """
         self.debuggerInterface.remoteUTRun(debug, failfast)
-        
+    
     def remoteUTStop(self):
         """
         public method to stop a unittest run.
         """
         self.debuggerInterface.remoteUTStop()
-        
-    def signalClientOutput(self, line):
+    
+    def signalClientOutput(self, line, debuggerId):
         """
         Public method to process a line of client output.
         
-        @param line client output (string)
+        @param line client output
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.clientOutput.emit(line)
-        
-    def signalClientLine(self, filename, lineno, forStack=False):
+        if debuggerId:
+            self.clientOutput.emit("{0}: {1}".format(debuggerId, line))
+        else:
+            self.clientOutput.emit(line)
+    
+    def signalClientLine(self, filename, lineno, debuggerId, forStack=False,
+                         threadName=""):
         """
         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)
+        @param filename name of the file currently being executed
+        @type str
+        @param lineno line of code currently being executed
+        @type int
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param forStack flag indicating this is for a stack dump
+        @type bool
+        @param threadName name of the thread signaling the event
+        @type str
         """
-        self.clientLine.emit(filename, lineno, forStack)
-        
-    def signalClientStack(self, stack):
+        self.clientLine.emit(filename, lineno, debuggerId, threadName,
+                             forStack)
+    
+    def signalClientStack(self, stack, debuggerId, threadName=""):
         """
         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))
+        @type list of lists of (string, integer, string)
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param threadName name of the thread signaling the event
+        @type str
         """
-        self.clientStack.emit(stack)
-        
-    def signalClientThreadList(self, currentId, threadList):
+        self.clientStack.emit(stack, debuggerId, threadName)
+    
+    def signalClientThreadList(self, currentId, threadList, debuggerId):
         """
         Public method to process the client thread list info.
         
-        @param currentId id of the current thread (integer)
+        @param currentId id of the current thread
+        @type int
         @param threadList list of dictionaries containing the thread data
+        @type list of dict
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.clientThreadList.emit(currentId, threadList)
-        
-    def signalClientThreadSet(self):
+        self.clientThreadList.emit(currentId, threadList, debuggerId)
+    
+    def signalClientThreadSet(self, debuggerId):
         """
         Public method to handle the change of the client thread.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.clientThreadSet.emit()
-        
-    def signalClientVariables(self, scope, variables):
+        self.clientThreadSet.emit(debuggerId)
+    
+    def signalClientVariables(self, scope, variables, debuggerId):
         """
         Public method to process the client variables info.
         
         @param scope scope of the variables (-1 = empty global, 1 = global,
             0 = local)
+        @type int
         @param variables the list of variables from the client
+        @type list
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.clientVariables.emit(scope, variables)
-        
-    def signalClientVariable(self, scope, variables):
+        self.clientVariables.emit(scope, variables, debuggerId)
+    
+    def signalClientVariable(self, scope, variables, debuggerId):
         """
         Public method to process the client variable info.
         
         @param scope scope of the variables (-1 = empty global, 1 = global,
             0 = local)
+        @type int
         @param variables the list of members of a classvariable from the client
+        @type list
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.clientVariable.emit(scope, variables)
-        
-    def signalClientStatement(self, more):
+        self.clientVariable.emit(scope, variables, debuggerId)
+    
+    def signalClientStatement(self, more, debuggerId):
         """
         Public method to process the input response from the client.
         
         @param more flag indicating that more user input is required
+        @type bool
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.clientStatement.emit(more)
-        
-    def signalClientDisassembly(self, disassembly):
+        self.clientStatement.emit(more, debuggerId)
+    
+    def signalClientDisassembly(self, disassembly, debuggerId):
         """
         Public method to process the disassembly info from the client.
         
         @param disassembly dictionary containing the disassembly information
         @type dict
+        @param debuggerId ID of the debugger backend
+        @type str
         """
         if self.running:
-            self.clientDisassembly.emit(disassembly)
+            self.clientDisassembly.emit(disassembly, debuggerId)
     
     def signalClientException(self, exceptionType, exceptionMessage,
-                              stackTrace):
+                              stackTrace, debuggerId, threadName=""):
         """
         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 exceptionType type of exception raised
+        @type str
+        @param exceptionMessage message given by the exception
+        @type str
         @param stackTrace list of stack entries with the exception position
             first. Each stack entry is a list giving the filename and the
             linenumber.
+        @type list
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param threadName name of the thread signaling the event
+        @type str
         """
         if self.running:
             self.clientException.emit(exceptionType, exceptionMessage,
-                                      stackTrace)
-        
-    def signalClientSyntaxError(self, message, filename, lineNo, characterNo):
+                                      stackTrace, debuggerId, threadName)
+    
+    def signalClientSyntaxError(self, message, filename, lineNo, characterNo,
+                                debuggerId, threadName=""):
         """
-        Public method to process the syntax error info from the client.
+        Public method to process a syntax error info from the client.
         
-        @param message message of the syntax error (string)
+        @param message message of the syntax error
+        @type str
         @param filename translated filename of the syntax error position
-            (string)
-        @param lineNo line number of the syntax error position (integer)
+        @type str
+        @param lineNo line number of the syntax error position
+        @type int
         @param characterNo character number of the syntax error position
-            (integer)
+        @type int
+        @param debuggerId ID of the debugger backend
+        @type str
+        @param threadName name of the thread signaling the event
+        @type str
         """
         if self.running:
-            self.clientSyntaxError.emit(message, filename, lineNo, characterNo)
-        
+            self.clientSyntaxError.emit(message, filename, lineNo, characterNo,
+                                        debuggerId, threadName)
+    
     def signalClientSignal(self, message, filename, lineNo,
-                           funcName, funcArgs):
+                           funcName, funcArgs, debuggerId):
         """
         Public method to process a signal generated on the client side.
         
@@ -1565,23 +1817,45 @@
         @type str
         @param funcArgs function arguments
         @type str
+        @param debuggerId ID of the debugger backend
+        @type str
         """
         if self.running:
             self.clientSignal.emit(message, filename, lineNo,
-                                   funcName, funcArgs)
+                                   funcName, funcArgs, debuggerId)
+    
+    def signalClientDisconnected(self, debuggerId):
+        """
+        Public method to send a signal when a debug client has closed its
+        connection.
         
-    def signalClientExit(self, status, message=""):
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientDisconnected.emit(debuggerId)
+    
+    def signalClientExit(self, program, status, message, debuggerId):
         """
         Public method to process the client exit status.
         
+        @param program name of the exited program
+        @type str
         @param status exit code
         @type int
         @param message message sent with the exit
         @type str
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientExit.emit(program, int(status), message, False, debuggerId)
+    
+    def signalLastClientExited(self):
+        """
+        Public method to process the last client exit event.
         """
         if self.passive:
             self.__passiveShutDown()
-        self.clientExit.emit(int(status), message, False)
+        self.lastClientExited.emit()
         if Preferences.getDebugger("AutomaticReset") or (self.running and
                                                          not self.debugging):
             self.debugging = False
@@ -1589,53 +1863,75 @@
         if self.passive:
             self.__createDebuggerInterface("None")
             self.signalClientOutput(self.tr('\nNot connected\n'))
-            self.signalClientStatement(False)
+            self.signalClientStatement(False, "")
         self.running = False
-        
-    def signalClientClearBreak(self, filename, lineno):
+    
+    def signalClientClearBreak(self, filename, lineno, debuggerId):
         """
         Public method to process the client clear breakpoint command.
         
-        @param filename filename of the breakpoint (string)
-        @param lineno line umber of the breakpoint (integer)
+        @param filename filename of the breakpoint
+        @type str
+        @param lineno line umber of the breakpoint
+        @type int
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.clientClearBreak.emit(filename, lineno)
-        
-    def signalClientBreakConditionError(self, filename, lineno):
+        self.clientClearBreak.emit(filename, lineno, debuggerId)
+    
+    def signalClientBreakConditionError(self, filename, lineno, debuggerId):
         """
         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)
+        @param filename filename of the breakpoint
+        @type str
+        @param lineno line umber of the breakpoint
+        @type int
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.clientBreakConditionError.emit(filename, lineno)
-        
-    def signalClientClearWatch(self, condition):
+        if (filename, lineno) not in self.__reportedBreakpointIssues:
+            self.__reportedBreakpointIssues.append((filename, lineno))
+            self.clientBreakConditionError.emit(filename, lineno, debuggerId)
+    
+    def signalClientClearWatch(self, condition, debuggerId):
         """
         Public slot to handle the clientClearWatch signal.
         
-        @param condition expression of watch expression to clear (string)
+        @param condition expression of watch expression to clear
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.clientClearWatch.emit(condition)
-        
-    def signalClientWatchConditionError(self, condition):
+        self.clientClearWatch.emit(condition, debuggerId)
+    
+    def signalClientWatchConditionError(self, condition, debuggerId):
         """
         Public method to process the client watch expression error info.
         
-        @param condition expression of watch expression to clear (string)
+        @param condition expression of watch expression to clear
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.clientWatchConditionError.emit(condition)
-        
-    def signalClientRawInput(self, prompt, echo):
+        if condition not in self.__reportedWatchpointIssues:
+            self.__reportedWatchpointIssues.append(condition)
+            self.clientWatchConditionError.emit(condition, debuggerId)
+    
+    def signalClientRawInput(self, prompt, echo, debuggerId):
         """
         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)
+        @param prompt the input prompt
+        @type str
+        @param echo flag indicating an echoing of the input
+        @type bool
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.clientRawInput.emit(prompt, echo)
-        
-    def signalClientBanner(self, version, platform, debugClient, venvName):
+        self.clientRawInput.emit(prompt, echo, debuggerId)
+    
+    def signalClientBanner(self, version, platform, venvName):
         """
         Public method to process the client banner info.
         
@@ -1643,12 +1939,10 @@
         @type str
         @param platform hostname of the client
         @type str
-        @param debugClient additional debugger type info
-        @type str
         @param venvName name of the virtual environment
         @type str
         """
-        self.clientBanner.emit(version, platform, debugClient, venvName)
+        self.clientBanner.emit(version, platform, venvName)
     
     def signalClientCapabilities(self, capabilities, clientType, venvName):
         """
@@ -1667,33 +1961,46 @@
         except KeyError:
             # ignore silently
             pass
-        
-    def signalClientCompletionList(self, completionList, text):
+    
+    def signalClientCompletionList(self, completionList, text, debuggerId):
         """
         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)
+        @param completionList list of possible completions
+        @type list of str
+        @param text the text to be completed
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
         """
         self.clientCompletionList.emit(completionList, text)
-        
+    
     def signalClientCallTrace(self, isCall, fromFile, fromLine, fromFunction,
-                              toFile, toLine, toFunction):
+                              toFile, toLine, toFunction, debuggerId):
         """
         Public method to process the client call trace data.
         
-        @param isCall flag indicating a 'call' (boolean)
-        @param fromFile name of the originating file (string)
-        @param fromLine line number in the originating file (string)
-        @param fromFunction name of the originating function (string)
-        @param toFile name of the target file (string)
-        @param toLine line number in the target file (string)
-        @param toFunction name of the target function (string)
+        @param isCall flag indicating a 'call'
+        @type bool
+        @param fromFile name of the originating file
+        @type str
+        @param fromLine line number in the originating file
+        @type str
+        @param fromFunction name of the originating function
+        @type str
+        @param toFile name of the target file
+        @type str
+        @param toLine line number in the target file
+        @type str
+        @param toFunction name of the target function
+        @type str
+        @param debuggerId ID of the debugger backend
+        @type str
         """
         self.callTraceInfo.emit(
             isCall, fromFile, fromLine, fromFunction,
-            toFile, toLine, toFunction)
-        
+            toFile, toLine, toFunction, debuggerId)
+    
     def clientUtDiscovered(self, testCases, exceptionType, exceptionValue):
         """
         Public method to process the client unittest discover info.
@@ -1706,81 +2013,100 @@
         @type str
         """
         self.utDiscovered.emit(testCases, exceptionType, exceptionValue)
-        
+    
     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)
+        @param result number of test cases (0 = error)
+        @type int
+        @param exceptionType exception type
+        @type str
+        @param exceptionValue exception message
+        @type str
         """
         self.utPrepared.emit(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)
+        @param testname name of the test
+        @type str
+        @param doc short description of the test
+        @type str
         """
         self.utStartTest.emit(testname, doc)
-        
+    
     def clientUtStopTest(self):
         """
         Public method to process the client stop test info.
         """
         self.utStopTest.emit()
-        
+    
     def clientUtTestFailed(self, testname, traceback, testId):
         """
         Public method to process the client test failed info.
         
-        @param testname name of the test (string)
-        @param traceback lines of traceback info (list of strings)
-        @param testId id of the test (string)
+        @param testname name of the test
+        @type str
+        @param traceback lines of traceback info
+        @type list of str
+        @param testId id of the test
+        @type str
         """
         self.utTestFailed.emit(testname, traceback, testId)
-        
+    
     def clientUtTestErrored(self, testname, traceback, testId):
         """
         Public method to process the client test errored info.
         
-        @param testname name of the test (string)
-        @param traceback lines of traceback info (list of strings)
-        @param testId id of the test (string)
+        @param testname name of the test
+        @type str
+        @param traceback lines of traceback info
+        @type list of str
+        @param testId id of the test
+        @type str
         """
         self.utTestErrored.emit(testname, traceback, testId)
-        
+    
     def clientUtTestSkipped(self, testname, reason, testId):
         """
         Public method to process the client test skipped info.
         
-        @param testname name of the test (string)
-        @param reason reason for skipping the test (string)
-        @param testId id of the test (string)
+        @param testname name of the test
+        @type str
+        @param reason reason for skipping the test
+        @type str
+        @param testId id of the test
+        @type str
         """
         self.utTestSkipped.emit(testname, reason, testId)
-        
+    
     def clientUtTestFailedExpected(self, testname, traceback, testId):
         """
         Public method to process the client test failed expected info.
         
-        @param testname name of the test (string)
-        @param traceback lines of traceback info (list of strings)
-        @param testId id of the test (string)
+        @param testname name of the test
+        @type str
+        @param traceback lines of traceback info
+        @type list of str
+        @param testId id of the test
+        @type str
         """
         self.utTestFailedExpected.emit(testname, traceback, testId)
-        
+    
     def clientUtTestSucceededUnexpected(self, testname, testId):
         """
         Public method to process the client test succeeded unexpected info.
         
-        @param testname name of the test (string)
-        @param testId id of the test (string)
+        @param testname name of the test
+        @type str
+        @param testId id of the test
+        @type str
         """
         self.utTestSucceededUnexpected.emit(testname, testId)
-        
+    
     def clientUtFinished(self, status):
         """
         Public method to process the client unit test finished info.
@@ -1790,25 +2116,29 @@
         """
         self.utFinished.emit()
         
-        self.clientExit.emit(int(status), "", True)
+        self.clientExit.emit("", int(status), "", True, "")
         self.debugging = False
         self.running = False
-        
-    def passiveStartUp(self, fn, exc):
+    
+    def passiveStartUp(self, fn, exc, debuggerId):
         """
         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)
+        @param fn filename of the debugged script
+        @type str
+        @param exc flag to enable exception reporting of the IDE
+        @type bool
+        @param debuggerId ID of the debugger backend
+        @type str
         """
         self.appendStdout.emit(self.tr("Passive debug connection received\n"))
         self.passiveClientExited = False
         self.debugging = True
         self.running = True
-        self.__restoreBreakpoints()
-        self.__restoreWatchpoints()
+        self.__restoreBreakpoints(debuggerId)
+        self.__restoreWatchpoints(debuggerId)
         self.passiveDebugStarted.emit(fn, exc)
-        
+    
     def __passiveShutDown(self):
         """
         Private method to shut down a passive debug connection.
@@ -1816,37 +2146,48 @@
         self.passiveClientExited = True
         self.shutdownServer()
         self.appendStdout.emit(self.tr("Passive debug connection closed\n"))
-        
-    def __restoreBreakpoints(self):
+    
+    def __restoreBreakpoints(self, debuggerId=""):
         """
         Private method to restore the breakpoints after a restart.
+        
+        @param debuggerId ID of the debugger backend to send to. If this is
+            empty, they will be broadcast to all connected backends.
+        @type str
         """
         if self.debugging:
             self.__addBreakPoints(
-                QModelIndex(), 0, self.breakpointModel.rowCount() - 1)
+                QModelIndex(), 0, self.breakpointModel.rowCount() - 1,
+                debuggerId)
     
-    def __restoreWatchpoints(self):
+    def __restoreWatchpoints(self, debuggerId=""):
         """
         Private method to restore the watch expressions after a restart.
+        
+        @param debuggerId ID of the debugger backend to send to. If this is
+            empty, they will be broadcast to all connected backends.
+        @type str
         """
         if self.debugging:
             self.__addWatchPoints(
-                QModelIndex(), 0, self.watchpointModel.rowCount() - 1)
+                QModelIndex(), 0, self.watchpointModel.rowCount() - 1,
+                debuggerId)
     
     def getBreakPointModel(self):
         """
         Public slot to get a reference to the breakpoint model object.
         
-        @return reference to the breakpoint model object (BreakPointModel)
+        @return reference to the breakpoint model object
+        @rtype 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)
+        @rtype WatchPointModel
         """
         return self.watchpointModel
     
@@ -1854,7 +2195,8 @@
         """
         Public method to test, if the debug server is connected to a backend.
         
-        @return flag indicating a connection (boolean)
+        @return flag indicating a connection
+        @rtype bool
         """
         return self.debuggerInterface and self.debuggerInterface.isConnected()
     
@@ -1875,3 +2217,50 @@
         @type bool
         """
         self.debugging = on
+    
+    def signalClientDebuggerId(self, debuggerId):
+        """
+        Public method to signal the receipt of a new debugger ID.
+        
+        This signal indicates, that a new debugger backend has connected.
+        
+        @param debuggerId ID of the newly connected debugger backend
+        @type str
+        """
+        self.clientDebuggerId.emit(debuggerId)
+    
+    def getDebuggerIds(self):
+        """
+        Public method to return the IDs of the connected debugger backends.
+        
+        @return list of connected debugger backend IDs
+        @rtype list of str
+        """
+        if self.debuggerInterface:
+            return self.debuggerInterface.getDebuggerIds()
+        else:
+            return []
+    
+    def initializeClient(self, debuggerId):
+        """
+        Public method to initialize a freshly connected debug client.
+        
+        @param debuggerId ID of the connected debugger
+        @type str
+        """
+        self.__restoreBreakpoints(debuggerId)
+        self.__restoreWatchpoints(debuggerId)
+        self.__restoreNoDebugList(debuggerId)
+    
+    def __restoreNoDebugList(self, debuggerId=""):
+        """
+        Private method to restore the list of scripts not to be debugged after
+        a restart.
+        
+        @param debuggerId ID of the debugger backend to send to. If this is
+            empty, they will be broadcast to all connected backends.
+        @type str
+        """
+        if self.debugging:
+            self.debuggerInterface.remoteNoDebugList(
+                debuggerId, self.__multiprocessNoDebugList)

eric ide

mercurial