Sun, 02 Feb 2020 16:41:40 +0100
Continued with the multiprocess debugger.
--- a/eric6/Debugger/DebugServer.py Sun Feb 02 12:01:27 2020 +0100 +++ b/eric6/Debugger/DebugServer.py Sun Feb 02 16:41:40 2020 +0100 @@ -11,7 +11,7 @@ import os import sys -from PyQt5.QtCore import pyqtSignal, QModelIndex +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QModelIndex from PyQt5.QtNetwork import ( QTcpServer, QHostAddress, QHostInfo, QNetworkInterface ) @@ -42,8 +42,8 @@ @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 clientRawInputSent(debuggerId) emitted after the data was sent + to the indicated debug client @signal clientLine(filename, lineno, debuggerId, forStack) emitted after the debug client has executed a line of code @signal clientStack(stack, debuggerId) emitted after the debug client has @@ -72,16 +72,16 @@ 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 - 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 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, dbgclient, venvname) emitted after the client banner data was received @signal clientCapabilities(capabilities, cltype, venvname) emitted after @@ -122,12 +122,13 @@ @signal clientDebuggerIds(debuggerIds) emitted to give the list of IDs of attached debugger backends """ - clientClearBreak = pyqtSignal(str, int) - clientClearWatch = pyqtSignal(str) + clientClearBreak = pyqtSignal(str, int, str) + clientClearWatch = pyqtSignal(str, str) + # TODO: check this signal for debuggerId clientGone = pyqtSignal(bool) clientProcessStdout = pyqtSignal(str) clientProcessStderr = pyqtSignal(str) - clientRawInputSent = pyqtSignal() + clientRawInputSent = pyqtSignal(str) clientOutput = pyqtSignal(str) clientLine = pyqtSignal(str, int, str, bool) clientStack = pyqtSignal(list, str) @@ -141,12 +142,13 @@ 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) + clientBreakConditionError = pyqtSignal(str, int, str) + clientWatchConditionError = pyqtSignal(str, str) + clientRawInput = pyqtSignal(str, bool, str) clientBanner = pyqtSignal(str, str, str, str) clientCapabilities = pyqtSignal(int, str, str) clientCompletionList = pyqtSignal(list, str) + # TODO: check this signal for debuggerId clientInterpreterChanged = pyqtSignal(str) clientDebuggerIds = pyqtSignal(list) utDiscovered = pyqtSignal(list, str, str) @@ -195,6 +197,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) @@ -259,15 +265,16 @@ self.__maxVariableSize = Preferences.getDebugger("MaxVariableSize") 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: @@ -282,14 +289,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(): @@ -302,7 +311,7 @@ networkInterface.index()) return "", 0 - + def preferencesChanged(self): """ Public slot to handle the preferencesChanged signal. @@ -318,7 +327,7 @@ reregister=True) self.__maxVariableSize = Preferences.getDebugger("MaxVariableSize") - + def registerDebuggerInterface(self, interfaceName, getRegistryData, reregister=False): """ @@ -352,7 +361,7 @@ self.__debuggerInterfaceRegistry[clientLanguage] = [ clientCapabilities, clientExtensions, interfaceCreator, interfaceName] - + def unregisterDebuggerInterface(self, interfaceName): """ Public method to unregister a debugger interface. @@ -369,7 +378,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. @@ -384,7 +393,7 @@ return language return "" - + def __registerDebuggerInterfaces(self): """ Private method to register the available internal debugger interfaces. @@ -397,14 +406,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: @@ -418,25 +429,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: @@ -450,18 +463,19 @@ 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): """ @@ -547,7 +561,7 @@ if clientInterpreter != self.clientInterpreter: self.clientInterpreter = clientInterpreter self.clientInterpreterChanged.emit(clientInterpreter) - + def __clientProcessOutput(self): """ Private slot to process client output received via stdout. @@ -556,7 +570,7 @@ Preferences.getSystem("IOEncoding"), 'replace') self.clientProcessStdout.emit(output) - + def __clientProcessError(self): """ Private slot to process client output received via stderr. @@ -565,25 +579,33 @@ 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): @@ -592,30 +614,36 @@ self.breakpointModel.getBreakPointByIndex(index)[0:2]) # 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, debuggerId=""): """ Private slot to add breakpoints. @@ -633,22 +661,30 @@ 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(debuggerId, 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(debuggerId, fn, line, False) + self.__remoteBreakpointEnable( + debuggerId, fn, lineno, False) if ignorecount: self.__remoteBreakpointIgnore( - debuggerId, fn, line, ignorecount) - + 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 @@ -658,14 +694,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() @@ -678,18 +715,22 @@ 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. @@ -708,7 +749,9 @@ self.watchpointModel.getWatchPointByIndex(index)[0:2]) cond = self.__makeWatchCondition(cond, special) 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 @@ -722,7 +765,7 @@ if self.debugging: self.__deleteWatchPoints( QModelIndex(), startIndex.row(), endIndex.row()) - + def __addWatchPoints(self, parentIndex, start, end, debuggerId=""): """ Private slot to set a watch expression. @@ -743,41 +786,50 @@ cond, special, temp, enabled, ignorecount = ( self.watchpointModel.getWatchPointByIndex(index)[:5]) cond = self.__makeWatchCondition(cond, special) + + if cond in self.__reportedWatchpointIssues: + self.__reportedWatchpointIssues.remove(cond) + self.__remoteWatchpoint(debuggerId, cond, True, temp) if not enabled: self.__remoteWatchpointEnable(debuggerId, cond, False) if 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 @@ -843,7 +895,7 @@ self.remoteBanner() elif self.passive: self.remoteBanner() - + def shutdownServer(self): """ Public method to cleanly shut down. @@ -853,12 +905,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 = {} @@ -871,7 +924,7 @@ 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, @@ -948,7 +1001,7 @@ self.running = True self.__restoreBreakpoints() self.__restoreWatchpoints() - + def remoteRun(self, venvName, fn, argv, wd, env, autoClearShell=True, forProject=False, runInConsole=False, autoFork=False, forkChild=False, clientType=""): @@ -1010,7 +1063,7 @@ self.debuggerInterface.remoteRun(fn, argv, wd, autoFork, forkChild) self.debugging = False self.running = True - + def remoteCoverage(self, venvName, fn, argv, wd, env, autoClearShell=True, erase=False, forProject=False, runInConsole=False, clientType=""): @@ -1071,7 +1124,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=""): @@ -1132,7 +1185,7 @@ self.debuggerInterface.remoteProfile(fn, argv, wd, erase) self.debugging = False self.running = True - + def remoteStatement(self, debuggerId, stmt): """ Public method to execute a Python statement. @@ -1143,7 +1196,7 @@ @type str """ self.debuggerInterface.remoteStatement(debuggerId, stmt.rstrip()) - + def remoteStep(self, debuggerId): """ Public method to single step the debugged program. @@ -1152,7 +1205,7 @@ @type str """ self.debuggerInterface.remoteStep(debuggerId) - + def remoteStepOver(self, debuggerId): """ Public method to step over the debugged program. @@ -1161,7 +1214,7 @@ @type str """ self.debuggerInterface.remoteStepOver(debuggerId) - + def remoteStepOut(self, debuggerId): """ Public method to step out the debugged program. @@ -1170,7 +1223,7 @@ @type str """ self.debuggerInterface.remoteStepOut(debuggerId) - + def remoteStepQuit(self, debuggerId): """ Public method to stop the debugged program. @@ -1179,7 +1232,7 @@ @type str """ self.debuggerInterface.remoteStepQuit(debuggerId) - + def remoteContinue(self, debuggerId, special=False): """ Public method to continue the debugged program. @@ -1189,7 +1242,7 @@ @param special flag indicating a special continue operation """ self.debuggerInterface.remoteContinue(debuggerId, special) - + def remoteMoveIP(self, debuggerId, line): """ Public method to move the instruction pointer to a different line. @@ -1200,7 +1253,7 @@ @type int """ self.debuggerInterface.remoteMoveIP(debuggerId, line) - + def remoteBreakpoint(self, debuggerId, fn, line, setBreakpoint, cond=None, temp=False): """ @@ -1221,7 +1274,7 @@ """ self.debuggerInterface.remoteBreakpoint( debuggerId, fn, line, setBreakpoint, cond, temp) - + def __remoteBreakpointEnable(self, debuggerId, fn, line, enable): """ Private method to enable or disable a breakpoint. @@ -1237,7 +1290,7 @@ """ 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. @@ -1253,7 +1306,7 @@ """ self.debuggerInterface.remoteBreakpointIgnore( debuggerId, fn, line, count) - + def __remoteWatchpoint(self, debuggerId, cond, setWatch, temp=False): """ Private method to set or clear a watch expression. @@ -1300,15 +1353,18 @@ # cond is combination of cond and special (s. watch expression viewer) 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() - + self.debuggerInterface.remoteRawInput(debuggerId, inputString) + self.clientRawInputSent.emit(debuggerId) + def remoteThreadList(self, debuggerId): """ Public method to request the list of threads from the client. @@ -1317,7 +1373,7 @@ @type str """ self.debuggerInterface.remoteThreadList(debuggerId) - + def remoteSetThread(self, debuggerId, tid): """ Public method to request to set the given thread as current thread. @@ -1353,7 +1409,7 @@ """ self.debuggerInterface.remoteClientVariables( debuggerId, scope, filterList, framenr, self.__maxVariableSize) - + def remoteClientVariable(self, debuggerId, scope, filterList, var, framenr=0, maxSize=0): """ @@ -1377,17 +1433,21 @@ self.debuggerInterface.remoteClientVariable( debuggerId, scope, filterList, var, framenr, self.__maxVariableSize) - - 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) - + self.debuggerInterface.remoteClientSetFilter( + debuggerId, scope, filterStr) + def setCallTraceEnabled(self, debuggerId, on): """ Public method to set the call trace state. @@ -1398,19 +1458,19 @@ @type bool """ 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): """ Public slot to get the a list of possible commandline completions @@ -1533,7 +1593,7 @@ if debug: self.__restoreBreakpoints() self.__restoreWatchpoints() - + def remoteUTRun(self, debug=False, failfast=False): """ Public method to start a unittest run. @@ -1544,13 +1604,13 @@ @type bool """ self.debuggerInterface.remoteUTRun(debug, failfast) - + def remoteUTStop(self): """ public method to stop a unittest run. """ self.debuggerInterface.remoteUTStop() - + def signalClientOutput(self, line, debuggerId): """ Public method to process a line of client output. @@ -1564,7 +1624,7 @@ self.clientOutput.emit("{0}: {1}".format(debuggerId, line)) else: self.clientOutput.emit(line) - + def signalClientLine(self, filename, lineno, debuggerId, forStack=False): """ Public method to process client position feedback. @@ -1579,7 +1639,7 @@ @type bool """ self.clientLine.emit(filename, lineno, debuggerId, forStack) - + def signalClientStack(self, stack, debuggerId): """ Public method to process a client's stack information. @@ -1591,7 +1651,7 @@ @type str """ self.clientStack.emit(stack, debuggerId) - + def signalClientThreadList(self, currentId, threadList, debuggerId): """ Public method to process the client thread list info. @@ -1604,7 +1664,7 @@ @type str """ self.clientThreadList.emit(currentId, threadList, debuggerId) - + def signalClientThreadSet(self, debuggerId): """ Public method to handle the change of the client thread. @@ -1613,7 +1673,7 @@ @type str """ self.clientThreadSet.emit(debuggerId) - + def signalClientVariables(self, scope, variables, debuggerId): """ Public method to process the client variables info. @@ -1627,7 +1687,7 @@ @type str """ self.clientVariables.emit(scope, variables, debuggerId) - + def signalClientVariable(self, scope, variables, debuggerId): """ Public method to process the client variable info. @@ -1641,7 +1701,7 @@ @type str """ self.clientVariable.emit(scope, variables, debuggerId) - + def signalClientStatement(self, more, debuggerId): """ Public method to process the input response from the client. @@ -1652,7 +1712,7 @@ @type str """ self.clientStatement.emit(more, debuggerId) - + def signalClientException(self, exceptionType, exceptionMessage, stackTrace, debuggerId): """ @@ -1672,7 +1732,7 @@ if self.running: self.clientException.emit(exceptionType, exceptionMessage, stackTrace, debuggerId) - + def signalClientSyntaxError(self, message, filename, lineNo, characterNo, debuggerId): """ @@ -1692,7 +1752,7 @@ if self.running: self.clientSyntaxError.emit(message, filename, lineNo, characterNo, debuggerId) - + def signalClientSignal(self, message, filename, lineNo, funcName, funcArgs, debuggerId): """ @@ -1714,7 +1774,7 @@ if self.running: self.clientSignal.emit(message, filename, lineNo, funcName, funcArgs, debuggerId) - + def signalClientExit(self, status, message, debuggerId): """ Public method to process the client exit status. @@ -1745,49 +1805,71 @@ 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) - + self.clientRawInput.emit(prompt, echo, debuggerId) + def signalClientBanner(self, version, platform, debugClient, venvName): """ Public method to process the client banner info. @@ -1820,16 +1902,18 @@ except KeyError: # ignore silently pass - + def signalClientCompletionList(self, completionList, text): """ Public method to process the client auto completion info. - @param completionList list of possible completions (list of strings) - @param text the text to be completed (string) + @param completionList list of possible completions + @type list of str + @param text the text to be completed + @type str """ self.clientCompletionList.emit(completionList, text) - + def signalClientCallTrace(self, isCall, fromFile, fromLine, fromFunction, toFile, toLine, toFunction, debuggerId): """ @@ -1868,81 +1952,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. @@ -1955,13 +2058,15 @@ self.clientExit.emit(int(status), "", True, "") self.debugging = False self.running = False - + def passiveStartUp(self, fn, exc): """ Public method to handle a passive debug connection. - @param fn filename of the debugged script (string) - @param exc flag to enable exception reporting of the IDE (boolean) + @param fn filename of the debugged script + @type str + @param exc flag to enable exception reporting of the IDE + @type bool """ self.appendStdout.emit(self.tr("Passive debug connection received\n")) self.passiveClientExited = False @@ -1970,7 +2075,7 @@ self.__restoreBreakpoints() self.__restoreWatchpoints() self.passiveDebugStarted.emit(fn, exc) - + def __passiveShutDown(self): """ Private method to shut down a passive debug connection. @@ -1978,7 +2083,7 @@ self.passiveClientExited = True self.shutdownServer() self.appendStdout.emit(self.tr("Passive debug connection closed\n")) - + def __restoreBreakpoints(self, debuggerId=""): """ Private method to restore the breakpoints after a restart. @@ -2009,16 +2114,17 @@ """ 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 @@ -2026,7 +2132,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()
--- a/eric6/Debugger/DebugUI.py Sun Feb 02 12:01:27 2020 +0100 +++ b/eric6/Debugger/DebugUI.py Sun Feb 02 16:41:40 2020 +0100 @@ -1366,12 +1366,16 @@ elif scope == 0: self.debugViewer.showVariable(variables, False) - def __clientBreakConditionError(self, filename, lineno): + def __clientBreakConditionError(self, filename, lineno, debuggerId): """ Private method to handle a condition error of a breakpoint. - @param filename filename of the breakpoint (string) - @param lineno linenumber 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 """ E5MessageBox.critical( self.ui, @@ -1401,13 +1405,16 @@ model.setBreakPointByIndex(index, fn, line, (cond, temp, enabled, count)) - def __clientWatchConditionError(self, cond): + def __clientWatchConditionError(self, cond, debuggerId): """ Private method to handle a expression error of a watch expression. Note: This can only happen for normal watch expressions - @param cond expression of the watch expression (string) + @param cond expression of the watch expression + @type str + @param debuggerId ID of the debugger backend + @type str """ E5MessageBox.critical( self.ui, @@ -1434,7 +1441,7 @@ cond, temp, enabled, count, special = dlg.getData() # check for duplicates - idx = self.__model.getWatchPointIndex(cond, special) + idx = model.getWatchPointIndex(cond, special) duplicate = (idx.isValid() and idx.internalPointer() != index.internalPointer()) if duplicate:
--- a/eric6/Debugger/DebugViewer.py Sun Feb 02 12:01:27 2020 +0100 +++ b/eric6/Debugger/DebugViewer.py Sun Feb 02 16:41:40 2020 +0100 @@ -8,15 +8,15 @@ The views avaliable are: <ul> - <li>variables viewer for global variables</li> - <li>variables viewer for local variables</li> + <li>selector showing all connected debugger backends</li> + <li>variables viewer for global variables for the selected debug client</li> + <li>variables viewer for local variables for the selected debug client</li> + <li>call stack viewer for the selected debug client</li> <li>call trace viewer</li> <li>viewer for breakpoints</li> <li>viewer for watch expressions</li> <li>viewer for exceptions</li> - <li>viewer for threads</li> - <li>a file browser (optional)</li> - <li>an interpreter shell (optional)</li> + <li>viewer for threads for the selected debug client</li> </ul> """ @@ -432,13 +432,8 @@ @param debuggerId ID of the debugger backend @type str """ - if debuggerId: - index = self.__debuggersCombo.findText(debuggerId, Qt.MatchExactly) - if index >= 0: - self.__debuggersCombo.setItemIcon( - index, UI.PixmapCache.getIcon("exceptions")) + self.__setDebuggerIcon(debuggerId, "exceptions") - # TODO: Refactor the icon setting code into a method def __clientSyntaxError(self, message, filename, lineNo, characterNo, debuggerId): """ @@ -455,11 +450,7 @@ @param debuggerId ID of the debugger backend @type str """ - if debuggerId: - index = self.__debuggersCombo.findText(debuggerId, Qt.MatchExactly) - if index >= 0: - self.__debuggersCombo.setItemIcon( - index, UI.PixmapCache.getIcon("syntaxError22")) + self.__setDebuggerIcon(debuggerId, "syntaxError22") def __clientException(self, exceptionType, exceptionMessage, stackTrace, debuggerId): @@ -475,11 +466,7 @@ @param debuggerId ID of the debugger backend @type str """ - if debuggerId: - index = self.__debuggersCombo.findText(debuggerId, Qt.MatchExactly) - if index >= 0: - self.__debuggersCombo.setItemIcon( - index, UI.PixmapCache.getIcon("exceptions")) + self.__setDebuggerIcon(debuggerId, "exceptions") def setVariablesFilter(self, globalsFilter, localsFilter): """ @@ -523,7 +510,8 @@ """ if self.debugServer.isDebugging(): filterStr = self.globalsFilterEdit.text() - self.debugServer.remoteClientSetFilter(1, filterStr) + self.debugServer.remoteClientSetFilter( + self.getSelectedDebuggerId(), 1, filterStr) self.debugServer.remoteClientVariables( self.getSelectedDebuggerId(), 2, self.globalsFilter) @@ -533,7 +521,8 @@ """ if self.debugServer.isDebugging(): filterStr = self.localsFilterEdit.text() - self.debugServer.remoteClientSetFilter(0, filterStr) + self.debugServer.remoteClientSetFilter( + self.getSelectedDebuggerId(), 0, filterStr) if self.currentStack: self.debugServer.remoteClientVariables( self.getSelectedDebuggerId(), 0, self.localsFilter, @@ -624,8 +613,7 @@ icon = "mediaPlaybackPause" else: icon = "exceptions" - self.__debuggersCombo.setItemIcon(self.__debuggersCombo.currentIndex(), - UI.PixmapCache.getIcon(icon)) + self.__setDebuggerIcon("", icon) def __threadSelected(self, current, previous): """ @@ -693,3 +681,21 @@ @rtype str """ return self.__debuggersCombo.currentText() + + def __setDebuggerIcon(self, debuggerId, iconName): + """ + Private method to set the icon for a specific debugger ID. + + @param debuggerId ID of the debugger backend (empty ID means the + currently selected one) + @type str + @param iconName name of the icon to be used + @type str + """ + if debuggerId: + index = self.__debuggersCombo.findText(debuggerId, Qt.MatchExactly) + else: + index = self.__debuggersCombo.currentIndex() + if index >= 0: + self.__debuggersCombo.setItemIcon( + index, UI.PixmapCache.getIcon(iconName))
--- a/eric6/Debugger/DebuggerInterfacePython.py Sun Feb 02 12:01:27 2020 +0100 +++ b/eric6/Debugger/DebuggerInterfacePython.py Sun Feb 02 16:41:40 2020 +0100 @@ -87,26 +87,32 @@ # attribute to remember the name of the executed script self.__scriptName = "" - + def __identityTranslation(self, fn, remote2local=True): """ Private method to perform the identity path translation. - @param fn filename to be translated (string) + @param fn filename to be translated + @type str @param remote2local flag indicating the direction of translation (False = local to remote, True = remote to local [default]) - @return translated filename (string) + @type bool + @return translated filename + @rtype str """ return fn - + def __remoteTranslation(self, fn, remote2local=True): """ Private method to perform the path translation. - @param fn filename to be translated (string) + @param fn filename to be translated + @type str @param remote2local flag indicating the direction of translation (False = local to remote, True = remote to local [default]) - @return translated filename (string) + @type bool + @return translated filename + @rtype str """ if remote2local: path = fn.replace(self.translateRemote, self.translateLocal) @@ -118,7 +124,7 @@ path = path.replace("\\", "/") return path - + def __startProcess(self, program, arguments, environment=None, workingDir=None): """ @@ -149,7 +155,7 @@ proc = None return proc - + def startRemote(self, port, runInConsole, venvName, originalPathString, workingDir=None): """ @@ -324,7 +330,7 @@ self.__startedVenv = venvName return process, self.__isNetworked, interpreter - + def startRemoteForProject(self, port, runInConsole, venvName, originalPathString, workingDir=None): """ @@ -423,7 +429,7 @@ else: # remote shell command is missing return None, self.__isNetworked, "" - + # set translation function self.translate = self.__identityTranslation @@ -492,7 +498,8 @@ """ Public method to retrieve the debug clients capabilities. - @return debug client capabilities (integer) + @return debug client capabilities + @rtype int """ return self.clientCapabilities @@ -529,7 +536,7 @@ if self.__master is None: self.__master = debuggerId # Get the remote clients capabilities - self.remoteCapabilities() + self.remoteCapabilities(debuggerId) self.debugServer.signalClientDebuggerIds( sorted(self.__connections.keys())) @@ -602,7 +609,7 @@ self.queue = [] self.__master = None - + def __shutdownSocket(self, sock): """ Private slot to shut down a socket. @@ -647,16 +654,22 @@ """ Public method to load a new program to debug. - @param fn the filename to debug (string) - @param argv the commandline arguments to pass to the program (string) - @param wd the working directory for the program (string) - @keyparam traceInterpreter flag indicating if the interpreter library - should be traced as well (boolean) - @keyparam autoContinue flag indicating, that the debugger should not - stop at the first executable line (boolean) - @keyparam autoFork flag indicating the automatic fork mode (boolean) - @keyparam forkChild flag indicating to debug the child after forking - (boolean) + @param fn the filename to debug + @type str + @param argv the commandline arguments to pass to the program + @type str + @param wd the working directory for the program + @type str + @param traceInterpreter flag indicating if the interpreter library + should be traced as well + @type bool + @param autoContinue flag indicating, that the debugger should not + stop at the first executable line + @type bool + @param autoFork flag indicating the automatic fork mode + @type bool + @param forkChild flag indicating to debug the child after forking + @type bool """ self.__autoContinue = autoContinue self.__scriptName = os.path.abspath(fn) @@ -676,12 +689,16 @@ """ Public method to load a new program to run. - @param fn the filename to run (string) - @param argv the commandline arguments to pass to the program (string) - @param wd the working directory for the program (string) - @keyparam autoFork flag indicating the automatic fork mode (boolean) - @keyparam forkChild flag indicating to debug the child after forking - (boolean) + @param fn the filename to run + @type str + @param argv the commandline arguments to pass to the program + @type str + @param wd the working directory for the program + @type str + @param autoFork flag indicating the automatic fork mode + @type bool + @param forkChild flag indicating to debug the child after forking + @type bool """ self.__scriptName = os.path.abspath(fn) @@ -699,11 +716,15 @@ """ Public method to load a new program to collect coverage data. - @param fn the filename to run (string) - @param argv the commandline arguments to pass to the program (string) - @param wd the working directory for the program (string) - @keyparam erase flag indicating that coverage info should be - cleared first (boolean) + @param fn the filename to run + @type str + @param argv the commandline arguments to pass to the program + @type str + @param wd the working directory for the program + @type str + @param erase flag indicating that coverage info should be + cleared first + @type bool """ self.__scriptName = os.path.abspath(fn) @@ -715,16 +736,20 @@ "argv": Utilities.parseOptionString(argv), "erase": erase, }, self.__master) - + def remoteProfile(self, fn, argv, wd, erase=False): """ Public method to load a new program to collect profiling data. - @param fn the filename to run (string) - @param argv the commandline arguments to pass to the program (string) - @param wd the working directory for the program (string) - @keyparam erase flag indicating that timing info should be cleared - first (boolean) + @param fn the filename to run + @type str + @param argv the commandline arguments to pass to the program + @type str + @param wd the working directory for the program + @type str + @param erase flag indicating that timing info should be cleared + first + @type bool """ self.__scriptName = os.path.abspath(fn) @@ -964,16 +989,18 @@ "count": count, }, debuggerId) - # TODO: add debuggerId - 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.__sendJsonCommand("RawInput", { - "input": s, - }) + "input": inputString, + }, debuggerId) def remoteThreadList(self, debuggerId): """ @@ -983,7 +1010,7 @@ @type str """ self.__sendJsonCommand("RequestThreadList", {}, debuggerId) - + def remoteSetThread(self, debuggerId, tid): """ Public method to request to set the given thread as current thread. @@ -1059,19 +1086,21 @@ "maxSize": maxSize, }, debuggerId) - # TODO: add debuggerId - 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.__sendJsonCommand("RequestSetFilter", { "scope": scope, "filter": filterStr, - }) + }, debuggerId) def setCallTraceEnabled(self, debuggerId, on): """ @@ -1106,7 +1135,8 @@ Public slot to get the a list of possible commandline completions from the remote client. - @param text the text to be completed (string) + @param text the text to be completed + @type str """ self.__sendJsonCommand("RequestCompletion", { "text": text, @@ -1210,26 +1240,30 @@ """ self.__sendJsonCommand("RequestUTStop", {}) - def __askForkTo(self): + def __askForkTo(self, debuggerId): """ Private method to ask the user which branch of a fork to follow. + + @param debuggerId ID of the debugger backend + @type str """ selections = [self.tr("Parent Process"), self.tr("Child process")] res, ok = QInputDialog.getItem( None, self.tr("Client forking"), - self.tr("Select the fork branch to follow."), + self.tr("Select the fork branch to follow (Debugger: {0}).") + .format(debuggerId), selections, 0, False) if not ok or res == selections[0]: self.__sendJsonCommand("ResponseForkTo", { "target": "parent", - }) + }, debuggerId) else: self.__sendJsonCommand("ResponseForkTo", { "target": "child", - }) + }, debuggerId) def __parseClientLine(self, sock): """ @@ -1365,30 +1399,27 @@ elif method == "ResponseContinue": self.debugServer.signalClientStatement(True, params["debuggerId"]) - # TODO: add debuggerId elif method == "RequestRaw": self.debugServer.signalClientRawInput( - params["prompt"], params["echo"]) + params["prompt"], params["echo"], params["debuggerId"]) - # TODO: add debuggerId elif method == "ResponseBPConditionError": fn = self.translate(params["filename"], True) self.debugServer.signalClientBreakConditionError( - fn, params["line"]) + fn, params["line"], params["debuggerId"]) - # TODO: add debuggerId elif method == "ResponseClearBreakpoint": fn = self.translate(params["filename"], True) - self.debugServer.signalClientClearBreak(fn, params["line"]) + self.debugServer.signalClientClearBreak( + fn, params["line"], params["debuggerId"]) - # TODO: add debuggerId elif method == "ResponseWatchConditionError": self.debugServer.signalClientWatchConditionError( - params["condition"]) + params["condition"], params["debuggerId"]) - # TODO: add debuggerId elif method == "ResponseClearWatch": - self.debugServer.signalClientClearWatch(params["condition"]) + self.debugServer.signalClientClearWatch( + params["condition"], params["debuggerId"]) elif method == "ResponseException": exctype = params["type"] @@ -1477,9 +1508,8 @@ self.debugServer.clientUtTestSucceededUnexpected( params["testname"], params["id"]) - # TODO: add debuggerId elif method == "RequestForkTo": - self.__askForkTo() + self.__askForkTo(params["debuggerId"]) def __sendJsonCommand(self, command, params, debuggerId="", sock=None): """
--- a/eric6/Debugger/VariablesViewer.py Sun Feb 02 12:01:27 2020 +0100 +++ b/eric6/Debugger/VariablesViewer.py Sun Feb 02 16:41:40 2020 +0100 @@ -792,9 +792,8 @@ # step 4: request the variable from the debugger variablesFilter = e5App().getObject("DebugUI").variablesFilter( self.__globalScope) - # TODO: refactor this statement e5App().getObject("DebugServer").remoteClientVariable( - e5App().getObject("DebugUI").debugViewer.getSelectedDebuggerId(), + e5App().getObject("DebugUI").getSelectedDebuggerId(), self.__globalScope, variablesFilter, pathlist, self.framenr) def setExpanded(self, index, state):
--- a/eric6/QScintilla/Shell.py Sun Feb 02 12:01:27 2020 +0100 +++ b/eric6/QScintilla/Shell.py Sun Feb 02 16:41:40 2020 +0100 @@ -610,8 +610,10 @@ """ self.buff = "" self.inContinue = False - self.inRawMode = False - self.echoInput = True + self.__inRawMode = False + self.__echoInput = True + self.__rawModeDebuggerId = None + self.__rawModeQueue = [] self.clientCapabilities = 0 self.inCommandExecution = False self.interruptCommandExecution = False @@ -865,7 +867,7 @@ @param more flag indicating that more user input is required @type bool """ - if not self.inRawMode: + if not self.__inRawMode: self.inContinue = more self.__writePrompt() self.inCommandExecution = False @@ -993,6 +995,12 @@ QApplication.processEvents() # Finally process the accumulated text + self.__flushQueuedText() + + def __flushQueuedText(self): + """ + Private slot to flush the accumulated text output. + """ self.__write(self.__queuedText) self.__queuedText = '' @@ -1001,7 +1009,7 @@ # little trick to get the cursor position registered within QScintilla self.SendScintilla(QsciScintilla.SCI_CHARLEFT) self.SendScintilla(QsciScintilla.SCI_CHARRIGHT) - + def __write(self, s): """ Private method to display some text without queuing. @@ -1032,30 +1040,41 @@ """ self.__write(self.tr("StdErr: {0}").format(s)) - def __raw_input(self, s, echo): + def __raw_input(self, prompt, echo, debuggerId): """ Private method to handle raw input. - @param s prompt to be displayed (string) - @param echo Flag indicating 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 """ - # Get all text which is still waiting for output - QApplication.processEvents() - - self.setFocus() - self.inRawMode = True - self.echoInput = echo - self.__writeQueued(s) - line, col = self.__getEndPos() - self.setCursorPosition(line, col) - buf = self.text(line) - if buf.startswith(sys.ps1): - buf = buf.replace(sys.ps1, "") - if buf.startswith(sys.ps2): - buf = buf.replace(sys.ps2, "") - self.prompt = buf - # move cursor to end of line - self.moveCursorToEOL() + if self.__inRawMode: + # we are processing another raw input event already + self.__rawModeQueue.append((debuggerId, prompt, echo)) + else: + self.setFocus() + self.__inRawMode = True + self.__echoInput = echo + self.__rawModeDebuggerId = debuggerId + + # Get all text which is still waiting for output + QApplication.processEvents() + self.__flushQueuedText() + + self.__write(self.tr("<{0}> {1}").format(debuggerId, prompt)) + line, col = self.__getEndPos() + self.setCursorPosition(line, col) + buf = self.text(line) + if buf.startswith(sys.ps1): + buf = buf.replace(sys.ps1, "") + if buf.startswith(sys.ps2): + buf = buf.replace(sys.ps2, "") + self.prompt = buf + # move cursor to end of line + self.moveCursorToEOL() def paste(self): """ @@ -1311,7 +1330,7 @@ line, col = self.__getEndPos() self.setCursorPosition(line, col) self.prline, self.prcol = self.getCursorPosition() - if self.echoInput: + if self.__echoInput: ac = self.isListActive() super(Shell, self).keyPressEvent(ev) self.incrementalSearchActive = True @@ -1753,7 +1772,7 @@ @param historyIndex history index to be set @type int """ - if not self.inRawMode: + if not self.__inRawMode: self.inCommandExecution = True self.interruptCommandExecution = False if not cmd: @@ -1853,15 +1872,19 @@ except KeyboardInterrupt: pass else: - if not self.echoInput: + if not self.__echoInput: cmd = self.buff self.buff = "" elif cmd: cmd = cmd[len(self.prompt):] - self.inRawMode = False - self.echoInput = True + self.__inRawMode = False + self.__echoInput = True - self.dbs.remoteRawInput(cmd) + self.dbs.remoteRawInput(self.__rawModeDebuggerId, cmd) + + if self.__rawModeQueue: + debuggerId, prompt, echo = self.__rawModeQueue.pop(0) + self.__raw_input(prompt, echo, debuggerId) def __showVenvName(self): """