diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/Debugger/DebuggerInterfacePython.py --- a/src/eric7/Debugger/DebuggerInterfacePython.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Debugger/DebuggerInterfacePython.py Wed Jul 13 14:55:47 2022 +0200 @@ -13,9 +13,7 @@ import shlex import contextlib -from PyQt6.QtCore import ( - QObject, QProcess, QProcessEnvironment, QTimer -) +from PyQt6.QtCore import QObject, QProcess, QProcessEnvironment, QTimer from EricWidgets.EricApplication import ericApp from EricWidgets import EricMessageBox @@ -37,59 +35,58 @@ Class implementing the debugger interface for the debug server for Python 3. """ + def __init__(self, debugServer, passive): """ Constructor - + @param debugServer reference to the debug server @type DebugServer @param passive flag indicating passive connection mode @type bool """ super().__init__() - + self.__isNetworked = True self.__autoContinue = False self.__autoContinued = [] self.__isStepCommand = False - + self.debugServer = debugServer self.passive = passive self.process = None self.__startedVenv = "" - + self.queue = [] self.__master = None self.__connections = {} self.__pendingConnections = [] self.__inShutdown = False - + # set default values for capabilities of clients self.clientCapabilities = ClientDefaultCapabilities - + # set translation function self.translate = self.__identityTranslation - + if passive: # set translation function if Preferences.getDebugger("PathTranslation"): - self.translateRemote = Preferences.getDebugger( - "PathTranslationRemote") + self.translateRemote = Preferences.getDebugger("PathTranslationRemote") self.translateRemoteWindows = "\\" in self.translateRemote - self.translateLocal = Preferences.getDebugger( - "PathTranslationLocal") + self.translateLocal = Preferences.getDebugger("PathTranslationLocal") self.translateLocalWindows = "\\" in self.translateLocal self.translate = self.__remoteTranslation else: self.translate = self.__identityTranslation - + # 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 @type str @param remote2local flag indicating the direction of translation @@ -99,11 +96,11 @@ @rtype str """ return fn - + def __remoteTranslation(self, fn, remote2local=True): """ Private method to perform the path translation. - + @param fn filename to be translated @type str @param remote2local flag indicating the direction of translation @@ -120,14 +117,13 @@ path = fn.replace(self.translateLocal, self.translateRemote) if not self.translateRemoteWindows: path = path.replace("\\", "/") - + return path - - def __startProcess(self, program, arguments, environment=None, - workingDir=None): + + def __startProcess(self, program, arguments, environment=None, workingDir=None): """ Private method to start the debugger client process. - + @param program name of the executable to start @type str @param arguments arguments to be passed to the program @@ -151,14 +147,21 @@ proc.start(program, args) if not proc.waitForStarted(10000): proc = None - + return proc - - def startRemote(self, port, runInConsole, venvName, originalPathString, - workingDir=None, configOverride=None): + + def startRemote( + self, + port, + runInConsole, + venvName, + originalPathString, + workingDir=None, + configOverride=None, + ): """ Public method to start a remote Python interpreter. - + @param port port number the debug server is listening on @type int @param runInConsole flag indicating to start the debugger in a @@ -178,7 +181,7 @@ @rtype tuple of (QProcess, bool, str) """ global origPathEnv - + if not venvName: venvName = Preferences.getDebugger("Python3VirtualEnv") venvManager = ericApp().getObject("VirtualEnvManager") @@ -191,37 +194,36 @@ EricMessageBox.critical( None, self.tr("Start Debugger"), - self.tr( - """<p>No suitable Python3 environment configured.</p>""") + self.tr("""<p>No suitable Python3 environment configured.</p>"""), ) return None, False, "" - + self.__inShutdown = False - + debugClientType = Preferences.getDebugger("DebugClientType3") if debugClientType == "standard": - debugClient = os.path.join(getConfig('ericDir'), - "DebugClients", "Python", - "DebugClient.py") + debugClient = os.path.join( + getConfig("ericDir"), "DebugClients", "Python", "DebugClient.py" + ) else: debugClient = Preferences.getDebugger("DebugClient3") if debugClient == "": - debugClient = os.path.join(sys.path[0], - "DebugClients", "Python", - "DebugClient.py") - + debugClient = os.path.join( + sys.path[0], "DebugClients", "Python", "DebugClient.py" + ) + redirect = ( str(configOverride["redirect"]) - if configOverride and configOverride["enable"] else - str(Preferences.getDebugger("Python3Redirect")) + if configOverride and configOverride["enable"] + else str(Preferences.getDebugger("Python3Redirect")) ) - noencoding = (Preferences.getDebugger("Python3NoEncoding") and - '--no-encoding' or '') + noencoding = ( + Preferences.getDebugger("Python3NoEncoding") and "--no-encoding" or "" + ) multiprocessEnabled = ( - '--multiprocess' if Preferences.getDebugger("MultiProcessEnabled") - else '' + "--multiprocess" if Preferences.getDebugger("MultiProcessEnabled") else "" ) - + if Preferences.getDebugger("RemoteDbgEnabled"): ipaddr = self.debugServer.getHostAddress(False) rexec = Preferences.getDebugger("RemoteExecution") @@ -230,7 +232,10 @@ rhost = "localhost" if rexec: args = Utilities.parseOptionString(rexec) + [ - rhost, interpreter, debugClient] + rhost, + interpreter, + debugClient, + ] if noencoding: args.append(noencoding) if multiprocessEnabled: @@ -245,32 +250,35 @@ break else: args[0] = Utilities.getExecutablePath(args[0]) - process = self.__startProcess(args[0], args[1:], - workingDir=workingDir) + process = self.__startProcess(args[0], args[1:], workingDir=workingDir) if process is None: EricMessageBox.critical( None, self.tr("Start Debugger"), self.tr( """<p>The debugger backend could not be""" - """ started.</p>""")) - + """ started.</p>""" + ), + ) + # set translation function if Preferences.getDebugger("PathTranslation"): self.translateRemote = Preferences.getDebugger( - "PathTranslationRemote") + "PathTranslationRemote" + ) self.translateRemoteWindows = "\\" in self.translateRemote self.translateLocal = Preferences.getDebugger( - "PathTranslationLocal") + "PathTranslationLocal" + ) self.translate = self.__remoteTranslation self.translateLocalWindows = "\\" in self.translateLocal else: self.translate = self.__identityTranslation return process, self.__isNetworked, "" - + # set translation function self.translate = self.__identityTranslation - + # setup the environment for the debugger if Preferences.getDebugger("DebugEnvironmentReplace"): clientEnv = {} @@ -278,68 +286,78 @@ clientEnv = os.environ.copy() if originalPathString: clientEnv["PATH"] = originalPathString - envlist = shlex.split( - Preferences.getDebugger("DebugEnvironment")) + envlist = shlex.split(Preferences.getDebugger("DebugEnvironment")) for el in envlist: with contextlib.suppress(ValueError): - key, value = el.split('=', 1) + key, value = el.split("=", 1) clientEnv[str(key)] = str(value) if execPath: if "PATH" in clientEnv: - clientEnv["PATH"] = os.pathsep.join( - [execPath, clientEnv["PATH"]]) + clientEnv["PATH"] = os.pathsep.join([execPath, clientEnv["PATH"]]) else: clientEnv["PATH"] = execPath - + ipaddr = self.debugServer.getHostAddress(True) if runInConsole or Preferences.getDebugger("ConsoleDbgEnabled"): ccmd = Preferences.getDebugger("ConsoleDbgCommand") if ccmd: args = Utilities.parseOptionString(ccmd) + [ - interpreter, os.path.abspath(debugClient)] + interpreter, + os.path.abspath(debugClient), + ] if noencoding: args.append(noencoding) if multiprocessEnabled: args.append(multiprocessEnabled) - args.extend([str(port), '0', ipaddr]) + args.extend([str(port), "0", ipaddr]) args[0] = Utilities.getExecutablePath(args[0]) - process = self.__startProcess(args[0], args[1:], clientEnv, - workingDir=workingDir) + process = self.__startProcess( + args[0], args[1:], clientEnv, workingDir=workingDir + ) if process is None: EricMessageBox.critical( None, self.tr("Start Debugger"), self.tr( """<p>The debugger backend could not be""" - """ started.</p>""")) + """ started.</p>""" + ), + ) return process, self.__isNetworked, interpreter - + args = [debugClient] if noencoding: args.append(noencoding) if multiprocessEnabled: args.append(multiprocessEnabled) args.extend([str(port), redirect, ipaddr]) - process = self.__startProcess(interpreter, args, clientEnv, - workingDir=workingDir) + process = self.__startProcess( + interpreter, args, clientEnv, workingDir=workingDir + ) if process is None: self.__startedVenv = "" EricMessageBox.critical( None, self.tr("Start Debugger"), - self.tr( - """<p>The debugger backend could not be started.</p>""")) + self.tr("""<p>The debugger backend could not be started.</p>"""), + ) else: self.__startedVenv = venvName - + return process, self.__isNetworked, interpreter - - def startRemoteForProject(self, port, runInConsole, venvName, - originalPathString, workingDir=None, - configOverride=None): + + def startRemoteForProject( + self, + port, + runInConsole, + venvName, + originalPathString, + workingDir=None, + configOverride=None, + ): """ Public method to start a remote Python interpreter for a project. - + @param port port number the debug server is listening on @type int @param runInConsole flag indicating to start the debugger in a @@ -359,27 +377,24 @@ @rtype tuple of (QProcess, bool, str) """ global origPathEnv - + project = ericApp().getObject("Project") if not project.isDebugPropertiesLoaded(): return None, self.__isNetworked, "" - + # start debugger with project specific settings debugClient = project.getDebugProperty("DEBUGCLIENT") - + redirect = ( str(configOverride["redirect"]) - if configOverride and configOverride["enable"] else - str(project.getDebugProperty("REDIRECT")) - ) - noencoding = ( - '--no-encoding' if project.getDebugProperty("NOENCODING") else '' + if configOverride and configOverride["enable"] + else str(project.getDebugProperty("REDIRECT")) ) + noencoding = "--no-encoding" if project.getDebugProperty("NOENCODING") else "" multiprocessEnabled = ( - '--multiprocess' if Preferences.getDebugger("MultiProcessEnabled") - else '' + "--multiprocess" if Preferences.getDebugger("MultiProcessEnabled") else "" ) - + if venvName: venvManager = ericApp().getObject("VirtualEnvManager") interpreter = venvManager.getVirtualenvInterpreter(venvName) @@ -392,13 +407,12 @@ EricMessageBox.critical( None, self.tr("Start Debugger"), - self.tr( - """<p>No suitable Python3 environment configured.</p>""") + self.tr("""<p>No suitable Python3 environment configured.</p>"""), ) return None, self.__isNetworked, "" - + self.__inShutdown = False - + if project.getDebugProperty("REMOTEDEBUGGER"): ipaddr = self.debugServer.getHostAddress(False) rexec = project.getDebugProperty("REMOTECOMMAND") @@ -407,7 +421,10 @@ rhost = "localhost" if rexec: args = Utilities.parseOptionString(rexec) + [ - rhost, interpreter, debugClient] + rhost, + interpreter, + debugClient, + ] if noencoding: args.append(noencoding) if multiprocessEnabled: @@ -422,19 +439,19 @@ break else: args[0] = Utilities.getExecutablePath(args[0]) - process = self.__startProcess(args[0], args[1:], - workingDir=workingDir) + process = self.__startProcess(args[0], args[1:], workingDir=workingDir) if process is None: EricMessageBox.critical( None, self.tr("Start Debugger"), self.tr( """<p>The debugger backend could not be""" - """ started.</p>""")) + """ started.</p>""" + ), + ) # set translation function if project.getDebugProperty("PATHTRANSLATION"): - self.translateRemote = project.getDebugProperty( - "REMOTEPATH") + self.translateRemote = project.getDebugProperty("REMOTEPATH") self.translateRemoteWindows = "\\" in self.translateRemote self.translateLocal = project.getDebugProperty("LOCALPATH") self.translateLocalWindows = "\\" in self.translateLocal @@ -445,10 +462,10 @@ else: # remote shell command is missing return None, self.__isNetworked, "" - + # set translation function self.translate = self.__identityTranslation - + # setup the environment for the debugger if project.getDebugProperty("ENVIRONMENTOVERRIDE"): clientEnv = {} @@ -456,93 +473,98 @@ clientEnv = os.environ.copy() if originalPathString: clientEnv["PATH"] = originalPathString - envlist = shlex.split( - project.getDebugProperty("ENVIRONMENTSTRING")) + envlist = shlex.split(project.getDebugProperty("ENVIRONMENTSTRING")) for el in envlist: with contextlib.suppress(ValueError): - key, value = el.split('=', 1) + key, value = el.split("=", 1) clientEnv[str(key)] = str(value) if execPath: if "PATH" in clientEnv: - clientEnv["PATH"] = os.pathsep.join( - [execPath, clientEnv["PATH"]]) + clientEnv["PATH"] = os.pathsep.join([execPath, clientEnv["PATH"]]) else: clientEnv["PATH"] = execPath - + ipaddr = self.debugServer.getHostAddress(True) if runInConsole or project.getDebugProperty("CONSOLEDEBUGGER"): - ccmd = (project.getDebugProperty("CONSOLECOMMAND") or - Preferences.getDebugger("ConsoleDbgCommand")) + ccmd = project.getDebugProperty( + "CONSOLECOMMAND" + ) or Preferences.getDebugger("ConsoleDbgCommand") if ccmd: args = Utilities.parseOptionString(ccmd) + [ - interpreter, os.path.abspath(debugClient)] + interpreter, + os.path.abspath(debugClient), + ] if noencoding: args.append(noencoding) if multiprocessEnabled: args.append(multiprocessEnabled) - args.extend([str(port), '0', ipaddr]) + args.extend([str(port), "0", ipaddr]) args[0] = Utilities.getExecutablePath(args[0]) - process = self.__startProcess(args[0], args[1:], clientEnv, - workingDir=workingDir) + process = self.__startProcess( + args[0], args[1:], clientEnv, workingDir=workingDir + ) if process is None: EricMessageBox.critical( None, self.tr("Start Debugger"), self.tr( """<p>The debugger backend could not be""" - """ started.</p>""")) + """ started.</p>""" + ), + ) return process, self.__isNetworked, interpreter - + args = [debugClient] if noencoding: args.append(noencoding) if multiprocessEnabled: args.append(multiprocessEnabled) args.extend([str(port), redirect, ipaddr]) - process = self.__startProcess(interpreter, args, clientEnv, - workingDir=workingDir) + process = self.__startProcess( + interpreter, args, clientEnv, workingDir=workingDir + ) if process is None: self.__startedVenv = "" EricMessageBox.critical( None, self.tr("Start Debugger"), - self.tr( - """<p>The debugger backend could not be started.</p>""")) + self.tr("""<p>The debugger backend could not be started.</p>"""), + ) else: self.__startedVenv = venvName - + return process, self.__isNetworked, interpreter - + def getClientCapabilities(self): """ Public method to retrieve the debug clients capabilities. - + @return debug client capabilities @rtype int """ return self.clientCapabilities - + def newConnection(self, sock): """ Public slot to handle a new connection. - + @param sock reference to the socket object @type QTcpSocket @return flag indicating success @rtype bool """ self.__pendingConnections.append(sock) - + sock.readyRead.connect(lambda: self.__parseClientLine(sock)) sock.disconnected.connect(lambda: self.__socketDisconnected(sock)) - + return True - + def __assignDebuggerId(self, sock, debuggerId): """ Private method to set the debugger id for a recent debugger connection attempt. - + @param sock reference to the socket object @type QTcpSocket @param debuggerId id of the connected debug client @@ -551,34 +573,33 @@ if sock in self.__pendingConnections: self.__connections[debuggerId] = sock self.__pendingConnections.remove(sock) - + if self.__master is None: self.__master = debuggerId # Get the remote clients capabilities self.remoteCapabilities(debuggerId) - + self.debugServer.signalClientDebuggerId(debuggerId) - + if debuggerId == self.__master: self.__flush() self.debugServer.masterClientConnected() - + self.debugServer.initializeClient(debuggerId) - + # perform auto-continue except for master if ( - debuggerId != self.__master and - self.__autoContinue and - not self.__isStepCommand + debuggerId != self.__master + and self.__autoContinue + and not self.__isStepCommand ): self.__autoContinued.append(debuggerId) - QTimer.singleShot( - 0, lambda: self.remoteContinue(debuggerId)) - + QTimer.singleShot(0, lambda: self.remoteContinue(debuggerId)) + def __socketDisconnected(self, sock): """ Private slot handling a socket disconnecting. - + @param sock reference to the disconnected socket @type QTcpSocket """ @@ -597,7 +618,7 @@ else: if sock in self.__pendingConnections: self.__pendingConnections.remove(sock) - + if not self.__connections: # no active connections anymore with contextlib.suppress(RuntimeError): @@ -606,16 +627,16 @@ # ignore this self.__autoContinued.clear() self.debugServer.startClient() - + 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 """ return sorted(self.__connections.keys()) - + def __flush(self): """ Private slot to flush the queue. @@ -623,40 +644,39 @@ if self.__master: # Send commands that were waiting for the connection. for cmd in self.queue: - self.__writeJsonCommandToSocket( - cmd, self.__connections[self.__master]) - + self.__writeJsonCommandToSocket(cmd, self.__connections[self.__master]) + self.queue = [] - + def shutdown(self): """ Public method to cleanly shut down. - + It closes our sockets and shuts down the debug clients. (Needed on Win OS) """ if not self.__master: return - + self.__inShutdown = True - + while self.__connections: debuggerId, sock = self.__connections.popitem() self.__shutdownSocket(sock) - + while self.__pendingConnections: sock = self.__pendingConnections.pop() self.__shutdownSocket(sock) - + # reinitialize self.queue = [] - + self.__master = None - + def __shutdownSocket(self, sock): """ Private slot to shut down a socket. - + @param sock reference to the socket @type QTcpSocket """ @@ -668,35 +688,43 @@ self.__sendJsonCommand("RequestShutdown", {}, sock=sock) sock.flush() sock.close() - + sock.setParent(None) sock.deleteLater() del sock - + def isConnected(self): """ Public method to test, if a debug client has connected. - + @return flag indicating the connection status @rtype bool """ return bool(self.__connections) - + def remoteEnvironment(self, env): """ Public method to set the environment for a program to debug, run, ... - + @param env environment settings @type dict """ - self.__sendJsonCommand("RequestEnvironment", {"environment": env}, - self.__master) - - def remoteLoad(self, fn, argv, wd, traceInterpreter=False, - autoContinue=True, enableMultiprocess=False): + self.__sendJsonCommand( + "RequestEnvironment", {"environment": env}, self.__master + ) + + def remoteLoad( + self, + fn, + argv, + wd, + traceInterpreter=False, + autoContinue=True, + enableMultiprocess=False, + ): """ Public method to load a new program to debug. - + @param fn the filename to debug @type str @param argv the commandline arguments to pass to the program @@ -716,21 +744,25 @@ self.__autoContinue = autoContinue self.__scriptName = os.path.abspath(fn) self.__isStepCommand = False - + wd = self.translate(wd, False) fn = self.translate(os.path.abspath(fn), False) - self.__sendJsonCommand("RequestLoad", { - "workdir": wd, - "filename": fn, - "argv": Utilities.parseOptionString(argv), - "traceInterpreter": traceInterpreter, - "multiprocess": enableMultiprocess, - }, self.__master) - + self.__sendJsonCommand( + "RequestLoad", + { + "workdir": wd, + "filename": fn, + "argv": Utilities.parseOptionString(argv), + "traceInterpreter": traceInterpreter, + "multiprocess": enableMultiprocess, + }, + self.__master, + ) + def remoteRun(self, fn, argv, wd): """ Public method to load a new program to run. - + @param fn the filename to run @type str @param argv the commandline arguments to pass to the program @@ -739,19 +771,23 @@ @type str """ self.__scriptName = os.path.abspath(fn) - + wd = self.translate(wd, False) fn = self.translate(os.path.abspath(fn), False) - self.__sendJsonCommand("RequestRun", { - "workdir": wd, - "filename": fn, - "argv": Utilities.parseOptionString(argv), - }, self.__master) - + self.__sendJsonCommand( + "RequestRun", + { + "workdir": wd, + "filename": fn, + "argv": Utilities.parseOptionString(argv), + }, + self.__master, + ) + def remoteCoverage(self, fn, argv, wd, erase=False): """ Public method to load a new program to collect coverage data. - + @param fn the filename to run @type str @param argv the commandline arguments to pass to the program @@ -763,20 +799,24 @@ @type bool """ self.__scriptName = os.path.abspath(fn) - + wd = self.translate(wd, False) fn = self.translate(os.path.abspath(fn), False) - self.__sendJsonCommand("RequestCoverage", { - "workdir": wd, - "filename": fn, - "argv": Utilities.parseOptionString(argv), - "erase": erase, - }, self.__master) - + self.__sendJsonCommand( + "RequestCoverage", + { + "workdir": wd, + "filename": fn, + "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 @type str @param argv the commandline arguments to pass to the program @@ -788,116 +828,137 @@ @type bool """ self.__scriptName = os.path.abspath(fn) - + wd = self.translate(wd, False) fn = self.translate(os.path.abspath(fn), False) - self.__sendJsonCommand("RequestProfile", { - "workdir": wd, - "filename": fn, - "argv": Utilities.parseOptionString(argv), - "erase": erase, - }, self.__master) - + self.__sendJsonCommand( + "RequestProfile", + { + "workdir": wd, + "filename": fn, + "argv": Utilities.parseOptionString(argv), + "erase": erase, + }, + self.__master, + ) + def remoteStatement(self, debuggerId, stmt): """ Public method to execute a Python statement. - + @param debuggerId ID of the debugger backend @type str @param stmt the Python statement to execute. @type str """ - self.__sendJsonCommand("ExecuteStatement", { - "statement": stmt, - }, debuggerId) - + self.__sendJsonCommand( + "ExecuteStatement", + { + "statement": stmt, + }, + debuggerId, + ) + def remoteStep(self, debuggerId): """ Public method to single step the debugged program. - + @param debuggerId ID of the debugger backend @type str """ self.__isStepCommand = True self.__sendJsonCommand("RequestStep", {}, debuggerId) - + def remoteStepOver(self, debuggerId): """ Public method to step over the debugged program. - + @param debuggerId ID of the debugger backend @type str """ self.__isStepCommand = True self.__sendJsonCommand("RequestStepOver", {}, debuggerId) - + def remoteStepOut(self, debuggerId): """ Public method to step out the debugged program. - + @param debuggerId ID of the debugger backend @type str """ self.__isStepCommand = True self.__sendJsonCommand("RequestStepOut", {}, debuggerId) - + def remoteStepQuit(self, debuggerId): """ Public method to stop the debugged program. - + @param debuggerId ID of the debugger backend @type str """ self.__isStepCommand = True self.__sendJsonCommand("RequestStepQuit", {}, 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 @type bool """ self.__isStepCommand = False - self.__sendJsonCommand("RequestContinue", { - "special": special, - }, debuggerId) - + self.__sendJsonCommand( + "RequestContinue", + { + "special": special, + }, + debuggerId, + ) + 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.__isStepCommand = False - self.__sendJsonCommand("RequestContinueUntil", { - "newLine": line, - }, debuggerId) - + self.__sendJsonCommand( + "RequestContinueUntil", + { + "newLine": line, + }, + debuggerId, + ) + 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.__sendJsonCommand("RequestMoveIP", { - "newLine": line, - }, debuggerId) - - def remoteBreakpoint(self, debuggerId, fn, line, setBreakpoint, cond=None, - temp=False): + self.__sendJsonCommand( + "RequestMoveIP", + { + "newLine": line, + }, + debuggerId, + ) + + def remoteBreakpoint( + self, debuggerId, fn, line, setBreakpoint, cond=None, temp=False + ): """ Public method to set or clear a breakpoint. - + @param debuggerId ID of the debugger backend @type str @param fn filename the breakpoint belongs to @@ -911,21 +972,24 @@ @param temp flag indicating a temporary breakpoint @type bool """ - debuggerList = ([debuggerId] if debuggerId - else list(self.__connections.keys())) + debuggerList = [debuggerId] if debuggerId else list(self.__connections.keys()) for debuggerId in debuggerList: - self.__sendJsonCommand("RequestBreakpoint", { - "filename": self.translate(fn, False), - "line": line, - "temporary": temp, - "setBreakpoint": setBreakpoint, - "condition": cond, - }, debuggerId) - + self.__sendJsonCommand( + "RequestBreakpoint", + { + "filename": self.translate(fn, False), + "line": line, + "temporary": temp, + "setBreakpoint": setBreakpoint, + "condition": cond, + }, + debuggerId, + ) + def remoteBreakpointEnable(self, debuggerId, fn, line, enable): """ Public method to enable or disable a breakpoint. - + @param debuggerId ID of the debugger backend @type str @param fn filename the breakpoint belongs to @@ -935,19 +999,22 @@ @param enable flag indicating enabling or disabling a breakpoint @type bool """ - debuggerList = ([debuggerId] if debuggerId - else list(self.__connections.keys())) + debuggerList = [debuggerId] if debuggerId else list(self.__connections.keys()) for debuggerId in debuggerList: - self.__sendJsonCommand("RequestBreakpointEnable", { - "filename": self.translate(fn, False), - "line": line, - "enable": enable, - }, debuggerId) - + self.__sendJsonCommand( + "RequestBreakpointEnable", + { + "filename": self.translate(fn, False), + "line": line, + "enable": enable, + }, + debuggerId, + ) + def remoteBreakpointIgnore(self, debuggerId, fn, line, count): """ Public method to ignore a breakpoint the next couple of occurrences. - + @param debuggerId ID of the debugger backend @type str @param fn filename the breakpoint belongs to @@ -957,19 +1024,22 @@ @param count number of occurrences to ignore @type int """ - debuggerList = ([debuggerId] if debuggerId - else list(self.__connections.keys())) + debuggerList = [debuggerId] if debuggerId else list(self.__connections.keys()) for debuggerId in debuggerList: - self.__sendJsonCommand("RequestBreakpointIgnore", { - "filename": self.translate(fn, False), - "line": line, - "count": count, - }, debuggerId) - + self.__sendJsonCommand( + "RequestBreakpointIgnore", + { + "filename": self.translate(fn, False), + "line": line, + "count": count, + }, + debuggerId, + ) + def remoteWatchpoint(self, debuggerId, cond, setWatch, temp=False): """ Public method to set or clear a watch expression. - + @param debuggerId ID of the debugger backend @type str @param cond expression of the watch expression @@ -979,21 +1049,24 @@ @param temp flag indicating a temporary watch expression @type bool """ - debuggerList = ([debuggerId] if debuggerId - else list(self.__connections.keys())) + debuggerList = [debuggerId] if debuggerId else list(self.__connections.keys()) for debuggerId in debuggerList: # cond is combination of cond and special (s. watch expression # viewer) - self.__sendJsonCommand("RequestWatch", { - "temporary": temp, - "setWatch": setWatch, - "condition": cond, - }, debuggerId) - + self.__sendJsonCommand( + "RequestWatch", + { + "temporary": temp, + "setWatch": setWatch, + "condition": cond, + }, + debuggerId, + ) + def remoteWatchpointEnable(self, debuggerId, cond, enable): """ Public method to enable or disable a watch expression. - + @param debuggerId ID of the debugger backend @type str @param cond expression of the watch expression @@ -1001,21 +1074,24 @@ @param enable flag indicating enabling or disabling a watch expression @type bool """ - debuggerList = ([debuggerId] if debuggerId - else list(self.__connections.keys())) + debuggerList = [debuggerId] if debuggerId else list(self.__connections.keys()) for debuggerId in debuggerList: # cond is combination of cond and special (s. watch expression # viewer) - self.__sendJsonCommand("RequestWatchEnable", { - "condition": cond, - "enable": enable, - }, debuggerId) - + self.__sendJsonCommand( + "RequestWatchEnable", + { + "condition": cond, + "enable": enable, + }, + debuggerId, + ) + def remoteWatchpointIgnore(self, debuggerId, cond, count): """ Public method to ignore a watch expression the next couple of occurrences. - + @param debuggerId ID of the debugger backend @type str @param cond expression of the watch expression @@ -1023,65 +1099,77 @@ @param count number of occurrences to ignore @type int """ - debuggerList = ([debuggerId] if debuggerId - else list(self.__connections.keys())) + debuggerList = [debuggerId] if debuggerId else list(self.__connections.keys()) for debuggerId in debuggerList: # cond is combination of cond and special (s. watch expression # viewer) - self.__sendJsonCommand("RequestWatchIgnore", { - "condition": cond, - "count": count, - }, debuggerId) - + self.__sendJsonCommand( + "RequestWatchIgnore", + { + "condition": cond, + "count": count, + }, + debuggerId, + ) + def remoteRawInput(self, debuggerId, inputString): """ Public method to send the raw input to the debugged program. - + @param debuggerId ID of the debugger backend @type str @param inputString the raw input @type str """ - self.__sendJsonCommand("RawInput", { - "input": inputString, - }, debuggerId) - + self.__sendJsonCommand( + "RawInput", + { + "input": inputString, + }, + 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.__sendJsonCommand("RequestThreadList", {}, debuggerId) - + def remoteSetThread(self, debuggerId, tid): """ Public method to request to set the given thread as current thread. - + @param debuggerId ID of the debugger backend @type str @param tid id of the thread @type int """ - self.__sendJsonCommand("RequestThreadSet", { - "threadID": tid, - }, debuggerId) - + self.__sendJsonCommand( + "RequestThreadSet", + { + "threadID": tid, + }, + 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.__sendJsonCommand("RequestStack", {}, debuggerId) - - def remoteClientVariables(self, debuggerId, scope, filterList, framenr=0, - maxSize=0): + + def remoteClientVariables( + self, debuggerId, scope, filterList, 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) @@ -1095,18 +1183,23 @@ be given (@@TOO_BIG_TO_SHOW@@). @type int """ - self.__sendJsonCommand("RequestVariables", { - "frameNumber": framenr, - "scope": scope, - "filters": filterList, - "maxSize": maxSize, - }, debuggerId) - - def remoteClientVariable(self, debuggerId, scope, filterList, var, - framenr=0, maxSize=0): + self.__sendJsonCommand( + "RequestVariables", + { + "frameNumber": framenr, + "scope": scope, + "filters": filterList, + "maxSize": maxSize, + }, + debuggerId, + ) + + 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) @@ -1122,27 +1215,31 @@ be given (@@TOO_BIG_TO_SHOW@@). @type int """ - self.__sendJsonCommand("RequestVariable", { - "variable": var, - "frameNumber": framenr, - "scope": scope, - "filters": filterList, - "maxSize": maxSize, - }, debuggerId) - + self.__sendJsonCommand( + "RequestVariable", + { + "variable": var, + "frameNumber": framenr, + "scope": scope, + "filters": filterList, + "maxSize": maxSize, + }, + debuggerId, + ) + def remoteClientDisassembly(self, debuggerId): """ Public method to ask the client for the latest traceback disassembly. - + @param debuggerId ID of the debugger backend @type str """ self.__sendJsonCommand("RequestDisassembly", {}, debuggerId) - + 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) @@ -1150,91 +1247,106 @@ @param filterStr regexp string for variable names to filter out @type str """ - self.__sendJsonCommand("RequestSetFilter", { - "scope": scope, - "filter": filterStr, - }, debuggerId) - + self.__sendJsonCommand( + "RequestSetFilter", + { + "scope": scope, + "filter": filterStr, + }, + debuggerId, + ) + def setCallTraceEnabled(self, debuggerId, on): """ Public method to set the call trace state. - + @param debuggerId ID of the debugger backend @type str @param on flag indicating to enable the call trace function @type bool """ - self.__sendJsonCommand("RequestCallTrace", { - "enable": on, - }, debuggerId) - + self.__sendJsonCommand( + "RequestCallTrace", + { + "enable": on, + }, + debuggerId, + ) + def remoteNoDebugList(self, debuggerId, noDebugList): """ Public method to set a list of programs not to be debugged. - + The programs given in the list will not be run under the control of the multi process debugger. - + @param debuggerId ID of the debugger backend @type str @param noDebugList list of Python programs not to be debugged @type list of str """ - self.__sendJsonCommand("RequestSetNoDebugList", { - "noDebug": noDebugList, - }, debuggerId) - + self.__sendJsonCommand( + "RequestSetNoDebugList", + { + "noDebug": noDebugList, + }, + debuggerId, + ) + def remoteBanner(self): """ Public slot to get the banner info of the remote client. """ self.__sendJsonCommand("RequestBanner", {}) - + def remoteCapabilities(self, debuggerId): """ Public slot to get the debug clients capabilities. - + @param debuggerId ID of the debugger backend @type str """ self.__sendJsonCommand("RequestCapabilities", {}, debuggerId) - + def remoteCompletion(self, debuggerId, text): """ Public slot to get the a list of possible commandline completions from the remote client. - + @param debuggerId ID of the debugger backend @type str @param text the text to be completed @type str """ - self.__sendJsonCommand("RequestCompletion", { - "text": text, - }, debuggerId) - + self.__sendJsonCommand( + "RequestCompletion", + { + "text": text, + }, + debuggerId, + ) + def __parseClientLine(self, sock): """ Private method to handle data from the client. - + @param sock reference to the socket to read data from @type QTcpSocket """ while sock and sock.canReadLine(): qs = sock.readLine() - line = bytes(qs).decode( - encoding=Preferences.getSystem("StringEncoding")) - + line = bytes(qs).decode(encoding=Preferences.getSystem("StringEncoding")) + logging.debug("<Debug-Server> %s", line) -## print("Server: ", line) ## debug # __IGNORE_WARNING_M891__ - + ##print("Server: ", line) ## debug # __IGNORE_WARNING_M891__ + self.__handleJsonCommand(line, sock) - + def __handleJsonCommand(self, jsonStr, sock): """ Private method to handle a command or response serialized as a JSON string. - + @param jsonStr string containing the command or response received from the debug backend @type str @@ -1242,33 +1354,34 @@ @type QTcpSocket """ import json - + try: commandDict = json.loads(jsonStr.strip()) except (TypeError, ValueError) as err: EricMessageBox.critical( None, self.tr("Debug Protocol Error"), - self.tr("""<p>The response received from the debugger""" - """ backend could not be decoded. Please report""" - """ this issue with the received data to the""" - """ eric bugs email address.</p>""" - """<p>Error: {0}</p>""" - """<p>Data:<br/>{1}</p>""").format( - str(err), Utilities.html_encode(jsonStr.strip())), - EricMessageBox.Ok) + self.tr( + """<p>The response received from the debugger""" + """ backend could not be decoded. Please report""" + """ this issue with the received data to the""" + """ eric bugs email address.</p>""" + """<p>Error: {0}</p>""" + """<p>Data:<br/>{1}</p>""" + ).format(str(err), Utilities.html_encode(jsonStr.strip())), + EricMessageBox.Ok, + ) return - + method = commandDict["method"] params = commandDict["params"] - + if method == "DebuggerId": self.__assignDebuggerId(sock, params["debuggerId"]) - + elif method == "ClientOutput": - self.debugServer.signalClientOutput( - params["text"], params["debuggerId"]) - + self.debugServer.signalClientOutput(params["text"], params["debuggerId"]) + elif method in ["ResponseLine", "ResponseStack"]: # Check if obsolete thread was clicked if params["stack"] == []: @@ -1278,50 +1391,58 @@ for s in params["stack"]: s[0] = self.translate(s[0], True) cf = params["stack"][0] - if ( - self.__autoContinue and - params["debuggerId"] not in self.__autoContinued - ): + if self.__autoContinue and params["debuggerId"] not in self.__autoContinued: self.__autoContinued.append(params["debuggerId"]) - QTimer.singleShot( - 0, lambda: self.remoteContinue(params["debuggerId"])) + QTimer.singleShot(0, lambda: self.remoteContinue(params["debuggerId"])) else: self.debugServer.signalClientLine( - cf[0], int(cf[1]), params["debuggerId"], - method == "ResponseStack", threadName=params["threadName"]) + cf[0], + int(cf[1]), + params["debuggerId"], + method == "ResponseStack", + threadName=params["threadName"], + ) self.debugServer.signalClientStack( - params["stack"], params["debuggerId"], - threadName=params["threadName"]) - + params["stack"], + params["debuggerId"], + threadName=params["threadName"], + ) + elif method == "CallTrace": isCall = params["event"].lower() == "c" fromInfo = params["from"] toInfo = params["to"] self.debugServer.signalClientCallTrace( isCall, - fromInfo["filename"], str(fromInfo["linenumber"]), + fromInfo["filename"], + str(fromInfo["linenumber"]), fromInfo["codename"], - toInfo["filename"], str(toInfo["linenumber"]), + toInfo["filename"], + str(toInfo["linenumber"]), toInfo["codename"], - params["debuggerId"]) - + params["debuggerId"], + ) + elif method == "ResponseVariables": self.debugServer.signalClientVariables( - params["scope"], params["variables"], params["debuggerId"]) - + params["scope"], params["variables"], params["debuggerId"] + ) + elif method == "ResponseVariable": self.debugServer.signalClientVariable( - params["scope"], [params["variable"]] + params["variables"], - params["debuggerId"]) - + params["scope"], + [params["variable"]] + params["variables"], + params["debuggerId"], + ) + elif method == "ResponseThreadList": self.debugServer.signalClientThreadList( - params["currentID"], params["threadList"], - params["debuggerId"]) - + params["currentID"], params["threadList"], params["debuggerId"] + ) + elif method == "ResponseThreadSet": self.debugServer.signalClientThreadSet(params["debuggerId"]) - + elif method == "ResponseCapabilities": self.clientCapabilities = params["capabilities"] if params["debuggerId"] == self.__master: @@ -1331,7 +1452,7 @@ params["clientType"], self.__startedVenv, ) - + elif method == "ResponseBanner": if params["debuggerId"] == self.__master: # signal only for the master connection @@ -1340,39 +1461,45 @@ params["platform"], self.__startedVenv, ) - + elif method == "ResponseOK": self.debugServer.signalClientStatement(False, params["debuggerId"]) - + elif method == "ResponseContinue": self.debugServer.signalClientStatement(True, params["debuggerId"]) - + elif method == "RequestRaw": self.debugServer.signalClientRawInput( - params["prompt"], params["echo"], params["debuggerId"]) - + params["prompt"], params["echo"], params["debuggerId"] + ) + elif method == "ResponseBPConditionError": fn = self.translate(params["filename"], True) self.debugServer.signalClientBreakConditionError( - fn, params["line"], params["debuggerId"]) - + fn, params["line"], params["debuggerId"] + ) + elif method == "ResponseClearBreakpoint": fn = self.translate(params["filename"], True) self.debugServer.signalClientClearBreak( - fn, params["line"], params["debuggerId"]) - + fn, params["line"], params["debuggerId"] + ) + elif method == "ResponseWatchConditionError": self.debugServer.signalClientWatchConditionError( - params["condition"], params["debuggerId"]) - + params["condition"], params["debuggerId"] + ) + elif method == "ResponseClearWatch": self.debugServer.signalClientClearWatch( - params["condition"], params["debuggerId"]) - + params["condition"], params["debuggerId"] + ) + elif method == "ResponseDisassembly": self.debugServer.signalClientDisassembly( - params["disassembly"], params["debuggerId"]) - + params["disassembly"], params["debuggerId"] + ) + elif method == "ResponseException": exctype = params["type"] excmessage = params["message"] @@ -1386,44 +1513,58 @@ stackEntry[0] = self.__scriptName else: break - + self.debugServer.signalClientException( - exctype, excmessage, stack, params["debuggerId"], - params["threadName"]) - + exctype, excmessage, stack, params["debuggerId"], params["threadName"] + ) + elif method == "ResponseSyntax": self.debugServer.signalClientSyntaxError( - params["message"], self.translate(params["filename"], True), - params["linenumber"], params["characternumber"], - params["debuggerId"], params["threadName"]) - + params["message"], + self.translate(params["filename"], True), + params["linenumber"], + params["characternumber"], + params["debuggerId"], + params["threadName"], + ) + elif method == "ResponseSignal": self.debugServer.signalClientSignal( - params["message"], self.translate(params["filename"], True), - params["linenumber"], params["function"], params["arguments"], - params["debuggerId"]) - + params["message"], + self.translate(params["filename"], True), + params["linenumber"], + params["function"], + params["arguments"], + params["debuggerId"], + ) + elif method == "ResponseExit": self.__scriptName = "" self.debugServer.signalClientExit( - params["program"], params["status"], params["message"], - params["debuggerId"]) + params["program"], + params["status"], + params["message"], + params["debuggerId"], + ) if params["debuggerId"] == self.__master: self.debugServer.signalMainClientExit() - + elif method == "PassiveStartup": self.debugServer.passiveStartUp( - self.translate(params["filename"], True), params["exceptions"], - params["debuggerId"]) - + self.translate(params["filename"], True), + params["exceptions"], + params["debuggerId"], + ) + elif method == "ResponseCompletion": self.debugServer.signalClientCompletionList( - params["completions"], params["text"], params["debuggerId"]) - + params["completions"], params["text"], params["debuggerId"] + ) + def __sendJsonCommand(self, command, params, debuggerId="", sock=None): """ Private method to send a single command to the client. - + @param command command name to be sent @type str @param params dictionary of named parameters for the command @@ -1435,14 +1576,14 @@ @type QTcpSocket """ import json - + commandDict = { "jsonrpc": "2.0", "method": command, "params": params, } - cmd = json.dumps(commandDict) + '\n' - + cmd = json.dumps(commandDict) + "\n" + if debuggerId and debuggerId in self.__connections: sock = self.__connections[debuggerId] elif sock is None and self.__master is not None: @@ -1451,17 +1592,17 @@ self.__writeJsonCommandToSocket(cmd, sock) else: self.queue.append(cmd) - + def __writeJsonCommandToSocket(self, cmd, sock): """ Private method to write a JSON command to the socket. - + @param cmd JSON command to be sent @type str @param sock reference to the socket to write to @type QTcpSocket """ - data = cmd.encode('utf8', 'backslashreplace') + data = cmd.encode("utf8", "backslashreplace") length = "{0:09d}".format(len(data)) sock.write(length.encode() + data) sock.flush() @@ -1470,8 +1611,8 @@ def createDebuggerInterfacePython3(debugServer, passive): """ Module function to create a debugger interface instance. - - + + @param debugServer reference to the debug server @type DebugServer @param passive flag indicating passive connection mode @@ -1486,7 +1627,7 @@ """ Module function to get characterizing data for the supported debugger interfaces. - + @return list of tuples containing the client type, the client capabilities, the client file type associations and a reference to the creation function @@ -1498,12 +1639,16 @@ py3Exts.append(ext) else: py3Exts.append(".{0}".format(ext)) - + registryData = [] if py3Exts: registryData.append( - ("Python3", ClientDefaultCapabilities, py3Exts, - createDebuggerInterfacePython3) + ( + "Python3", + ClientDefaultCapabilities, + py3Exts, + createDebuggerInterfacePython3, + ) ) - + return registryData