eric6/Debugger/DebugServer.py

branch
multi_processing
changeset 7379
72a72fd56494
parent 7377
cc920e534ac0
child 7389
770ffcb88be5
diff -r cc920e534ac0 -r 72a72fd56494 eric6/Debugger/DebugServer.py
--- a/eric6/Debugger/DebugServer.py	Thu Jan 30 19:37:03 2020 +0100
+++ b/eric6/Debugger/DebugServer.py	Sat Feb 01 19:48:21 2020 +0100
@@ -50,23 +50,28 @@
         executed a line of code
     @signal clientThreadList(currentId, threadList, debuggerId) 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 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 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 clientException(exceptionType, exceptionMessage, stackTrace,
+        debuggerId) emitted after an exception occured on the client side
+    @signal clientSyntaxError(message, filename, linenumber, characternumber,
+        debuggerId) 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 clientExit(int, str, bool, str) emitted after the client has exited
+        giving 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) emitted after the debug client
         has decided to clear a temporary breakpoint
     @signal clientBreakConditionError(fn, lineno) emitted after the client has
@@ -111,7 +116,7 @@
         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 clientDebuggerIds(debuggerIds) emitted to give the list of IDs of
@@ -127,14 +132,15 @@
     clientLine = pyqtSignal(str, int, str, bool)
     clientStack = pyqtSignal(list, str)
     clientThreadList = pyqtSignal(int, list, str)
-    clientThreadSet = pyqtSignal()
-    clientVariables = pyqtSignal(int, list)
-    clientVariable = pyqtSignal(int, list)
-    clientStatement = pyqtSignal(bool)
-    clientException = pyqtSignal(str, str, list)
-    clientSyntaxError = pyqtSignal(str, str, int, int)
-    clientSignal = pyqtSignal(str, str, int, str, str)
-    clientExit = pyqtSignal(int, str, bool)
+    clientThreadSet = pyqtSignal(str)
+    clientVariables = pyqtSignal(int, list, str)
+    clientVariable = pyqtSignal(int, list, str)
+    clientStatement = pyqtSignal(bool, str)
+    clientException = pyqtSignal(str, str, list, str)
+    clientSyntaxError = pyqtSignal(str, str, int, int, str)
+    clientSignal = pyqtSignal(str, str, int, str, str, str)
+    clientExit = pyqtSignal(int, str, bool, str)
+    lastClientExited = pyqtSignal()
     clientBreakConditionError = pyqtSignal(str, int)
     clientWatchConditionError = pyqtSignal(str)
     clientRawInput = pyqtSignal(str, bool)
@@ -154,7 +160,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):
@@ -531,8 +537,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)
@@ -586,7 +590,8 @@
                 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)
 
     def __changeBreakPoints(self, startIndex, endIndex):
         """
@@ -611,24 +616,31 @@
             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 = (
                     self.breakpointModel.getBreakPointByIndex(index)[:6])
-                self.remoteBreakpoint(fn, line, True, cond, temp)
+                self.remoteBreakpoint(debuggerId, fn, line, True, cond, temp)
                 if not enabled:
-                    self.__remoteBreakpointEnable(fn, line, False)
+                    self.__remoteBreakpointEnable(debuggerId, fn, line, False)
                 if ignorecount:
-                    self.__remoteBreakpointIgnore(fn, line, ignorecount)
+                    self.__remoteBreakpointIgnore(
+                        debuggerId, fn, line, ignorecount)
 
     def __makeWatchCondition(self, cond, special):
         """
@@ -682,9 +694,12 @@
         """
         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):
@@ -692,27 +707,35 @@
                 cond, special = (
                     self.watchpointModel.getWatchPointByIndex(index)[0:2])
                 cond = self.__makeWatchCondition(cond, special)
-                self.__remoteWatchpoint(cond, False)
+                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)
+        @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):
@@ -720,11 +743,12 @@
                 cond, special, temp, enabled, ignorecount = (
                     self.watchpointModel.getWatchPointByIndex(index)[:5])
                 cond = self.__makeWatchCondition(cond, special)
-                self.__remoteWatchpoint(cond, True, temp)
+                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):
         """
@@ -803,19 +827,22 @@
             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):
         """
@@ -912,7 +939,7 @@
         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,
@@ -1106,123 +1133,172 @@
         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)
+        self.debuggerInterface.remoteStatement(debuggerId, stmt.rstrip())
 
-    def remoteStep(self):
+    def remoteStep(self, debuggerId):
         """
         Public method to single step the debugged program.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.debuggerInterface.remoteStep()
+        self.debuggerInterface.remoteStep(debuggerId)
 
-    def remoteStepOver(self):
+    def remoteStepOver(self, debuggerId):
         """
         Public method to step over the debugged program.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.debuggerInterface.remoteStepOver()
+        self.debuggerInterface.remoteStepOver(debuggerId)
 
-    def remoteStepOut(self):
+    def remoteStepOut(self, debuggerId):
         """
         Public method to step out the debugged program.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.debuggerInterface.remoteStepOut()
+        self.debuggerInterface.remoteStepOut(debuggerId)
 
-    def remoteStepQuit(self):
+    def remoteStepQuit(self, debuggerId):
         """
         Public method to stop the debugged program.
+        
+        @param debuggerId ID of the debugger backend
+        @type str
         """
-        self.debuggerInterface.remoteStepQuit()
+        self.debuggerInterface.remoteStepQuit(debuggerId)
 
-    def remoteContinue(self, special=False):
+    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)
+        self.debuggerInterface.remoteContinue(debuggerId, special)
 
-    def remoteMoveIP(self, 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)
+        self.debuggerInterface.remoteMoveIP(debuggerId, line)
 
-    def remoteBreakpoint(self, fn, line, setBreakpoint, cond=None, temp=False):
+    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)
+        self.debuggerInterface.remoteBreakpoint(
+            debuggerId, fn, line, setBreakpoint, cond, temp)
         
-    def __remoteBreakpointEnable(self, fn, line, enable):
+    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)
+        self.debuggerInterface.remoteBreakpointEnable(
+            debuggerId, fn, line, enable)
         
-    def __remoteBreakpointIgnore(self, fn, line, count):
+    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)
+        self.debuggerInterface.remoteBreakpointIgnore(
+            debuggerId, fn, line, count)
         
-    def __remoteWatchpoint(self, cond, setWatch, temp=False):
+    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):
         """
@@ -1233,64 +1309,74 @@
         self.debuggerInterface.remoteRawInput(s)
         self.clientRawInputSent.emit()
         
-    def remoteThreadList(self, 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(debuggerId=debuggerId)
+        self.debuggerInterface.remoteThreadList(debuggerId)
         
-    def remoteSetThread(self, tid, debuggerId=""):
+    def remoteSetThread(self, debuggerId, tid):
         """
         Public method to request to set the given thread as current thread.
         
-        @param tid id of the thread
-        @type int
         @param debuggerId ID of the debugger backend
         @type str
+        @param tid id of the thread
+        @type int
         """
-        self.debuggerInterface.remoteSetThread(tid, debuggerId=debuggerId)
+        self.debuggerInterface.remoteSetThread(debuggerId, tid)
     
-    def remoteClientStack(self, debuggerId=""):
+    def remoteClientStack(self, debuggerId):
         """
         Public method to request the stack of the main thread.
         
         @param debuggerId ID of the debugger backend
         @type str
         """
-        self.debuggerInterface.remoteClientStack(debuggerId=debuggerId)
+        self.debuggerInterface.remoteClientStack(debuggerId)
     
-    def remoteClientVariables(self, scope, filterList, framenr=0,
-                              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)
         @type int
         @param filterList list of variable types to filter out
         @type list of int
         @param framenr framenumber of the variables to retrieve
         @type int
-        @param debuggerId ID of the debugger backend
-        @type str
         """
         self.debuggerInterface.remoteClientVariables(
-            scope, filterList, framenr, self.__maxVariableSize,
-            debuggerId=debuggerId)
+            debuggerId, scope, filterList, framenr, self.__maxVariableSize)
         
-    def remoteClientVariable(self, scope, filterList, var, framenr=0):
+    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 int
+        @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 remoteClientSetFilter(self, scope, filterStr):
         """
@@ -1302,13 +1388,16 @@
         """
         self.debuggerInterface.remoteClientSetFilter(scope, filterStr)
         
-    def setCallTraceEnabled(self, on):
+    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):
         """
@@ -1516,71 +1605,96 @@
         """
         self.clientThreadList.emit(currentId, threadList, debuggerId)
         
-    def signalClientThreadSet(self):
+    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()
+        self.clientThreadSet.emit(debuggerId)
         
-    def signalClientVariables(self, scope, variables):
+    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)
+        self.clientVariables.emit(scope, variables, debuggerId)
         
-    def signalClientVariable(self, scope, variables):
+    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)
+        self.clientVariable.emit(scope, variables, debuggerId)
         
-    def signalClientStatement(self, more):
+    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)
+        self.clientStatement.emit(more, debuggerId)
         
     def signalClientException(self, exceptionType, exceptionMessage,
-                              stackTrace):
+                              stackTrace, debuggerId):
         """
         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
         """
         if self.running:
             self.clientException.emit(exceptionType, exceptionMessage,
-                                      stackTrace)
+                                      stackTrace, debuggerId)
         
-    def signalClientSyntaxError(self, message, filename, lineNo, characterNo):
+    def signalClientSyntaxError(self, message, filename, lineNo, characterNo,
+                                debuggerId):
         """
-        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
         """
         if self.running:
-            self.clientSyntaxError.emit(message, filename, lineNo, characterNo)
+            self.clientSyntaxError.emit(message, filename, lineNo, characterNo,
+                                        debuggerId)
         
     def signalClientSignal(self, message, filename, lineNo,
-                           funcName, funcArgs):
+                           funcName, funcArgs, debuggerId):
         """
         Public method to process a signal generated on the client side.
         
@@ -1594,12 +1708,14 @@
         @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 signalClientExit(self, status, message=""):
+    def signalClientExit(self, status, message, debuggerId):
         """
         Public method to process the client exit status.
         
@@ -1607,10 +1723,18 @@
         @type int
         @param message message sent with the exit
         @type str
+        @param debuggerId ID of the debugger backend
+        @type str
+        """
+        self.clientExit.emit(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
@@ -1618,9 +1742,9 @@
         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):
         """
         Public method to process the client clear breakpoint command.
@@ -1707,21 +1831,30 @@
         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):
         """
@@ -1819,7 +1952,7 @@
         """
         self.utFinished.emit()
         
-        self.clientExit.emit(int(status), "", True)
+        self.clientExit.emit(int(status), "", True, "")
         self.debugging = False
         self.running = False
         
@@ -1846,21 +1979,31 @@
         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):
         """
@@ -1927,3 +2070,13 @@
             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)

eric ide

mercurial